Authors: Chris Morgan <cmorgan@wpi.edu>, James Abbatiello <abbeyj@wpi.edu>
[wine] / dlls / comctl32 / listview.c
1 /*
2  * Listview control
3  *
4  * Copyright 1998, 1999 Eric Kohl
5  * Copyright 1999 Luc Tourangeau
6  *
7  * NOTES
8  * Listview control implementation. 
9  *
10  * TODO:
11  *   1. No horizontal scrolling when header is larger than the client area.
12  *   2. Drawing optimizations.
13  *   3. Hot item handling.
14  *
15  * Notifications:
16  *   LISTVIEW_Notify : most notifications from children (editbox and header)
17  *
18  * Data structure:
19  *   LISTVIEW_SetItemCount : empty stub 
20  * 
21  * Unicode:
22  *   LISTVIEW_SetItemW : no unicode support
23  *   LISTVIEW_InsertItemW : no unicode support
24  *   LISTVIEW_InsertColumnW : no unicode support
25  *   LISTVIEW_GetColumnW : no unicode support
26  *   LISTVIEW_SetColumnW : no unicode support
27  *
28  * Advanced functionality:
29  *   LISTVIEW_GetNumberOfWorkAreas : not implemented
30  *   LISTVIEW_GetHotCursor : not implemented
31  *   LISTVIEW_GetHoverTime : not implemented
32  *   LISTVIEW_GetISearchString : not implemented 
33  *   LISTVIEW_GetBkImage : not implemented
34  *   LISTVIEW_GetColumnOrderArray : not implemented
35  *   LISTVIEW_SetColumnOrderArray : not implemented
36  *   LISTVIEW_Arrange : empty stub
37  *   LISTVIEW_ApproximateViewRect : incomplete
38  *   LISTVIEW_Scroll : not implemented 
39  *   LISTVIEW_RedrawItems : empty stub
40  *   LISTVIEW_Update : not completed
41  */
42
43 #include <string.h>
44 #include "winbase.h"
45 #include "heap.h"
46 #include "commctrl.h"
47 #include "listview.h"
48 #include "debugtools.h"
49
50 DEFAULT_DEBUG_CHANNEL(listview)
51
52 /*
53  * constants 
54  */
55
56 /* maximum size of a label */
57 #define DISP_TEXT_SIZE 128
58
59 /* padding for items in list and small icon display modes */
60 #define WIDTH_PADDING 12
61
62 /* padding for items in list, report and small icon display modes */
63 #define HEIGHT_PADDING 1
64
65 /* offset of items in report display mode */
66 #define REPORT_MARGINX 2
67
68 /* padding for icon in large icon display mode */
69 #define ICON_TOP_PADDING 2
70 #define ICON_BOTTOM_PADDING 2
71
72 /* padding for label in large icon display mode */
73 #define LABEL_VERT_OFFSET 2
74
75 /* default label width for items in list and small icon display modes */
76 #define DEFAULT_LABEL_WIDTH 40
77
78 /* default column width for items in list display mode */
79 #define DEFAULT_COLUMN_WIDTH 96 
80
81 /* Increment size of the horizontal scroll bar */
82 #define LISTVIEW_SCROLL_DIV_SIZE 10
83
84 /* 
85  * macros
86  */
87 #define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
88 #define ListView_LVNotify(hwnd,lCtrlId,plvnm) \
89     (BOOL)SendMessageA((hwnd),WM_NOTIFY,(WPARAM)(INT)lCtrlId,(LPARAM)(LPNMLISTVIEW)(plvnm))
90 #define ListView_Notify(hwnd,lCtrlId,pnmh) \
91     (BOOL)SendMessageA((hwnd),WM_NOTIFY,(WPARAM)(INT)lCtrlId,(LPARAM)(LPNMHDR)(pnmh))
92 /* retrieve the number of items in the listview */
93 #define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
94
95 HWND CreateEditLabel(LPCSTR text, DWORD style, INT x, INT y, 
96         INT width, INT height, HWND parent, HINSTANCE hinst, 
97         EditlblCallback EditLblCb, DWORD param);
98  
99 /* 
100  * forward declarations 
101  */
102 static INT LISTVIEW_HitTestItem(HWND, LPLVHITTESTINFO);
103 static INT LISTVIEW_GetCountPerRow(HWND);
104 static INT LISTVIEW_GetCountPerColumn(HWND);
105 static VOID LISTVIEW_AlignLeft(HWND);
106 static VOID LISTVIEW_AlignTop(HWND);
107 static VOID LISTVIEW_AddGroupSelection(HWND, INT);
108 static VOID LISTVIEW_AddSelection(HWND, INT);
109 static BOOL LISTVIEW_AddSubItem(HWND, LPLVITEMA);
110 static INT LISTVIEW_FindInsertPosition(HDPA, INT);
111 static INT LISTVIEW_GetItemHeight(HWND);
112 static BOOL LISTVIEW_GetItemPosition(HWND, INT, LPPOINT);
113 static LRESULT LISTVIEW_GetItemRect(HWND, INT, LPRECT);
114 static INT LISTVIEW_GetItemWidth(HWND);
115 static INT LISTVIEW_GetLabelWidth(HWND, INT);
116 static LRESULT LISTVIEW_GetOrigin(HWND, LPPOINT);
117 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem);
118 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA, INT);
119 static LRESULT LISTVIEW_GetViewRect(HWND, LPRECT);
120 static BOOL LISTVIEW_InitItem(HWND, LISTVIEW_ITEM *, LPLVITEMA);
121 static BOOL LISTVIEW_InitSubItem(HWND, LISTVIEW_SUBITEM *, LPLVITEMA);
122 static LRESULT LISTVIEW_MouseSelection(HWND, POINT);
123 static BOOL LISTVIEW_RemoveColumn(HDPA, INT);
124 static VOID LISTVIEW_RemoveSelections(HWND, INT, INT);
125 static BOOL LISTVIEW_RemoveSubItem(HDPA, INT);
126 static VOID LISTVIEW_SetGroupSelection(HWND, INT);
127 static BOOL LISTVIEW_SetItem(HWND, LPLVITEMA);
128 static BOOL LISTVIEW_SetItemFocus(HWND, INT);
129 static BOOL LISTVIEW_SetItemPosition(HWND, INT, INT, INT);
130 static VOID LISTVIEW_UpdateScroll(HWND);
131 static VOID LISTVIEW_SetSelection(HWND, INT);
132 static VOID LISTVIEW_UpdateSize(HWND);
133 static BOOL LISTVIEW_SetSubItem(HWND, LPLVITEMA);
134 static LRESULT LISTVIEW_SetViewRect(HWND, LPRECT);
135 static BOOL LISTVIEW_ToggleSelection(HWND, INT);
136 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle);
137 static HWND LISTVIEW_EditLabelA(HWND hwnd, INT nItem);
138 static BOOL LISTVIEW_EndEditLabel(HWND hwnd, LPSTR pszText, DWORD nItem);
139 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam);
140
141 /*************************************************************************
142  * LISTVIEW_UpdateHeaderSize [Internal] 
143  *
144  * Function to resize the header control
145  *
146  * PARAMS
147  *     hwnd             [I] handle to a window
148  *     nNewScrollPos    [I] Scroll Pos to Set
149  *
150  * RETURNS
151  *     nothing
152  *
153  * NOTES
154  */
155 static VOID LISTVIEW_UpdateHeaderSize(HWND hwnd, INT nNewScrollPos)
156 {
157     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
158     RECT winRect;
159     POINT point[2];
160
161     GetWindowRect(infoPtr->hwndHeader, &winRect);
162     point[0].x = winRect.left;
163     point[0].y = winRect.top;
164     point[1].x = winRect.right;
165     point[1].y = winRect.bottom;
166
167     MapWindowPoints(HWND_DESKTOP, hwnd, point, 2);
168     point[0].x = -(nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
169     point[1].x += (nNewScrollPos * LISTVIEW_SCROLL_DIV_SIZE);
170
171     SetWindowPos(infoPtr->hwndHeader,0,
172         point[0].x,point[0].y,point[1].x,point[1].y,
173         SWP_NOZORDER | SWP_NOACTIVATE);
174 }
175
176 /***
177  * DESCRIPTION:
178  * Update the scrollbars. This functions should be called whenever 
179  * the content, size or view changes.
180  * 
181  * PARAMETER(S):
182  * [I] HWND : window handle
183  *
184  * RETURN:
185  * None
186  */
187 static VOID LISTVIEW_UpdateScroll(HWND hwnd)
188 {
189   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); 
190   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
191   UINT uView =  lStyle & LVS_TYPEMASK;
192   INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
193   INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
194   SCROLLINFO scrollInfo;
195
196   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
197   scrollInfo.cbSize = sizeof(SCROLLINFO);
198
199   if (uView == LVS_LIST)
200   {
201     /* update horizontal scrollbar */
202
203     INT nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
204     INT nCountPerRow = LISTVIEW_GetCountPerRow(hwnd);
205     INT nCountPerPage = nCountPerRow * nCountPerColumn;
206     INT nNumOfItems = GETITEMCOUNT(infoPtr);
207
208     if (nCountPerPage < GETITEMCOUNT(infoPtr))
209     {
210       scrollInfo.nMax = nNumOfItems / nCountPerColumn;
211       if((nNumOfItems % nCountPerColumn) == 0)
212       {
213           scrollInfo.nMax--;
214       }
215       scrollInfo.nPos = ListView_GetTopIndex(hwnd) / nCountPerColumn;
216       scrollInfo.nPage = nCountPerRow;
217       scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
218       SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
219     }
220     else
221     {
222       /* hide scrollbar */
223       if (lStyle & WS_HSCROLL)
224       {
225         ShowScrollBar(hwnd, SB_HORZ, FALSE);
226       }
227     }
228   }
229   else if (uView == LVS_REPORT)
230   {
231     /* update vertical scrollbar */
232     scrollInfo.nMin = 0;
233     scrollInfo.nMax = GETITEMCOUNT(infoPtr) - 1;
234     scrollInfo.nPos = ListView_GetTopIndex(hwnd);
235     scrollInfo.nPage = LISTVIEW_GetCountPerColumn(hwnd);
236     scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
237     SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
238
239     /* update horizontal scrollbar */
240     if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE)
241     {
242       scrollInfo.nPos = 0;
243     } 
244     scrollInfo.nMin = 0;
245     scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE  ; 
246     scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
247     scrollInfo.nMax = max(infoPtr->nItemWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
248     SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE); 
249
250     /* Update the Header Control */
251     scrollInfo.fMask = SIF_POS;
252     GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
253     LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
254
255   }
256   else
257   {
258     RECT rcView;
259
260     if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
261     {
262       INT nViewWidth = rcView.right - rcView.left;
263       INT nViewHeight = rcView.bottom - rcView.top;
264
265       if (nViewWidth > nListWidth)
266       {
267         scrollInfo.fMask = SIF_POS;
268         if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE)
269         {
270           scrollInfo.nPos = 0;
271         }
272         scrollInfo.nMax = max(nViewWidth / LISTVIEW_SCROLL_DIV_SIZE, 0)-1;
273         scrollInfo.nMin = 0;
274         scrollInfo.nPage = nListWidth / LISTVIEW_SCROLL_DIV_SIZE;
275         scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
276         SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
277       }
278       else
279       {
280         if (lStyle & WS_HSCROLL)
281         {
282           ShowScrollBar(hwnd, SB_HORZ, FALSE);
283         }
284       }
285
286       if (nViewHeight > nListHeight)
287       {
288         scrollInfo.fMask = SIF_POS;
289         if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) == FALSE)
290         {
291           scrollInfo.nPos = 0;
292         }
293         scrollInfo.nMax = max(nViewHeight / LISTVIEW_SCROLL_DIV_SIZE,0)-1;
294         scrollInfo.nMin = 0;
295         scrollInfo.nPage = nListHeight / LISTVIEW_SCROLL_DIV_SIZE;
296         scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
297         SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
298       }
299       else
300       {
301         if (lStyle & WS_VSCROLL)
302         {
303           ShowScrollBar(hwnd, SB_VERT, FALSE);
304         }
305       }
306     }
307   }
308 }
309
310 /***
311  * DESCRIPTION:
312  * Prints a message for unsupported window styles.
313  * A kind of TODO list for window styles.
314  * 
315  * PARAMETER(S):
316  * [I] LONG : window style
317  *
318  * RETURN:
319  * None
320  */
321 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle)
322 {
323   if ((LVS_TYPEMASK & lStyle) == LVS_EDITLABELS)
324   {
325     FIXME("  LVS_EDITLABELS\n");
326   }
327
328   if ((LVS_TYPEMASK & lStyle) == LVS_NOLABELWRAP)
329   {
330     FIXME("  LVS_NOLABELWRAP\n");
331   }
332
333   if ((LVS_TYPEMASK & lStyle) == LVS_NOSCROLL)
334   {
335     FIXME("  LVS_NOSCROLL\n");
336   }
337
338   if ((LVS_TYPEMASK & lStyle) == LVS_NOSORTHEADER)
339   {
340     FIXME("  LVS_NOSORTHEADER\n");
341   }
342
343   if ((LVS_TYPEMASK & lStyle) == LVS_OWNERDRAWFIXED)
344   {
345     FIXME("  LVS_OWNERDRAWFIXED\n");
346   }
347
348   if ((LVS_TYPEMASK & lStyle) == LVS_SHAREIMAGELISTS)
349   {
350     FIXME("  LVS_SHAREIMAGELISTS\n");
351   }
352
353   if ((LVS_TYPEMASK & lStyle) == LVS_SORTASCENDING)
354   {
355     FIXME("  LVS_SORTASCENDING\n");
356   }
357
358   if ((LVS_TYPEMASK & lStyle) == LVS_SORTDESCENDING)
359   {
360     FIXME("  LVS_SORTDESCENDING\n");
361   }
362 }
363
364 /***
365  * DESCRIPTION:
366  * Aligns the items with the top edge of the window.
367  * 
368  * PARAMETER(S):
369  * [I] HWND : window handle
370  *
371  * RETURN:
372  * None
373  */
374 static VOID LISTVIEW_AlignTop(HWND hwnd)
375 {
376   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
377   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
378   INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
379   POINT ptItem;
380   RECT rcView;
381   INT i;
382   
383   if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
384   {
385     ZeroMemory(&ptItem, sizeof(POINT));
386     ZeroMemory(&rcView, sizeof(RECT));
387     
388     if (nListWidth > infoPtr->nItemWidth)
389     {
390       for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
391       {
392         if (ptItem.x + infoPtr->nItemWidth > nListWidth)
393         {
394           ptItem.x = 0;
395           ptItem.y += infoPtr->nItemHeight;
396         }
397         
398         ListView_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
399         ptItem.x += infoPtr->nItemWidth;
400         rcView.right = max(rcView.right, ptItem.x);
401       }
402
403       rcView.bottom = ptItem.y + infoPtr->nItemHeight;
404     }
405     else
406     {
407       for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
408       {
409         ListView_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
410         ptItem.y += infoPtr->nItemHeight;
411       }
412
413       rcView.right = infoPtr->nItemWidth;
414       rcView.bottom = ptItem.y;
415     }
416
417     LISTVIEW_SetViewRect(hwnd, &rcView);
418   }
419 }
420
421 /***
422  * DESCRIPTION:
423  * Aligns the items with the left edge of the window.
424  * 
425  * PARAMETER(S):
426  * [I] HWND : window handle
427  *
428  * RETURN:
429  * None
430  */
431 static VOID LISTVIEW_AlignLeft(HWND hwnd)
432 {
433   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
434   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
435   INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
436   POINT ptItem;
437   RECT rcView;
438   INT i;
439   
440   if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
441   {
442     ZeroMemory(&ptItem, sizeof(POINT));
443     ZeroMemory(&rcView, sizeof(RECT));
444
445     if (nListHeight > infoPtr->nItemHeight)
446     {
447       for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
448       {
449         if (ptItem.y + infoPtr->nItemHeight > nListHeight)
450         {
451           ptItem.y = 0;
452           ptItem.x += infoPtr->nItemWidth;
453         }
454
455         ListView_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
456         ptItem.y += infoPtr->nItemHeight;
457         rcView.bottom = max(rcView.bottom, ptItem.y);
458       }
459
460       rcView.right = ptItem.x + infoPtr->nItemWidth;
461     }
462     else
463     {
464       for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
465       {
466         ListView_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
467         ptItem.x += infoPtr->nItemWidth;
468       }
469
470       rcView.bottom = infoPtr->nItemHeight;
471       rcView.right = ptItem.x;
472     }
473
474     LISTVIEW_SetViewRect(hwnd, &rcView);
475   }
476 }
477
478 /***
479  * DESCRIPTION:
480  * Set the bounding rectangle of all the items.
481  * 
482  * PARAMETER(S):
483  * [I] HWND : window handle
484  * [I] LPRECT : bounding rectangle
485  *
486  * RETURN:
487  *   SUCCESS : TRUE
488  *   FAILURE : FALSE
489  */
490 static LRESULT LISTVIEW_SetViewRect(HWND hwnd, LPRECT lprcView)
491 {
492   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
493   BOOL bResult = FALSE;
494
495   TRACE("(hwnd=%x, left=%d, top=%d, right=%d, bottom=%d)\n", hwnd, 
496         lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
497   
498   if (lprcView != NULL)
499   {
500     bResult = TRUE;
501     infoPtr->rcView.left = lprcView->left;
502     infoPtr->rcView.top = lprcView->top;
503     infoPtr->rcView.right = lprcView->right;
504     infoPtr->rcView.bottom = lprcView->bottom;
505   }
506
507   return bResult;
508 }
509
510 /***
511  * DESCRIPTION:
512  * Retrieves the bounding rectangle of all the items.
513  * 
514  * PARAMETER(S):
515  * [I] HWND : window handle
516  * [O] LPRECT : bounding rectangle
517  *
518  * RETURN:
519  *   SUCCESS : TRUE
520  *   FAILURE : FALSE
521  */
522 static LRESULT LISTVIEW_GetViewRect(HWND hwnd, LPRECT lprcView)
523 {
524   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
525   BOOL bResult = FALSE;
526   POINT ptOrigin;
527
528   TRACE("(hwnd=%x, lprcView=%p)\n", hwnd, lprcView);
529
530   if (lprcView != NULL)
531   {
532     bResult = LISTVIEW_GetOrigin(hwnd, &ptOrigin);
533     if (bResult != FALSE)
534     {
535       lprcView->left = infoPtr->rcView.left + ptOrigin.x;
536       lprcView->top = infoPtr->rcView.top + ptOrigin.y;
537       lprcView->right = infoPtr->rcView.right + ptOrigin.x;
538       lprcView->bottom = infoPtr->rcView.bottom + ptOrigin.y;
539     }
540
541     TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n", 
542           lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
543   }
544
545   return bResult;
546 }
547
548 /***
549  * DESCRIPTION:
550  * Retrieves the subitem pointer associated with the subitem index.
551  * 
552  * PARAMETER(S):
553  * [I] HDPA : DPA handle for a specific item
554  * [I] INT : index of subitem
555  *
556  * RETURN:
557  *   SUCCESS : subitem pointer
558  *   FAILURE : NULL
559  */
560 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, 
561                                                 INT nSubItem)
562 {
563   LISTVIEW_SUBITEM *lpSubItem;
564   INT i;
565
566   for (i = 1; i < hdpaSubItems->nItemCount; i++)
567   {
568     lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
569     if (lpSubItem != NULL)
570     {
571       if (lpSubItem->iSubItem == nSubItem)
572       {
573         return lpSubItem;
574       }
575     }
576   }
577
578   return NULL;
579 }
580
581 /***
582  * DESCRIPTION:
583  * Calculates the width of an item.
584  * 
585  * PARAMETER(S):
586  * [I] HWND : window handle
587  * [I] LONG : window style
588  *
589  * RETURN:
590  * Returns item width.
591  */
592 static INT LISTVIEW_GetItemWidth(HWND hwnd)
593 {
594   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
595   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
596   INT nHeaderItemCount;
597   RECT rcHeaderItem;
598   INT nItemWidth = 0;
599   INT nLabelWidth;
600   INT i;
601
602   TRACE("(hwnd=%x)\n", hwnd);
603
604   if (uView == LVS_ICON)
605   {
606     nItemWidth = infoPtr->iconSpacing.cx;
607   }
608   else if (uView == LVS_REPORT)
609   {
610     /* calculate width of header */
611     nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
612     for (i = 0; i < nHeaderItemCount; i++)
613     {
614       if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
615       {
616         nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
617       }
618     }
619   }
620   else
621   {
622     for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
623     { 
624       nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, i);
625       nItemWidth = max(nItemWidth, nLabelWidth);
626     }
627     
628     /* default label size */
629     if (GETITEMCOUNT(infoPtr) == 0)
630     {
631       nItemWidth = DEFAULT_COLUMN_WIDTH;
632     }
633     else
634     {
635       if (nItemWidth == 0)
636       {
637         nItemWidth = DEFAULT_LABEL_WIDTH;
638       }
639       else
640       {
641         /* add padding */
642         nItemWidth += WIDTH_PADDING;
643       
644         if (infoPtr->himlSmall != NULL)
645         {
646           nItemWidth += infoPtr->iconSize.cx;
647         }
648
649         if (infoPtr->himlState != NULL)
650         {
651           nItemWidth += infoPtr->iconSize.cx;
652         }
653       }
654     }
655   }
656   
657   return nItemWidth;
658 }
659
660 /***
661  * DESCRIPTION:
662  * Calculates the width of a specific item.
663  * 
664  * PARAMETER(S):
665  * [I] HWND : window handle
666  * [I] LPSTR : string  
667  *
668  * RETURN:
669  * Returns the width of an item width a specified string.
670  */
671 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem)
672 {
673   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
674   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
675   INT nHeaderItemCount;
676   RECT rcHeaderItem;
677   INT nItemWidth = 0;
678   INT i;
679
680   TRACE("(hwnd=%x)\n", hwnd);
681
682   if (uView == LVS_ICON)
683   {
684     nItemWidth = infoPtr->iconSpacing.cx;
685   }
686   else if (uView == LVS_REPORT)
687   {
688     /* calculate width of header */
689     nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
690     for (i = 0; i < nHeaderItemCount; i++)
691     {
692       if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
693       {
694         nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
695       }
696     }
697   }
698   else
699   {
700     /* get width of string */
701     nItemWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
702
703     /* default label size */
704     if (GETITEMCOUNT(infoPtr) == 0)
705     {
706       nItemWidth = DEFAULT_COLUMN_WIDTH;
707     }
708     else
709     {
710       if (nItemWidth == 0)
711       {
712         nItemWidth = DEFAULT_LABEL_WIDTH;
713       }
714       else
715       {
716         /* add padding */
717         nItemWidth += WIDTH_PADDING;
718       
719         if (infoPtr->himlSmall != NULL)
720         {
721           nItemWidth += infoPtr->iconSize.cx;
722         }
723
724         if (infoPtr->himlState != NULL)
725         {
726           nItemWidth += infoPtr->iconSize.cx;
727         }
728       }
729     }
730   }
731   
732   return nItemWidth;
733 }
734
735 /***
736  * DESCRIPTION:
737  * Calculates the height of an item.
738  * 
739  * PARAMETER(S):
740  * [I] HWND : window handle
741  * [I] LONG : window style
742  *
743  * RETURN:
744  * Returns item height.
745  */
746 static INT LISTVIEW_GetItemHeight(HWND hwnd)
747 {
748   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
749   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
750   INT nItemHeight = 0;
751
752   if (uView == LVS_ICON)
753   {
754     nItemHeight = infoPtr->iconSpacing.cy;
755   }
756   else 
757   {
758     TEXTMETRICA tm; 
759     HDC hdc = GetDC(hwnd);
760     HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
761     GetTextMetricsA(hdc, &tm);
762     nItemHeight = max(tm.tmHeight, infoPtr->iconSize.cy) + HEIGHT_PADDING;
763     SelectObject(hdc, hOldFont);
764     ReleaseDC(hwnd, hdc);
765   }
766
767   return nItemHeight;
768 }
769
770 /***
771  * DESCRIPTION:
772  * Adds a block of selections.
773  * 
774  * PARAMETER(S):
775  * [I] HWND : window handle
776  * [I] INT : item index 
777  *
778  * RETURN:
779  * None
780  */
781 static VOID LISTVIEW_AddGroupSelection(HWND hwnd, INT nItem)
782 {
783   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
784   INT nFirst = min(infoPtr->nSelectionMark, nItem);
785   INT nLast = max(infoPtr->nSelectionMark, nItem);
786   LVITEMA lvItem;
787   INT i;
788
789   lvItem.state = LVIS_SELECTED;
790   lvItem.stateMask= LVIS_SELECTED;
791   
792   for (i = nFirst; i <= nLast; i++)
793   {
794     ListView_SetItemState(hwnd, i, &lvItem);
795   }
796   
797   LISTVIEW_SetItemFocus(hwnd, nItem);
798   infoPtr->nSelectionMark = nItem;
799 }
800
801 /***
802  * DESCRIPTION:
803  * Adds a single selection.
804  * 
805  * PARAMETER(S):
806  * [I] HWND : window handle
807  * [I] INT : item index 
808  *
809  * RETURN:
810  * None
811  */
812 static VOID LISTVIEW_AddSelection(HWND hwnd, INT nItem)
813 {
814   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
815   LVITEMA lvItem;
816
817   lvItem.state = LVIS_SELECTED;
818   lvItem.stateMask= LVIS_SELECTED;
819
820   ListView_SetItemState(hwnd, nItem, &lvItem);
821
822   LISTVIEW_SetItemFocus(hwnd, nItem);
823   infoPtr->nSelectionMark = nItem;
824 }
825
826 /***
827  * DESCRIPTION:
828  * Selects or unselects an item.
829  * 
830  * PARAMETER(S):
831  * [I] HWND : window handle
832  * [I] INT : item index 
833  *
834  * RETURN:
835  *   SELECT: TRUE 
836  *   UNSELECT : FALSE
837  */
838 static BOOL LISTVIEW_ToggleSelection(HWND hwnd, INT nItem)
839 {
840   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
841   BOOL bResult;
842   LVITEMA lvItem;
843
844   lvItem.stateMask= LVIS_SELECTED;
845
846   if (ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED)
847   {
848     lvItem.state = 0;
849     ListView_SetItemState(hwnd, nItem, &lvItem);
850     bResult = FALSE;
851   }
852   else
853   {
854     lvItem.state = LVIS_SELECTED;
855     ListView_SetItemState(hwnd, nItem, &lvItem);
856     bResult = TRUE;
857   }
858
859   LISTVIEW_SetItemFocus(hwnd, nItem);
860   infoPtr->nSelectionMark = nItem;
861
862   return bResult;
863 }
864
865 /***
866  * DESCRIPTION:
867  * Selects items based on view coorddiantes. 
868  * 
869  * PARAMETER(S):
870  * [I] HWND : window handle
871  * [I] RECT : selection rectangle  
872  *
873  * RETURN:
874  * None
875  */
876 static VOID LISTVIEW_SetSelectionRect(HWND hwnd, RECT rcSelRect)
877 {
878   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
879   LVITEMA lvItem;
880   POINT ptItem;
881   INT i;
882
883   lvItem.stateMask = LVIS_SELECTED;
884
885   for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
886   {
887     LISTVIEW_GetItemPosition(hwnd, i, &ptItem);
888     if (PtInRect(&rcSelRect, ptItem) != FALSE)
889     {
890       lvItem.state = LVIS_SELECTED;
891     }
892     else
893     {
894       lvItem.state = 0;
895     }
896
897     ListView_SetItemState(hwnd, i, &lvItem);
898   }
899 }
900
901 /***
902  * DESCRIPTION:
903  * Sets a single group selection.
904  * 
905  * PARAMETER(S):
906  * [I] HWND : window handle
907  * [I] INT : item index 
908  *
909  * RETURN:
910  * None 
911  */
912 static VOID LISTVIEW_SetGroupSelection(HWND hwnd, INT nItem)
913 {
914   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
915   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
916   LVITEMA lvItem;
917
918   if ((uView == LVS_LIST) || (uView == LVS_REPORT))
919   {
920     INT i;
921     INT nFirst = min(infoPtr->nSelectionMark, nItem);
922     INT nLast = max(infoPtr->nSelectionMark, nItem);
923     lvItem.stateMask = LVIS_SELECTED;
924    
925     for (i = 0; i <= GETITEMCOUNT(infoPtr); i++)
926     {
927       if ((i < nFirst) || (i > nLast))
928       {
929         lvItem.state = 0;
930       }
931       else
932       {
933         lvItem.state = LVIS_SELECTED;
934       }
935
936       ListView_SetItemState(hwnd, i, &lvItem);
937     }
938   }
939   else
940   {
941     POINT ptItem;
942     POINT ptSelMark;
943     RECT rcSel;
944     LISTVIEW_GetItemPosition(hwnd, nItem, &ptItem);
945     LISTVIEW_GetItemPosition(hwnd, infoPtr->nSelectionMark, &ptSelMark);
946     rcSel.left = min(ptSelMark.x, ptItem.x);
947     rcSel.top = min(ptSelMark.y, ptItem.y);
948     rcSel.right = max(ptSelMark.x, ptItem.x) + infoPtr->nItemWidth;
949     rcSel.bottom = max(ptSelMark.y, ptItem.y) + infoPtr->nItemHeight;
950     LISTVIEW_SetSelectionRect(hwnd, rcSel);
951   }
952
953   LISTVIEW_SetItemFocus(hwnd, nItem);
954 }
955
956 /***
957  * DESCRIPTION:
958  * Manages the item focus.
959  * 
960  * PARAMETER(S):
961  * [I] HWND : window handle
962  * [I] INT : item index 
963  *
964  * RETURN:
965  *   TRUE : focused item changed
966  *   FALSE : focused item has NOT changed
967  */
968 static BOOL LISTVIEW_SetItemFocus(HWND hwnd, INT nItem)
969 {
970   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
971   BOOL bResult = FALSE;
972   LVITEMA lvItem;
973
974   if (infoPtr->nFocusedItem != nItem)
975   {
976     bResult = TRUE;
977     ZeroMemory(&lvItem, sizeof(LVITEMA));
978     lvItem.stateMask = LVIS_FOCUSED;
979     ListView_SetItemState(hwnd, infoPtr->nFocusedItem, &lvItem); 
980
981     lvItem.state =  LVIS_FOCUSED;
982     lvItem.stateMask = LVIS_FOCUSED;
983     ListView_SetItemState(hwnd, nItem, &lvItem);
984
985     infoPtr->nFocusedItem = nItem;
986     ListView_EnsureVisible(hwnd, nItem, FALSE);
987   }
988   
989   return bResult; 
990 }
991
992 /***
993  * DESCRIPTION:
994  * Sets a single selection.
995  * 
996  * PARAMETER(S):
997  * [I] HWND : window handle
998  * [I] INT : item index 
999  *
1000  * RETURN:
1001  * None
1002  */
1003 static VOID LISTVIEW_SetSelection(HWND hwnd, INT nItem)
1004 {
1005   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1006   LVITEMA lvItem;
1007
1008   if (nItem > 0)
1009   {
1010     LISTVIEW_RemoveSelections(hwnd, 0, nItem - 1);
1011   }
1012
1013   if (nItem < GETITEMCOUNT(infoPtr))
1014   {
1015     LISTVIEW_RemoveSelections(hwnd, nItem + 1, GETITEMCOUNT(infoPtr));
1016   }
1017
1018   ZeroMemory(&lvItem, sizeof(LVITEMA));
1019   lvItem.stateMask = LVIS_FOCUSED;
1020   ListView_SetItemState(hwnd, infoPtr->nFocusedItem, &lvItem); 
1021   
1022   lvItem.state =  LVIS_SELECTED | LVIS_FOCUSED;
1023   lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
1024   ListView_SetItemState(hwnd, nItem, &lvItem);
1025
1026   infoPtr->nFocusedItem = nItem;
1027   infoPtr->nSelectionMark = nItem;
1028 }
1029
1030 /***
1031  * DESCRIPTION:
1032  * Set selection(s) with keyboard.
1033  * 
1034  * PARAMETER(S):
1035  * [I] HWND : window handle
1036  * [I] INT : item index 
1037  *
1038  * RETURN:
1039  *   SUCCESS : TRUE (needs to be repainted)
1040  *   FAILURE : FALSE (nothing has changed)
1041  */
1042 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem)
1043 {
1044   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1045   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1046   WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
1047   WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
1048   BOOL bResult = FALSE;
1049
1050   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
1051   {
1052     if (lStyle & LVS_SINGLESEL)
1053     {
1054       bResult = TRUE;
1055       LISTVIEW_SetSelection(hwnd, nItem); 
1056       ListView_EnsureVisible(hwnd, nItem, FALSE);
1057     }
1058     else
1059     {
1060       if (wShift)
1061       {
1062         bResult = TRUE;
1063         LISTVIEW_SetGroupSelection(hwnd, nItem); 
1064       }
1065       else if (wCtrl)
1066       {
1067         bResult = LISTVIEW_SetItemFocus(hwnd, nItem);
1068       }
1069       else
1070       {
1071         bResult = TRUE;
1072         LISTVIEW_SetSelection(hwnd, nItem); 
1073         ListView_EnsureVisible(hwnd, nItem, FALSE);
1074       }
1075     }
1076   }
1077
1078   return bResult;
1079 }
1080   
1081 /***
1082  * DESCRIPTION:
1083  * Selects an item based on coordinates.
1084  * 
1085  * PARAMETER(S):
1086  * [I] HWND : window handle
1087  * [I] POINT : mouse click ccordinates
1088  *
1089  * RETURN:
1090  *   SUCCESS : item index
1091  *   FAILURE : -1
1092  */
1093 static LRESULT LISTVIEW_MouseSelection(HWND hwnd, POINT pt)
1094 {
1095   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1096   RECT rcItem;
1097   INT i;
1098   
1099   for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1100   {
1101     rcItem.left = LVIR_SELECTBOUNDS;
1102     if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) == TRUE)
1103     {
1104       if (PtInRect(&rcItem, pt) != FALSE)
1105       {
1106         return i;
1107       }
1108     }
1109   }
1110
1111   return -1;
1112 }
1113
1114 /***
1115  * DESCRIPTION:
1116  * Removes all selection states.
1117  * 
1118  * PARAMETER(S):
1119  * [I] HWND : window handle
1120  * [I] INT : item index 
1121  *
1122  * RETURN:
1123  *   SUCCCESS : TRUE
1124  *   FAILURE : FALSE
1125  */
1126 static VOID LISTVIEW_RemoveSelections(HWND hwnd, INT nFirst, INT nLast)
1127 {
1128   LVITEMA lvItem;
1129   INT i;
1130
1131   lvItem.state = 0;
1132   lvItem.stateMask = LVIS_SELECTED;
1133
1134   for (i = nFirst; i <= nLast; i++)
1135   {
1136     ListView_SetItemState(hwnd, i, &lvItem);
1137   }
1138 }
1139
1140 /***
1141  * DESCRIPTION:
1142  * Removes a column.
1143  * 
1144  * PARAMETER(S):
1145  * [IO] HDPA : dynamic pointer array handle
1146  * [I] INT : column index (subitem index)
1147  *
1148  * RETURN:
1149  *   SUCCCESS : TRUE
1150  *   FAILURE : FALSE
1151  */
1152 static BOOL LISTVIEW_RemoveColumn(HDPA hdpaItems, INT nSubItem)
1153 {
1154   BOOL bResult = TRUE;
1155   HDPA hdpaSubItems;
1156   INT i;
1157
1158   for (i = 0; i < hdpaItems->nItemCount; i++)
1159   {
1160     hdpaSubItems = (HDPA)DPA_GetPtr(hdpaItems, i);
1161     if (hdpaSubItems != NULL)
1162     {
1163       if (LISTVIEW_RemoveSubItem(hdpaSubItems, nSubItem) == FALSE)
1164       {
1165         bResult = FALSE;
1166       }
1167     }
1168   }
1169     
1170   return bResult;
1171 }
1172
1173 /***
1174  * DESCRIPTION:
1175  * Removes a subitem at a given position.
1176  * 
1177  * PARAMETER(S):
1178  * [IO] HDPA : dynamic pointer array handle
1179  * [I] INT : subitem index
1180  *
1181  * RETURN:
1182  *   SUCCCESS : TRUE
1183  *   FAILURE : FALSE
1184  */
1185 static BOOL LISTVIEW_RemoveSubItem(HDPA hdpaSubItems, INT nSubItem)
1186 {
1187   LISTVIEW_SUBITEM *lpSubItem;
1188   INT i;
1189
1190   for (i = 1; i < hdpaSubItems->nItemCount; i++)
1191   {
1192     lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
1193     if (lpSubItem != NULL)
1194     {
1195       if (lpSubItem->iSubItem == nSubItem)
1196       {
1197         /* free string */
1198         if ((lpSubItem->pszText != NULL) && 
1199             (lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
1200         {
1201           COMCTL32_Free(lpSubItem->pszText);
1202         }
1203         
1204         /* free item */
1205         COMCTL32_Free(lpSubItem);
1206
1207         /* free dpa memory */
1208         if (DPA_DeletePtr(hdpaSubItems, i) == NULL)
1209         {
1210           return FALSE;
1211         }
1212       }
1213       else if (lpSubItem->iSubItem > nSubItem)
1214       {
1215         return TRUE;
1216       }
1217     }
1218   }    
1219   
1220   return TRUE;
1221 }
1222
1223 /***
1224  * DESCRIPTION:
1225  * Compares the item information.
1226  * 
1227  * PARAMETER(S):
1228  * [I] LISTVIEW_ITEM *: destination item 
1229  * [I] LPLVITEM : source item 
1230  *
1231  * RETURN:
1232  *   SUCCCESS : TRUE (EQUAL)
1233  *   FAILURE : FALSE (NOT EQUAL)
1234  */
1235 static UINT LISTVIEW_GetItemChanges(LISTVIEW_ITEM *lpItem, LPLVITEMA lpLVItem)
1236 {
1237   UINT uChanged = 0;
1238
1239   if ((lpItem != NULL) && (lpLVItem != NULL))
1240   {
1241     if (lpLVItem->mask & LVIF_STATE)
1242     {
1243       if ((lpItem->state & lpLVItem->stateMask) != 
1244           (lpLVItem->state & lpLVItem->stateMask))
1245       {
1246         uChanged |= LVIF_STATE; 
1247       }
1248     }
1249
1250     if (lpLVItem->mask & LVIF_IMAGE)
1251     {
1252       if (lpItem->iImage != lpLVItem->iImage)
1253       {
1254         uChanged |= LVIF_IMAGE; 
1255       }
1256     }
1257   
1258     if (lpLVItem->mask & LVIF_PARAM)
1259     {
1260       if (lpItem->lParam != lpLVItem->lParam)
1261       {
1262         uChanged |= LVIF_PARAM; 
1263       }
1264     }
1265     
1266     if (lpLVItem->mask & LVIF_INDENT)
1267     {
1268       if (lpItem->iIndent != lpLVItem->iIndent)
1269       {
1270         uChanged |= LVIF_INDENT; 
1271       }
1272     }
1273
1274     if (lpLVItem->mask & LVIF_TEXT) 
1275     {
1276       if (lpLVItem->pszText == LPSTR_TEXTCALLBACKA) 
1277       {
1278         if (lpItem->pszText != LPSTR_TEXTCALLBACKA)
1279         {
1280           uChanged |= LVIF_TEXT; 
1281         }
1282       }
1283       else
1284       {
1285         if (lpItem->pszText == LPSTR_TEXTCALLBACKA)
1286         {
1287           uChanged |= LVIF_TEXT; 
1288         }
1289         else
1290         {
1291           if (lpLVItem->pszText)
1292           {
1293             if (lpItem->pszText)
1294             {
1295               if (strcmp(lpLVItem->pszText, lpItem->pszText) != 0)
1296               {
1297                 uChanged |= LVIF_TEXT;
1298               }
1299             }
1300             else
1301             {
1302               uChanged |= LVIF_TEXT;
1303             }
1304           }
1305           else
1306           {
1307             if (lpItem->pszText)
1308             {
1309               uChanged |= LVIF_TEXT;
1310             }
1311           }
1312         }
1313       }
1314     }
1315   }
1316   return uChanged;
1317 }
1318
1319 /***
1320  * DESCRIPTION:
1321  * Initializes item attributes.
1322  * 
1323  * PARAMETER(S):
1324  * [I] HWND : window handle
1325  * [O] LISTVIEW_ITEM *: destination item 
1326  * [I] LPLVITEM : source item 
1327  *
1328  * RETURN:
1329  *   SUCCCESS : TRUE
1330  *   FAILURE : FALSE
1331  */
1332 static BOOL LISTVIEW_InitItem(HWND hwnd, LISTVIEW_ITEM *lpItem, 
1333                               LPLVITEMA lpLVItem)
1334 {
1335   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1336   BOOL bResult = FALSE;
1337
1338   if ((lpItem != NULL) && (lpLVItem != NULL))
1339   {
1340     bResult = TRUE;
1341     
1342     if (lpLVItem->mask & LVIF_STATE)
1343     {
1344       lpItem->state &= ~lpLVItem->stateMask;
1345       lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
1346     }
1347     
1348     if (lpLVItem->mask & LVIF_IMAGE)
1349     {
1350       lpItem->iImage = lpLVItem->iImage;
1351     }
1352   
1353     if (lpLVItem->mask & LVIF_PARAM)
1354     {
1355       lpItem->lParam = lpLVItem->lParam;
1356     }
1357     
1358     if (lpLVItem->mask & LVIF_INDENT)
1359     {
1360       lpItem->iIndent = lpLVItem->iIndent;
1361     }
1362
1363     if (lpLVItem->mask & LVIF_TEXT) 
1364     {
1365       if (lpLVItem->pszText == LPSTR_TEXTCALLBACKA) 
1366       {
1367         if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
1368         {
1369           return FALSE;
1370         }
1371
1372         if ((lpItem->pszText != NULL) && 
1373             (lpItem->pszText != LPSTR_TEXTCALLBACKA))
1374         {
1375           COMCTL32_Free(lpItem->pszText);
1376         }
1377     
1378         lpItem->pszText = LPSTR_TEXTCALLBACKA;
1379       }
1380       else 
1381       {
1382         if (lpItem->pszText == LPSTR_TEXTCALLBACKA)
1383         {
1384           lpItem->pszText = NULL;
1385         }
1386         
1387         bResult = Str_SetPtrA(&lpItem->pszText, lpLVItem->pszText);
1388       }
1389     }
1390   }
1391
1392   return bResult;
1393 }
1394
1395 /***
1396  * DESCRIPTION:
1397  * Initializes subitem attributes.
1398  *
1399  * NOTE: The documentation specifies that the operation fails if the user
1400  * tries to set the indent of a subitem.
1401  *
1402  * PARAMETER(S):
1403  * [I] HWND : window handle
1404  * [O] LISTVIEW_SUBITEM *: destination subitem
1405  * [I] LPLVITEM : source subitem
1406  *
1407  * RETURN:
1408  *   SUCCCESS : TRUE
1409  *   FAILURE : FALSE
1410  */
1411 static BOOL LISTVIEW_InitSubItem(HWND hwnd, LISTVIEW_SUBITEM *lpSubItem, 
1412                                    LPLVITEMA lpLVItem)
1413 {
1414   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1415   BOOL bResult = FALSE;
1416   
1417   if ((lpSubItem != NULL) && (lpLVItem != NULL))
1418   {
1419     if (!(lpLVItem->mask & LVIF_INDENT))
1420     {
1421       bResult = TRUE;
1422       ZeroMemory(lpSubItem, sizeof(LISTVIEW_SUBITEM));
1423
1424       lpSubItem->iSubItem = lpLVItem->iSubItem;
1425
1426       if (lpLVItem->mask & LVIF_IMAGE)
1427       {
1428         lpSubItem->iImage = lpLVItem->iImage;
1429       }
1430       
1431       if (lpLVItem->mask & LVIF_TEXT) 
1432       {
1433         if (lpLVItem->pszText == LPSTR_TEXTCALLBACKA) 
1434         {
1435           if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
1436           {
1437             return FALSE;
1438           } 
1439
1440           if ((lpSubItem->pszText != NULL) && 
1441               (lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
1442           {
1443             COMCTL32_Free(lpSubItem->pszText);
1444           }
1445     
1446           lpSubItem->pszText = LPSTR_TEXTCALLBACKA;
1447         }
1448         else 
1449         {
1450           if (lpSubItem->pszText == LPSTR_TEXTCALLBACKA)
1451           {
1452             lpSubItem->pszText = NULL;
1453           }
1454         
1455           bResult = Str_SetPtrA(&lpSubItem->pszText, lpLVItem->pszText);
1456         }
1457       }
1458     }
1459   }
1460
1461   return bResult;
1462 }
1463
1464 /***
1465  * DESCRIPTION:
1466  * Adds a subitem at a given position (column index).
1467  * 
1468  * PARAMETER(S):
1469  * [I] HWND : window handle
1470  * [I] LPLVITEM : new subitem atttributes 
1471  *
1472  * RETURN:
1473  *   SUCCESS : TRUE
1474  *   FAILURE : FALSE
1475  */
1476 static BOOL LISTVIEW_AddSubItem(HWND hwnd, LPLVITEMA lpLVItem)
1477 {
1478   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1479   LISTVIEW_SUBITEM *lpSubItem = NULL;
1480   BOOL bResult = FALSE;
1481   HDPA hdpaSubItems;
1482   INT nPosition, nItem;
1483
1484   if (lpLVItem != NULL)
1485   {
1486     hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
1487     if (hdpaSubItems != NULL)
1488     {
1489       lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
1490       if (lpSubItem != NULL)
1491       {
1492         if (LISTVIEW_InitSubItem(hwnd, lpSubItem, lpLVItem) != FALSE)
1493         {
1494           nPosition = LISTVIEW_FindInsertPosition(hdpaSubItems, 
1495                                                   lpSubItem->iSubItem);
1496           nItem = DPA_InsertPtr(hdpaSubItems, nPosition, lpSubItem);
1497           if (nItem != -1)
1498           {
1499             bResult = TRUE;
1500           }            
1501         }
1502       }
1503     }
1504   }
1505   
1506   /* cleanup if unsuccessful */   
1507   if ((bResult == FALSE) && (lpSubItem != NULL))
1508   {
1509     COMCTL32_Free(lpSubItem);
1510   }
1511   
1512   return bResult;
1513 }
1514
1515 /***
1516  * DESCRIPTION:
1517  * Finds the dpa insert position (array index).
1518  * 
1519  * PARAMETER(S):
1520  * [I] HWND : window handle
1521  * [I] INT : subitem index
1522  *
1523  * RETURN:
1524  *   SUCCESS : TRUE
1525  *   FAILURE : FALSE
1526  */
1527 static INT LISTVIEW_FindInsertPosition(HDPA hdpaSubItems, INT nSubItem)
1528 {
1529   LISTVIEW_SUBITEM *lpSubItem;
1530   INT i;
1531
1532   for (i = 1; i < hdpaSubItems->nItemCount; i++)
1533   {
1534     lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
1535     if (lpSubItem != NULL)
1536     {
1537       if (lpSubItem->iSubItem > nSubItem)
1538       {
1539         return i;
1540       }
1541     } 
1542   }
1543
1544   return hdpaSubItems->nItemCount;
1545 }
1546
1547 /***
1548  * DESCRIPTION:
1549  * Retrieves a listview subitem at a given position (column index).
1550  * 
1551  * PARAMETER(S):
1552  * [I] HWND : window handle
1553  * [I] INT : subitem index
1554  *
1555  * RETURN:
1556  *   SUCCESS : TRUE
1557  *   FAILURE : FALSE
1558  */
1559 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA hdpaSubItems, INT nSubItem)
1560 {
1561   LISTVIEW_SUBITEM *lpSubItem;
1562   INT i;
1563
1564   for (i = 1; i < hdpaSubItems->nItemCount; i++)
1565   {
1566     lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
1567     if (lpSubItem != NULL)
1568     {
1569       if (lpSubItem->iSubItem == nSubItem)
1570       {
1571         return lpSubItem;
1572       }
1573       else if (lpSubItem->iSubItem > nSubItem)
1574       {
1575         return NULL;
1576       }  
1577     }
1578   }
1579   
1580   return NULL;
1581 }
1582
1583 /***
1584  * DESCRIPTION:
1585  * Sets item attributes.
1586  * 
1587  * PARAMETER(S):
1588  * [I] HWND : window handle
1589  * [I] LPLVITEM : new item atttributes 
1590  *
1591  * RETURN:
1592  *   SUCCESS : TRUE
1593  *   FAILURE : FALSE
1594  */
1595 static BOOL LISTVIEW_SetItem(HWND hwnd, LPLVITEMA lpLVItem)
1596 {
1597   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1598   BOOL bResult = FALSE;
1599   HDPA hdpaSubItems;
1600   LISTVIEW_ITEM *lpItem;
1601   NMLISTVIEW nmlv;
1602   UINT uChanged;
1603   LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
1604
1605   if (lpLVItem != NULL)
1606   {
1607     if (lpLVItem->iSubItem == 0)
1608     {
1609       hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
1610       if (hdpaSubItems != NULL)
1611       {
1612         lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, lpLVItem->iSubItem);
1613         if (lpItem != NULL)
1614         {
1615           ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
1616           nmlv.hdr.hwndFrom = hwnd;
1617           nmlv.hdr.idFrom = lCtrlId;
1618           nmlv.hdr.code = LVN_ITEMCHANGING;
1619           nmlv.lParam = lpItem->lParam;
1620           uChanged = LISTVIEW_GetItemChanges(lpItem, lpLVItem);
1621           if (uChanged != 0)
1622           {
1623             if (uChanged & LVIF_STATE)
1624             {
1625               nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
1626               nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
1627             }
1628             
1629             nmlv.uChanged = uChanged;
1630             nmlv.iItem = lpLVItem->iItem;
1631             nmlv.lParam = lpItem->lParam;
1632             /* send LVN_ITEMCHANGING notification */
1633             ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
1634
1635             /* copy information */
1636             bResult = LISTVIEW_InitItem(hwnd, lpItem, lpLVItem);
1637
1638             /* send LVN_ITEMCHANGED notification */
1639             nmlv.hdr.code = LVN_ITEMCHANGED;
1640             ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
1641           }
1642           else
1643           {
1644             bResult = TRUE;
1645           }
1646
1647           InvalidateRect(hwnd, NULL, FALSE);
1648         }
1649       }
1650     }
1651   }
1652
1653   return bResult;
1654 }
1655
1656 /***
1657  * DESCRIPTION:
1658  * Sets subitem attributes.
1659  * 
1660  * PARAMETER(S):
1661  * [I] HWND : window handle
1662  * [I] LPLVITEM : new subitem atttributes 
1663  *
1664  * RETURN:
1665  *   SUCCESS : TRUE
1666  *   FAILURE : FALSE
1667  */
1668 static BOOL LISTVIEW_SetSubItem(HWND hwnd, LPLVITEMA lpLVItem)
1669 {
1670   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
1671   BOOL bResult = FALSE;
1672   HDPA hdpaSubItems;
1673   LISTVIEW_SUBITEM *lpSubItem;
1674
1675   if (lpLVItem != NULL)
1676   {
1677     if (lpLVItem->iSubItem > 0)
1678     {
1679       hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
1680       if (hdpaSubItems != NULL)
1681       {
1682         /* set subitem only if column is present */
1683         if (Header_GetItemCount(infoPtr->hwndHeader) > lpLVItem->iSubItem)
1684         {
1685           lpSubItem = LISTVIEW_GetSubItem(hdpaSubItems, lpLVItem->iSubItem);
1686           if (lpSubItem != NULL)
1687           {
1688             bResult = LISTVIEW_InitSubItem(hwnd, lpSubItem, lpLVItem);
1689           }
1690           else
1691           {
1692             bResult = LISTVIEW_AddSubItem(hwnd, lpLVItem);
1693           }
1694           
1695           InvalidateRect(hwnd, NULL, FALSE);
1696         } 
1697       }
1698     }
1699   }
1700
1701   return bResult;
1702 }
1703
1704 /***
1705  * DESCRIPTION:
1706  * Retrieves the index of the item at coordinate (0, 0) of the client area.
1707  * 
1708  * PARAMETER(S):
1709  * [I] HWND : window handle
1710  *
1711  * RETURN:
1712  * item index
1713  */
1714 static INT LISTVIEW_GetTopIndex(HWND hwnd)
1715 {
1716   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
1717   UINT uView = lStyle & LVS_TYPEMASK;
1718   INT nItem = 0;
1719   SCROLLINFO scrollInfo;
1720
1721   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
1722   scrollInfo.cbSize = sizeof(SCROLLINFO);
1723   scrollInfo.fMask = SIF_POS;
1724   
1725   if (uView == LVS_LIST)
1726   {
1727     if (lStyle & WS_HSCROLL)
1728     {
1729       if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
1730       {
1731         nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(hwnd);
1732       }
1733     }
1734   }
1735   else if (uView == LVS_REPORT)
1736   {
1737     if (lStyle & WS_VSCROLL)
1738     {
1739       if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
1740       {
1741         nItem = scrollInfo.nPos;
1742       }
1743     }
1744   }
1745   
1746   return nItem;
1747 }
1748
1749 /***
1750  * DESCRIPTION:
1751  * Draws a subitem.
1752  * 
1753  * PARAMETER(S):
1754  * [I] HWND : window handle
1755  * [I] HDC : device context handle
1756  * [I] INT : item index
1757  * [I] INT : subitem index
1758  * [I] RECT * : clipping rectangle
1759  *
1760  * RETURN:
1761  * None
1762  */
1763 static VOID LISTVIEW_DrawSubItem(HWND hwnd, HDC hdc, INT nItem, INT nSubItem, 
1764                                  RECT rcItem)
1765 {
1766   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); 
1767   CHAR szDispText[DISP_TEXT_SIZE];
1768   LVITEMA lvItem;
1769
1770   TRACE("(hwnd=%x, hdc=%x, nItem=%d, nSubItem=%d\n", hwnd, hdc, 
1771         nItem, nSubItem);
1772
1773   /* get information needed for drawing the item */
1774   ZeroMemory(&lvItem, sizeof(LVITEMA));
1775   lvItem.mask = LVIF_TEXT;
1776   lvItem.iItem = nItem;
1777   lvItem.iSubItem = nSubItem;
1778   lvItem.cchTextMax = DISP_TEXT_SIZE;
1779   lvItem.pszText = szDispText;
1780   ListView_GetItemA(hwnd, &lvItem);
1781
1782   /* set item colors */
1783   SetBkColor(hdc, infoPtr->clrTextBk);
1784   SetTextColor(hdc, infoPtr->clrText);
1785   
1786   ExtTextOutA(hdc, rcItem.left, rcItem.top, ETO_OPAQUE | ETO_CLIPPED, 
1787               &rcItem, lvItem.pszText, lstrlenA(lvItem.pszText), NULL);
1788 }
1789
1790
1791 /***
1792  * DESCRIPTION:
1793  * Draws an item.
1794  * 
1795  * PARAMETER(S):
1796  * [I] HWND : window handle
1797  * [I] HDC : device context handle
1798  * [I] INT : item index
1799  * [I] RECT * : clipping rectangle
1800  *
1801  * RETURN:
1802  * None
1803  */
1804 static VOID LISTVIEW_DrawItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem)
1805 {
1806   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); 
1807   CHAR szDispText[DISP_TEXT_SIZE];
1808   INT nLabelWidth;
1809   LVITEMA lvItem;
1810   INT nMixMode;
1811   DWORD dwBkColor;
1812   DWORD dwTextColor;
1813
1814   TRACE("(hwnd=%x, hdc=%x, nItem=%d\n", hwnd, hdc, nItem);
1815
1816   /* get information needed for drawing the item */
1817   ZeroMemory(&lvItem, sizeof(LVITEMA));
1818   lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
1819   lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
1820   lvItem.iItem = nItem;
1821   lvItem.iSubItem = 0;
1822   lvItem.cchTextMax = DISP_TEXT_SIZE;
1823   lvItem.pszText = szDispText;
1824   ListView_GetItemA(hwnd, &lvItem);
1825
1826   /* state icons */
1827   if (infoPtr->himlState != NULL)
1828   {
1829      UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12; 
1830      if (uStateImage != 0)
1831      {
1832        ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcItem.left, 
1833                       rcItem.top, ILD_NORMAL);
1834      }
1835  
1836      rcItem.left += infoPtr->iconSize.cx; 
1837   }
1838   
1839   /* small icons */
1840   if (infoPtr->himlSmall != NULL)
1841   {
1842     if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE))
1843     {
1844       ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
1845       ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left, 
1846                      rcItem.top, ILD_SELECTED);
1847     }
1848     else
1849     {
1850       ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
1851       ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left, 
1852                      rcItem.top, ILD_NORMAL);
1853     }
1854     
1855     rcItem.left += infoPtr->iconSize.cx; 
1856   }
1857
1858   /* Don't bother painting item being edited */
1859   if (infoPtr->hwndEdit && lvItem.state & LVIS_FOCUSED)
1860       return;
1861
1862   if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE))
1863   {
1864     /* set item colors */ 
1865     dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1866     dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1867     /* set raster mode */
1868     nMixMode = SetROP2(hdc, R2_XORPEN);
1869   }
1870   else if ((GetWindowLongA(hwnd, GWL_STYLE) & LVS_SHOWSELALWAYS) && 
1871            (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus == FALSE))
1872   {
1873     dwBkColor = SetBkColor(hdc, GetSysColor(COLOR_3DFACE));
1874     dwTextColor = SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
1875     /* set raster mode */
1876     nMixMode = SetROP2(hdc, R2_COPYPEN);
1877   }
1878   else
1879   {
1880     /* set item colors */
1881     dwBkColor = SetBkColor(hdc, infoPtr->clrTextBk);
1882     dwTextColor = SetTextColor(hdc, infoPtr->clrText);
1883     /* set raster mode */
1884     nMixMode = SetROP2(hdc, R2_COPYPEN);
1885   }
1886   
1887   nLabelWidth = ListView_GetStringWidthA(hwnd, lvItem.pszText);
1888   if (rcItem.left + nLabelWidth < rcItem.right)
1889   {
1890     rcItem.right = rcItem.left + nLabelWidth;
1891   }
1892   
1893   /* draw label */  
1894   ExtTextOutA(hdc, rcItem.left, rcItem.top, ETO_OPAQUE | ETO_CLIPPED, 
1895               &rcItem, lvItem.pszText, lstrlenA(lvItem.pszText), NULL);
1896   
1897   if ((lvItem.state & LVIS_FOCUSED) && (infoPtr->bFocus == TRUE))
1898   {
1899     Rectangle(hdc, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom); 
1900   }
1901
1902   if (nMixMode != 0)
1903   {
1904     SetROP2(hdc, R2_COPYPEN);
1905     SetBkColor(hdc, infoPtr->clrTextBk);
1906     SetTextColor(hdc, infoPtr->clrText);
1907   }
1908 }
1909
1910 /***
1911  * DESCRIPTION:
1912  * Draws an item when in large icon display mode.
1913  * 
1914  * PARAMETER(S):
1915  * [I] HWND : window handle
1916  * [I] HDC : device context handle
1917  * [I] LISTVIEW_ITEM * : item
1918  * [I] INT : item index
1919  * [I] RECT * : clipping rectangle
1920  *
1921  * RETURN:
1922  * None
1923  */
1924 static VOID LISTVIEW_DrawLargeItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem)
1925 {
1926   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); 
1927   CHAR szDispText[DISP_TEXT_SIZE];
1928   INT nDrawPosX = rcItem.left;
1929   INT nLabelWidth;
1930   TEXTMETRICA tm;
1931   LVITEMA lvItem;
1932
1933   TRACE("(hwnd=%x, hdc=%x, nItem=%d, left=%d, top=%d, right=%d, \
1934 bottom=%d)\n", hwnd, hdc, nItem, rcItem.left, rcItem.top, rcItem.right, 
1935         rcItem.bottom);
1936
1937   /* get information needed for drawing the item */
1938   ZeroMemory(&lvItem, sizeof(LVITEMA));
1939   lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
1940   lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
1941   lvItem.iItem = nItem;
1942   lvItem.iSubItem = 0;
1943   lvItem.cchTextMax = DISP_TEXT_SIZE;
1944   lvItem.pszText = szDispText;
1945   ListView_GetItemA(hwnd, &lvItem);
1946
1947   if (lvItem.state & LVIS_SELECTED)
1948   {
1949     /* set item colors */ 
1950     SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
1951     SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
1952     /* set raster mode */
1953     SetROP2(hdc, R2_XORPEN);
1954   }
1955   else
1956   {
1957     /* set item colors */
1958     SetBkColor(hdc, infoPtr->clrTextBk);
1959     SetTextColor(hdc, infoPtr->clrText);
1960     /* set raster mode */
1961     SetROP2(hdc, R2_COPYPEN);
1962   }
1963
1964   if (infoPtr->himlNormal != NULL)
1965   {
1966     rcItem.top += ICON_TOP_PADDING;
1967     nDrawPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1968     if (lvItem.state & LVIS_SELECTED)
1969     {
1970       ImageList_Draw(infoPtr->himlNormal, lvItem.iImage, hdc, nDrawPosX, 
1971                      rcItem.top, ILD_SELECTED);
1972     }
1973     else
1974     {
1975       ImageList_Draw(infoPtr->himlNormal, lvItem.iImage, hdc, nDrawPosX, 
1976                      rcItem.top, ILD_NORMAL);
1977     }
1978   }
1979
1980   rcItem.top += infoPtr->iconSize.cy + ICON_BOTTOM_PADDING; 
1981   nLabelWidth = ListView_GetStringWidthA(hwnd, lvItem.pszText);
1982   nDrawPosX = infoPtr->iconSpacing.cx - nLabelWidth;
1983   if (nDrawPosX > 1)
1984   {
1985     rcItem.left += nDrawPosX / 2;
1986     rcItem.right = rcItem.left + nLabelWidth;
1987   }
1988   else
1989   {
1990     rcItem.left += 1;
1991     rcItem.right = rcItem.left + infoPtr->iconSpacing.cx - 1;
1992   }
1993
1994   /* draw label */  
1995   GetTextMetricsA(hdc, &tm);
1996   rcItem.bottom = rcItem.top + tm.tmHeight + HEIGHT_PADDING; 
1997   ExtTextOutA(hdc, rcItem.left, rcItem.top, ETO_OPAQUE | ETO_CLIPPED, 
1998               &rcItem, lvItem.pszText, lstrlenA(lvItem.pszText), NULL);
1999         
2000   if (lvItem.state & LVIS_FOCUSED)
2001   {
2002     Rectangle(hdc, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom); 
2003   }
2004 }
2005
2006 /***
2007  * DESCRIPTION:
2008  * Draws listview items when in report display mode.
2009  * 
2010  * PARAMETER(S):
2011  * [I] HWND : window handle
2012  * [I] HDC : device context handle 
2013  *
2014  * RETURN:
2015  * None
2016  */
2017 static VOID LISTVIEW_RefreshReport(HWND hwnd, HDC hdc)
2018 {
2019   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd,0);
2020   SCROLLINFO scrollInfo;
2021   INT nDrawPosY = infoPtr->rcList.top;
2022   INT nColumnCount;
2023   RECT rcItem;
2024   INT  j;
2025   INT nItem;
2026   INT nLast;
2027
2028   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
2029   scrollInfo.cbSize = sizeof(SCROLLINFO);
2030   scrollInfo.fMask = SIF_POS;
2031
2032   nItem = ListView_GetTopIndex(hwnd);
2033
2034   /* add 1 for displaying a partial item at the bottom */
2035   nLast = nItem + LISTVIEW_GetCountPerColumn(hwnd) + 1;
2036   nLast = min(nLast, GETITEMCOUNT(infoPtr));
2037   for (; nItem < nLast; nItem++)
2038   {
2039     nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
2040     for (j = 0; j < nColumnCount; j++)
2041     {
2042       Header_GetItemRect(infoPtr->hwndHeader, j, &rcItem);
2043       rcItem.left += REPORT_MARGINX;
2044       rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
2045       rcItem.top = nDrawPosY;
2046       rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
2047
2048       /* Offset the Scroll Bar Pos */
2049       if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE) 
2050       {
2051         rcItem.left -= (scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE);
2052         rcItem.right -= (scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE);
2053       }
2054
2055       if (j == 0)
2056       {
2057         LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem); 
2058       }
2059       else 
2060       {
2061         LISTVIEW_DrawSubItem(hwnd, hdc, nItem, j, rcItem);
2062       }
2063     }
2064     
2065     nDrawPosY += infoPtr->nItemHeight;
2066   }
2067 }
2068
2069 /***
2070  * DESCRIPTION:
2071  * Retrieves the number of items that can fit vertically in the client area.
2072  * 
2073  * PARAMETER(S):
2074  * [I] HWND : window handle
2075  *
2076  * RETURN:
2077  * Number of items per row.
2078  */
2079 static INT LISTVIEW_GetCountPerRow(HWND hwnd)
2080 {
2081   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd,0);
2082   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
2083   INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2084   INT nCountPerRow = 1;
2085
2086   if (nListWidth > 0)
2087   {
2088     if (uView == LVS_REPORT)
2089     {
2090       nCountPerRow = 1;
2091     }
2092     else
2093     {
2094       nCountPerRow =  nListWidth / infoPtr->nItemWidth;
2095       if (nCountPerRow == 0)
2096       {
2097         nCountPerRow = 1;
2098       }
2099     }
2100   }
2101
2102   return nCountPerRow;
2103 }
2104
2105 /***
2106  * DESCRIPTION:
2107  * Retrieves the number of items that can fit horizontally in the client 
2108  * area.
2109  * 
2110  * PARAMETER(S):
2111  * [I] HWND : window handle
2112  *
2113  * RETURN:
2114  * Number of items per column.
2115  */
2116 static INT LISTVIEW_GetCountPerColumn(HWND hwnd)
2117 {
2118   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd,0);
2119   INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2120   INT nCountPerColumn = 1;
2121
2122   if (nListHeight > 0)
2123   {
2124     nCountPerColumn =  nListHeight / infoPtr->nItemHeight;
2125     if (nCountPerColumn == 0)
2126     {
2127       nCountPerColumn = 1;
2128     }
2129   }
2130
2131   return nCountPerColumn;
2132 }
2133
2134 /***
2135  * DESCRIPTION:
2136  * Retrieves the number of columns needed to display all the items when in 
2137  * list display mode.
2138  * 
2139  * PARAMETER(S):
2140  * [I] HWND : window handle
2141  *
2142  * RETURN:
2143  * Number of columns.
2144  */
2145 static INT LISTVIEW_GetColumnCount(HWND hwnd)
2146 {
2147   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2148   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2149   INT nColumnCount = 0;
2150
2151   if ((lStyle & LVS_TYPEMASK) == LVS_LIST)
2152   {
2153     if (infoPtr->rcList.right % infoPtr->nItemWidth == 0)
2154     {
2155       nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth;
2156     }
2157     else
2158     {
2159       nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth + 1;
2160     }
2161   }
2162
2163   return nColumnCount;
2164 }  
2165   
2166
2167 /***
2168  * DESCRIPTION:
2169  * Draws listview items when in list display mode.
2170  * 
2171  * PARAMETER(S):
2172  * [I] HWND : window handle
2173  * [I] HDC : device context handle 
2174  *
2175  * RETURN:
2176  * None
2177  */
2178 static VOID LISTVIEW_RefreshList(HWND hwnd, HDC hdc)
2179 {
2180   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2181   RECT rcItem;
2182   INT i, j;
2183   INT nItem;
2184   INT nColumnCount;
2185   INT nCountPerColumn;
2186   INT nItemWidth = LISTVIEW_GetItemWidth(hwnd);
2187   INT nItemHeight = LISTVIEW_GetItemHeight(hwnd);
2188   
2189   /* get number of fully visible columns */
2190   nColumnCount = LISTVIEW_GetColumnCount(hwnd);
2191   nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
2192   nItem = ListView_GetTopIndex(hwnd);
2193
2194   for (i = 0; i < nColumnCount; i++)
2195   {
2196     for (j = 0; j < nCountPerColumn; j++, nItem++)
2197     {
2198       if (nItem >= GETITEMCOUNT(infoPtr))
2199         return;
2200
2201       rcItem.top = j * nItemHeight;
2202       rcItem.left = i * nItemWidth;
2203       rcItem.bottom = rcItem.top + nItemHeight;
2204       rcItem.right = rcItem.left + nItemWidth;
2205       LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem);
2206     }
2207   }
2208 }
2209
2210 /***
2211  * DESCRIPTION:
2212  * Draws listview items when in icon or small icon display mode.
2213  * 
2214  * PARAMETER(S):
2215  * [I] HWND : window handle
2216  * [I] HDC : device context handle 
2217  *
2218  * RETURN:
2219  * None
2220  */
2221 static VOID LISTVIEW_RefreshIcon(HWND hwnd, HDC hdc, BOOL bSmall)
2222 {
2223   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2224   POINT ptPosition;
2225   POINT ptOrigin;
2226   RECT rcItem;
2227   INT i;
2228
2229   LISTVIEW_GetOrigin(hwnd, &ptOrigin);
2230   for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
2231   {
2232     LISTVIEW_GetItemPosition(hwnd, i, &ptPosition);
2233     ptPosition.x += ptOrigin.x;
2234     ptPosition.y += ptOrigin.y;
2235       
2236     if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
2237     {
2238       if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
2239       {
2240         if (ptPosition.y < infoPtr->rcList.bottom)
2241         {
2242           if (ptPosition.x < infoPtr->rcList.right)
2243           {
2244             rcItem.top = ptPosition.y;
2245             rcItem.left = ptPosition.x;
2246             rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
2247             rcItem.right = rcItem.left + infoPtr->nItemWidth;
2248             if (bSmall == FALSE)
2249             {
2250               LISTVIEW_DrawLargeItem(hwnd, hdc, i, rcItem);
2251             }
2252             else
2253             {
2254               LISTVIEW_DrawItem(hwnd, hdc, i, rcItem);
2255             }
2256           }
2257         }
2258       }
2259     }
2260   }
2261 }
2262
2263 /***
2264  * DESCRIPTION:
2265  * Draws listview items.
2266  * 
2267  * PARAMETER(S):
2268  * [I] HWND : window handle
2269  * [I] HDC : device context handle 
2270  *
2271  * RETURN:
2272  * NoneX
2273  */
2274 static VOID LISTVIEW_Refresh(HWND hwnd, HDC hdc)
2275 {
2276   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2277   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
2278   HFONT hOldFont;
2279   HPEN hPen, hOldPen;
2280
2281   /* select font */
2282   hOldFont = SelectObject(hdc, infoPtr->hFont);
2283
2284   /* select the doted pen (for drawing the focus box) */
2285   hPen = CreatePen(PS_DOT, 1, 0);
2286   hOldPen = SelectObject(hdc, hPen);
2287
2288   /* select transparent brush (for drawing the focus box) */
2289   SelectObject(hdc, GetStockObject(NULL_BRUSH)); 
2290
2291   if (uView == LVS_LIST)
2292   {
2293     LISTVIEW_RefreshList(hwnd, hdc); 
2294   }
2295   else if (uView == LVS_REPORT)
2296   {
2297     LISTVIEW_RefreshReport(hwnd, hdc);
2298   }
2299   else if (uView == LVS_SMALLICON)
2300   {
2301     LISTVIEW_RefreshIcon(hwnd, hdc, TRUE);
2302   }
2303   else if (uView == LVS_ICON)
2304   {
2305     LISTVIEW_RefreshIcon(hwnd, hdc, FALSE);
2306   }
2307
2308   /* unselect objects */
2309   SelectObject(hdc, hOldFont);
2310   SelectObject(hdc, hOldPen);
2311   
2312   /* delete pen */
2313   DeleteObject(hPen);
2314 }
2315
2316
2317 /***
2318  * DESCRIPTION:
2319  * Calculates the approximate width and height of a given number of items.
2320  * 
2321  * PARAMETER(S):
2322  * [I] HWND : window handle
2323  * [I] INT : number of items
2324  * [I] INT : width
2325  * [I] INT : height
2326  *
2327  * RETURN:
2328  * Returns a DWORD. The width in the low word and the height in high word.
2329  */
2330 static LRESULT LISTVIEW_ApproximateViewRect(HWND hwnd, INT nItemCount, 
2331                                             WORD wWidth, WORD wHeight)
2332 {
2333   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2334   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
2335   INT nItemCountPerColumn = 1;
2336   INT nColumnCount = 0;
2337   DWORD dwViewRect = 0;
2338
2339   if (nItemCount == -1)
2340   {
2341     nItemCount = GETITEMCOUNT(infoPtr);
2342   }
2343
2344   if (uView == LVS_LIST)
2345   {
2346     if (wHeight == 0xFFFF)
2347     {
2348       /* use current height */
2349       wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2350     }
2351
2352     if (wHeight < infoPtr->nItemHeight)
2353     {
2354       wHeight = infoPtr->nItemHeight;
2355     }
2356
2357     if (nItemCount > 0)
2358     {
2359       if (infoPtr->nItemHeight > 0)
2360       {
2361         nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
2362         if (nItemCountPerColumn == 0)
2363         {
2364           nItemCountPerColumn = 1;
2365         }
2366         
2367         if (nItemCount % nItemCountPerColumn != 0)
2368         {
2369           nColumnCount = nItemCount / nItemCountPerColumn;
2370         }
2371         else
2372         {
2373           nColumnCount = nItemCount / nItemCountPerColumn + 1;
2374         }
2375       }
2376     }
2377
2378     /* Microsoft padding magic */
2379     wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
2380     wWidth = nColumnCount * infoPtr->nItemWidth + 2;
2381
2382     dwViewRect = MAKELONG(wWidth, wHeight);
2383   }
2384   else if (uView == LVS_REPORT)
2385   {
2386     /* TO DO */
2387   }
2388   else if (uView == LVS_SMALLICON)
2389   {
2390     /* TO DO */
2391   }
2392   else if (uView == LVS_ICON)
2393   {
2394     /* TO DO */
2395   }
2396  
2397   return dwViewRect;
2398 }
2399
2400 /***
2401  * DESCRIPTION:
2402  * Arranges listview items in icon display mode.
2403  * 
2404  * PARAMETER(S):
2405  * [I] HWND : window handle
2406  * [I] INT : alignment code
2407  *
2408  * RETURN:
2409  *   SUCCESS : TRUE
2410  *   FAILURE : FALSE
2411  */
2412 static LRESULT LISTVIEW_Arrange(HWND hwnd, INT nAlignCode)
2413 {
2414   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK; 
2415   BOOL bResult = FALSE;
2416
2417   if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
2418   {
2419     switch (nAlignCode)
2420     {
2421     case LVA_ALIGNLEFT:
2422       /* TO DO */
2423       break;
2424     case LVA_ALIGNTOP:
2425       /* TO DO */
2426       break;
2427     case LVA_DEFAULT:
2428       /* TO DO */
2429       break;
2430     case LVA_SNAPTOGRID:
2431       /* TO DO */
2432       break;
2433     }
2434   }
2435
2436   return bResult;
2437 }
2438
2439 /* << LISTVIEW_CreateDragImage >> */
2440
2441 /***
2442  * DESCRIPTION:
2443  * Removes all listview items and subitems.
2444  * 
2445  * PARAMETER(S):
2446  * [I] HWND : window handle
2447  *
2448  * RETURN:
2449  *   SUCCESS : TRUE
2450  *   FAILURE : FALSE
2451  */
2452 static LRESULT LISTVIEW_DeleteAllItems(HWND hwnd)
2453 {
2454   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2455   LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
2456   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2457   UINT uView = lStyle & LVS_TYPEMASK;
2458   LISTVIEW_ITEM *lpItem;
2459   LISTVIEW_SUBITEM *lpSubItem;
2460   NMLISTVIEW nmlv;
2461   BOOL bSuppress;
2462   BOOL bResult = FALSE;
2463   INT i;
2464   INT j;
2465   HDPA hdpaSubItems;
2466
2467   TRACE("(hwnd=%x,)\n", hwnd);
2468
2469   if (GETITEMCOUNT(infoPtr) > 0)
2470   {
2471     /* initialize memory */
2472     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2473     
2474     /* send LVN_DELETEALLITEMS notification */
2475     nmlv.hdr.hwndFrom = hwnd;
2476     nmlv.hdr.idFrom = lCtrlId;
2477     nmlv.hdr.code = LVN_DELETEALLITEMS;
2478     nmlv.iItem = -1;
2479
2480     /* verify if subsequent LVN_DELETEITEM notifications should be 
2481        suppressed */
2482     bSuppress = ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
2483
2484     for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
2485     {
2486       hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
2487       if (hdpaSubItems != NULL)
2488       {
2489         for (j = 1; j < hdpaSubItems->nItemCount; j++)
2490         {
2491           lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
2492           if (lpSubItem != NULL)
2493           {
2494             /* free subitem string */
2495             if ((lpSubItem->pszText != NULL) && 
2496                 (lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
2497             {
2498               COMCTL32_Free(lpSubItem->pszText);
2499             }
2500             
2501             /* free subitem */
2502             COMCTL32_Free(lpSubItem);
2503           }    
2504         }
2505     
2506         lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
2507         if (lpItem != NULL)
2508         {
2509           if (bSuppress == FALSE)
2510           {
2511             /* send LVN_DELETEITEM notification */
2512             nmlv.hdr.code = LVN_DELETEITEM;
2513             nmlv.iItem = i;
2514             nmlv.lParam = lpItem->lParam;
2515             ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
2516           }
2517
2518           /* free item string */
2519           if ((lpItem->pszText != NULL) && 
2520               (lpItem->pszText != LPSTR_TEXTCALLBACKA))
2521           {
2522             COMCTL32_Free(lpItem->pszText);
2523           }
2524           
2525           /* free item */
2526           COMCTL32_Free(lpItem);
2527         }
2528         
2529         DPA_Destroy(hdpaSubItems);
2530       }
2531     }
2532
2533     /* reinitialize listview memory */
2534     bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
2535     
2536     /* align items (set position of each item) */
2537     if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
2538     {
2539       if (lStyle & LVS_ALIGNLEFT)
2540       {
2541         LISTVIEW_AlignLeft(hwnd);
2542       }
2543       else
2544       {
2545         LISTVIEW_AlignTop(hwnd);
2546       }
2547     }
2548     
2549     LISTVIEW_UpdateScroll(hwnd);
2550
2551     /* invalidate client area (optimization needed) */
2552     InvalidateRect(hwnd, NULL, TRUE);
2553   }
2554   
2555   return bResult;
2556 }
2557
2558 /***
2559  * DESCRIPTION:
2560  * Removes a column from the listview control.
2561  * 
2562  * PARAMETER(S):
2563  * [I] HWND : window handle
2564  * [I] INT : column index
2565  *
2566  * RETURN:
2567  *   SUCCESS : TRUE
2568  *   FAILURE : FALSE
2569  */
2570 static LRESULT LISTVIEW_DeleteColumn(HWND hwnd, INT nColumn)
2571 {
2572   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2573   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
2574   BOOL bResult = FALSE;
2575   
2576   if (Header_DeleteItem(infoPtr->hwndHeader, nColumn) != FALSE)
2577   {
2578     bResult = LISTVIEW_RemoveColumn(infoPtr->hdpaItems, nColumn);
2579
2580     /* Need to reset the item width when deleting a column */
2581     infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
2582
2583     /* reset scroll parameters */
2584     if (uView == LVS_REPORT)
2585     {
2586       /* update scrollbar(s) */
2587       LISTVIEW_UpdateScroll(hwnd);
2588
2589       /* refresh client area */
2590       InvalidateRect(hwnd, NULL, FALSE);
2591     }
2592   }
2593
2594   return bResult;
2595 }
2596
2597 /***
2598  * DESCRIPTION:
2599  * Removes an item from the listview control.
2600  * 
2601  * PARAMETER(S):
2602  * [I] HWND : window handle
2603  * [I] INT : item index  
2604  *
2605  * RETURN:
2606  *   SUCCESS : TRUE
2607  *   FAILURE : FALSE
2608  */
2609 static LRESULT LISTVIEW_DeleteItem(HWND hwnd, INT nItem)
2610 {
2611   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2612   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2613   UINT uView = lStyle & LVS_TYPEMASK;
2614   LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
2615   NMLISTVIEW nmlv;
2616   BOOL bResult = FALSE;
2617   HDPA hdpaSubItems;
2618   LISTVIEW_ITEM *lpItem;
2619   LISTVIEW_SUBITEM *lpSubItem;
2620   INT i;
2621
2622   TRACE("(hwnd=%x,nItem=%d)\n", hwnd, nItem);
2623
2624   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
2625   {
2626     /* initialize memory */
2627     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2628
2629     hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
2630     if (hdpaSubItems != NULL)
2631     {
2632       for (i = 1; i < hdpaSubItems->nItemCount; i++)
2633       {
2634         lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2635         if (lpSubItem != NULL)
2636         {
2637           /* free item string */
2638           if ((lpSubItem->pszText != NULL) && 
2639               (lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
2640           {
2641             COMCTL32_Free(lpSubItem->pszText);
2642           }
2643           
2644           /* free item */
2645           COMCTL32_Free(lpSubItem);
2646         }    
2647       }
2648     
2649       lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
2650       if (lpItem != NULL)
2651       {
2652         /* send LVN_DELETEITEM notification */
2653         nmlv.hdr.hwndFrom = hwnd;
2654         nmlv.hdr.idFrom = lCtrlId;
2655         nmlv.hdr.code = LVN_DELETEITEM;
2656         nmlv.iItem = nItem;
2657         nmlv.lParam = lpItem->lParam;
2658         SendMessageA(GetParent(hwnd), WM_NOTIFY, (WPARAM)lCtrlId, 
2659                      (LPARAM)&nmlv);
2660
2661         /* free item string */
2662         if ((lpItem->pszText != NULL) && 
2663             (lpItem->pszText != LPSTR_TEXTCALLBACKA))
2664         {
2665           COMCTL32_Free(lpItem->pszText);
2666         }
2667         
2668         /* free item */
2669         COMCTL32_Free(lpItem);
2670       }
2671       
2672       bResult = DPA_Destroy(hdpaSubItems);
2673     }
2674
2675     /* align items (set position of each item) */
2676     if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
2677     {
2678       if (lStyle & LVS_ALIGNLEFT)
2679       {
2680         LISTVIEW_AlignLeft(hwnd);
2681       }
2682       else
2683       {
2684         LISTVIEW_AlignTop(hwnd);
2685       }
2686     }
2687
2688     /* If this item had focus change focus to next or previous item */
2689     if (GETITEMCOUNT(infoPtr) > 0)
2690     {
2691        int sItem = nItem < GETITEMCOUNT(infoPtr) ? nItem : nItem - 1;
2692        if (infoPtr->nFocusedItem == nItem)
2693            LISTVIEW_SetItemFocus(hwnd, sItem);
2694     }
2695     else
2696           infoPtr->nFocusedItem = -1;
2697
2698     LISTVIEW_UpdateScroll(hwnd);
2699
2700     /* refresh client area */
2701     InvalidateRect(hwnd, NULL, TRUE);
2702   }
2703   
2704   return bResult;
2705 }
2706
2707
2708 /***
2709  * DESCRIPTION:
2710  * Return edit control handle of current edit label
2711  *
2712  * PARAMETER(S):
2713  * [I] HWND : window handle
2714  *
2715  * RETURN:
2716  *   SUCCESS : HWND
2717  *   FAILURE : 0
2718  */
2719 static LRESULT LISTVIEW_GetEditControl(hwnd)
2720 {
2721   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2722   return infoPtr->hwndEdit;
2723 }
2724
2725
2726 /***
2727  * DESCRIPTION:
2728  * Callback implementation for editlabel control
2729  *
2730  * PARAMETER(S):
2731  * [I] HWND : window handle
2732  * [I] LPSTR : modified text
2733  * [I] DWORD : item index
2734  *
2735  * RETURN:
2736  *   SUCCESS : TRUE
2737  *   FAILURE : FALSE
2738  */
2739
2740 static BOOL LISTVIEW_EndEditLabel(HWND hwnd, LPSTR pszText, DWORD nItem)
2741 {
2742   NMLVDISPINFOA dispInfo;
2743   LISTVIEW_ITEM *lpItem;
2744   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
2745   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2746   HDPA hdpaSubItems;
2747   
2748   ZeroMemory(&dispInfo, sizeof(NMLVDISPINFOA));
2749
2750   if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
2751           return FALSE;
2752
2753   if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
2754           return FALSE;
2755
2756   dispInfo.hdr.hwndFrom = hwnd;
2757   dispInfo.hdr.idFrom = nCtrlId;
2758   dispInfo.hdr.code = LVN_ENDLABELEDITA;
2759   dispInfo.item.mask = 0;
2760   dispInfo.item.iItem = nItem;
2761   dispInfo.item.state = lpItem->state;
2762   dispInfo.item.stateMask = 0;
2763   dispInfo.item.pszText = pszText;
2764   dispInfo.item.cchTextMax = pszText ? strlen(pszText) : 0;
2765   dispInfo.item.iImage = lpItem->iImage;
2766   dispInfo.item.lParam = lpItem->lParam;
2767
2768   ListView_Notify(GetParent(hwnd), nCtrlId, &dispInfo);
2769   infoPtr->hwndEdit = 0;
2770
2771   return TRUE;
2772 }
2773
2774 /***
2775  * DESCRIPTION:
2776  * Begin in place editing of specified list view item
2777  *
2778  * PARAMETER(S):
2779  * [I] HWND : window handle
2780  * [I] INT : item index
2781  *
2782  * RETURN:
2783  *   SUCCESS : TRUE
2784  *   FAILURE : FALSE
2785  */
2786
2787 static HWND LISTVIEW_EditLabelA(HWND hwnd, INT nItem)
2788 {
2789   NMLVDISPINFOA dispInfo;
2790   RECT rect;
2791   LISTVIEW_ITEM *lpItem;
2792   HWND hedit; 
2793   HINSTANCE hinst = GetWindowLongA(hwnd, GWL_HINSTANCE);
2794   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
2795   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2796   HDPA hdpaSubItems;
2797  
2798   if (~GetWindowLongA(hwnd, GWL_STYLE) & LVS_EDITLABELS)
2799       return FALSE;
2800
2801   LISTVIEW_SetSelection(hwnd, nItem);
2802   LISTVIEW_SetItemFocus(hwnd, nItem);
2803
2804   ZeroMemory(&dispInfo, sizeof(NMLVDISPINFOA));
2805   if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
2806           return 0;
2807
2808   if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
2809           return 0;
2810
2811   dispInfo.hdr.hwndFrom = hwnd;
2812   dispInfo.hdr.idFrom = nCtrlId;
2813   dispInfo.hdr.code = LVN_BEGINLABELEDITA;
2814   dispInfo.item.mask = 0;
2815   dispInfo.item.iItem = nItem;
2816   dispInfo.item.state = lpItem->state;
2817   dispInfo.item.stateMask = 0;
2818   dispInfo.item.pszText = lpItem->pszText;
2819   dispInfo.item.cchTextMax = strlen(lpItem->pszText);
2820   dispInfo.item.iImage = lpItem->iImage;
2821   dispInfo.item.lParam = lpItem->lParam;
2822
2823   if (ListView_LVNotify(GetParent(hwnd), nCtrlId, &dispInfo))
2824           return 0;
2825
2826   rect.left = LVIR_LABEL;
2827   if (!LISTVIEW_GetItemRect(hwnd, nItem, &rect))
2828           return 0;
2829   
2830  if (!(hedit = CreateEditLabel(dispInfo.item.pszText , WS_VISIBLE, 
2831                  rect.left, rect.top, rect.right - rect.left + 15, 
2832                  rect.bottom - rect.top, 
2833                  hwnd, hinst, LISTVIEW_EndEditLabel, nItem)))
2834          return 0;
2835
2836   infoPtr->hwndEdit = hedit;
2837   SetFocus(hedit); 
2838   SendMessageA(hedit, EM_SETSEL, 0, -1);
2839
2840   return hedit;
2841 }
2842
2843
2844 /***
2845  * DESCRIPTION:
2846  * Ensures the specified item is visible, scrolling into view if necessary.
2847  * 
2848  * PARAMETER(S):
2849  * [I] HWND : window handle
2850  * [I] INT : item index
2851  * [I] BOOL : partially or entirely visible
2852  *
2853  * RETURN:
2854  *   SUCCESS : TRUE
2855  *   FAILURE : FALSE
2856  */
2857 static BOOL LISTVIEW_EnsureVisible(HWND hwnd, INT nItem, BOOL bPartial)
2858 {
2859   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2860   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
2861   INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2862   INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2863   INT nScrollPosHeight = 0;
2864   INT nScrollPosWidth = 0;
2865   SCROLLINFO scrollInfo;
2866   RECT rcItem;
2867
2868   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
2869   scrollInfo.cbSize = sizeof(SCROLLINFO);
2870   scrollInfo.fMask = SIF_POS;
2871
2872   /* ALWAYS bPartial == FALSE, FOR NOW! */
2873
2874   rcItem.left = LVIR_BOUNDS;
2875   if (LISTVIEW_GetItemRect(hwnd, nItem, &rcItem) != FALSE)
2876   {
2877     if (rcItem.left < infoPtr->rcList.left)
2878     {
2879       if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
2880       {
2881         /* scroll left */
2882         if (uView == LVS_LIST)
2883         {
2884           nScrollPosWidth = infoPtr->nItemWidth;
2885           rcItem.left += infoPtr->rcList.left;
2886         }
2887         else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
2888         {
2889           nScrollPosWidth = max(1, nListWidth / LISTVIEW_SCROLL_DIV_SIZE);
2890           rcItem.left += infoPtr->rcList.left;
2891         }
2892         
2893         if (rcItem.left % nScrollPosWidth == 0)
2894         {
2895           scrollInfo.nPos += rcItem.left / nScrollPosWidth;
2896         }
2897         else
2898         {
2899           scrollInfo.nPos += rcItem.left / nScrollPosWidth - 1;
2900         }
2901         
2902         SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
2903       }
2904     }
2905     else if (rcItem.right > infoPtr->rcList.right)
2906     {
2907       if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
2908       {
2909         /* scroll right */
2910         if (uView == LVS_LIST)
2911         {
2912           rcItem.right -= infoPtr->rcList.right;
2913           nScrollPosWidth = infoPtr->nItemWidth;
2914         }
2915         else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
2916         {
2917           rcItem.right -= infoPtr->rcList.right;
2918           nScrollPosWidth = max(1, nListWidth / LISTVIEW_SCROLL_DIV_SIZE);
2919         }
2920
2921         if (rcItem.right % nScrollPosWidth == 0)
2922         {
2923           scrollInfo.nPos += rcItem.right / nScrollPosWidth;
2924         }
2925         else
2926         {
2927           scrollInfo.nPos += rcItem.right / nScrollPosWidth + 1;
2928         }
2929
2930         SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
2931       }
2932     }
2933     
2934     if (rcItem.top < infoPtr->rcList.top)
2935     {
2936       /* scroll up */
2937       if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
2938       {
2939         if (uView == LVS_REPORT)
2940         {
2941           rcItem.top -= infoPtr->rcList.top; 
2942           nScrollPosHeight = infoPtr->nItemHeight;
2943         }
2944         else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
2945         {
2946           nScrollPosHeight = max(1, nListHeight / LISTVIEW_SCROLL_DIV_SIZE);
2947           rcItem.top += infoPtr->rcList.top;
2948         }
2949
2950         if (rcItem.top % nScrollPosHeight == 0)
2951         {
2952           scrollInfo.nPos += rcItem.top / nScrollPosHeight;
2953         }
2954         else
2955         {
2956           scrollInfo.nPos += rcItem.top / nScrollPosHeight - 1;
2957         }
2958         
2959         SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
2960       }
2961     }
2962     else if (rcItem.bottom > infoPtr->rcList.bottom)
2963     {
2964       /* scroll down */
2965       if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
2966       {
2967         if (uView == LVS_REPORT)
2968         {
2969           rcItem.bottom -= infoPtr->rcList.bottom;
2970           nScrollPosHeight = infoPtr->nItemHeight;
2971         }
2972         else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
2973         {
2974           nScrollPosHeight = max(1, nListHeight / LISTVIEW_SCROLL_DIV_SIZE);
2975           rcItem.bottom -= infoPtr->rcList.bottom;
2976         }
2977
2978         if (rcItem.bottom % nScrollPosHeight == 0)
2979         {
2980           scrollInfo.nPos += rcItem.bottom / nScrollPosHeight;
2981         }
2982         else
2983         {
2984           scrollInfo.nPos += rcItem.bottom / nScrollPosHeight + 1;
2985         }
2986         
2987         SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
2988       }
2989     }
2990   }
2991   
2992   return TRUE;
2993 }
2994
2995 /***
2996  * DESCRIPTION:
2997  * Retrieves the nearest item, given a position and a direction.
2998  * 
2999  * PARAMETER(S):
3000  * [I] HWND : window handle
3001  * [I] POINT : start position
3002  * [I] UINT : direction
3003  *
3004  * RETURN:
3005  * Item index if successdful, -1 otherwise.
3006  */
3007 static INT LISTVIEW_GetNearestItem(HWND hwnd, POINT pt, UINT vkDirection)
3008 {
3009   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3010   LVHITTESTINFO lvHitTestInfo;
3011   INT nItem = -1;
3012   RECT rcView;
3013
3014   if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
3015   {
3016     ZeroMemory(&lvHitTestInfo, sizeof(LVHITTESTINFO));
3017     LISTVIEW_GetOrigin(hwnd, &lvHitTestInfo.pt);
3018     lvHitTestInfo.pt.x += pt.x;
3019     lvHitTestInfo.pt.y += pt.y;
3020     
3021     do
3022     { 
3023       if (vkDirection == VK_DOWN)
3024       {
3025         lvHitTestInfo.pt.y += infoPtr->nItemHeight;
3026       }      
3027       else if (vkDirection == VK_UP)
3028       {
3029         lvHitTestInfo.pt.y -= infoPtr->nItemHeight;
3030       }
3031       else if (vkDirection == VK_LEFT)
3032       {
3033         lvHitTestInfo.pt.x -= infoPtr->nItemWidth;
3034       }
3035       else if (vkDirection == VK_RIGHT)
3036       {
3037         lvHitTestInfo.pt.x += infoPtr->nItemWidth;
3038       }
3039
3040       if (PtInRect(&rcView, lvHitTestInfo.pt) == FALSE)
3041       {
3042         return -1;
3043       }
3044       else
3045       {
3046         nItem = LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo);
3047       }
3048
3049     }
3050     while (nItem == -1);
3051   }
3052
3053   return nItem;
3054 }  
3055
3056 /***
3057  * DESCRIPTION:
3058  * Searches for an item with specific characteristics.
3059  * 
3060  * PARAMETER(S):
3061  * [I] HWND : window handle
3062  * [I] INT : base item index
3063  * [I] LPLVFINDINFO : item information to look for
3064  * 
3065  * RETURN:
3066  *   SUCCESS : index of item
3067  *   FAILURE : -1
3068  */
3069 static LRESULT LISTVIEW_FindItem(HWND hwnd, INT nStart, 
3070                                  LPLVFINDINFO lpFindInfo)
3071 {
3072   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3073   POINT ptItem;
3074   CHAR szDispText[DISP_TEXT_SIZE];
3075   LVITEMA lvItem;
3076   BOOL bWrap = FALSE;
3077   INT nItem = nStart;
3078   INT nLast = GETITEMCOUNT(infoPtr);
3079
3080   if ((nItem >= -1) && (lpFindInfo != NULL))
3081   {
3082     ZeroMemory(&lvItem, sizeof(LVITEMA));
3083
3084     if (lpFindInfo->flags & LVFI_PARAM)
3085     {
3086       lvItem.mask |= LVIF_PARAM;
3087     }
3088     
3089     if (lpFindInfo->flags & LVFI_STRING)
3090     {
3091       lvItem.mask |= LVIF_TEXT;
3092       lvItem.pszText = szDispText;
3093       lvItem.cchTextMax = DISP_TEXT_SIZE;
3094     }
3095
3096     if (lpFindInfo->flags & LVFI_PARTIAL)
3097     {
3098       lvItem.mask |= LVIF_TEXT;
3099       lvItem.pszText = szDispText;
3100       lvItem.cchTextMax = DISP_TEXT_SIZE;
3101     }
3102
3103     if (lpFindInfo->flags & LVFI_WRAP)
3104     {
3105       bWrap = TRUE;
3106     }
3107
3108     if (lpFindInfo->flags & LVFI_NEARESTXY)
3109     {
3110       ptItem.x = lpFindInfo->pt.x;
3111       ptItem.y = lpFindInfo->pt.y;
3112     }
3113
3114     while (1)
3115     {
3116       while (nItem < nLast)
3117       {
3118         if (lpFindInfo->flags & LVFI_NEARESTXY)
3119         {
3120           nItem = LISTVIEW_GetNearestItem(hwnd, ptItem, 
3121                                           lpFindInfo->vkDirection);
3122           if (nItem != -1)
3123           {
3124             /* get position of the new item index */
3125             if (ListView_GetItemPosition(hwnd, nItem, &ptItem) == FALSE)
3126               return -1;
3127           }
3128           else
3129             return -1;
3130         }
3131         else
3132         {
3133           nItem++;
3134         }
3135         
3136         lvItem.iItem = nItem;
3137         lvItem.iSubItem = 0;
3138         if (ListView_GetItemA(hwnd, &lvItem) != FALSE)
3139         {
3140           if (lvItem.mask & LVIF_TEXT)
3141           {
3142             if (lpFindInfo->flags & LVFI_PARTIAL)
3143             {
3144               if (strstr(lvItem.pszText, lpFindInfo->psz) == NULL)
3145                 continue;
3146             }
3147             else
3148             {
3149               if (strcmp(lvItem.pszText, lpFindInfo->psz) != 0)
3150                 continue;
3151             }
3152           }
3153           
3154           if (lvItem.mask & LVIF_PARAM)
3155           {
3156             if (lpFindInfo->lParam != lvItem.lParam)
3157               continue;
3158           }
3159           
3160           return nItem;
3161         }
3162       }
3163
3164       if (bWrap != FALSE)
3165       {
3166         nItem = -1;
3167         nLast = nStart + 1;
3168         bWrap = FALSE;
3169       }
3170       else
3171       {
3172         return -1;
3173       }       
3174     }
3175   }
3176
3177  return -1; 
3178 }
3179
3180 /***
3181  * DESCRIPTION:
3182  * Retrieves the background color of the listview control.
3183  * 
3184  * PARAMETER(S):
3185  * [I] HWND : window handle
3186  *
3187  * RETURN:
3188  * COLORREF associated with the background.
3189  */
3190 static LRESULT LISTVIEW_GetBkColor(HWND hwnd)
3191 {
3192   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3193
3194   return infoPtr->clrBk;
3195 }
3196
3197 /***
3198  * DESCRIPTION:
3199  * Retrieves the background image of the listview control.
3200  * 
3201  * PARAMETER(S):
3202  * [I] HWND : window handle
3203  * [O] LPLVMKBIMAGE : background image attributes
3204  *
3205  * RETURN:
3206  *   SUCCESS : TRUE
3207  *   FAILURE : FALSE`
3208  */
3209 /* static LRESULT LISTVIEW_GetBkImage(HWND hwnd, LPLVBKIMAGE lpBkImage)   */
3210 /* {   */
3211 /*   FIXME (listview, "empty stub!\n"); */
3212 /*   return FALSE;   */
3213 /* }   */
3214
3215 /***
3216  * DESCRIPTION:
3217  * Retrieves the callback mask.
3218  * 
3219  * PARAMETER(S):
3220  * [I] HWND : window handle
3221  *
3222  * RETURN:
3223  *  Value of mask
3224  */
3225 static UINT LISTVIEW_GetCallbackMask(HWND hwnd)
3226 {
3227   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3228
3229   return infoPtr->uCallbackMask;
3230 }
3231
3232 /***
3233  * DESCRIPTION:
3234  * Retrieves column attributes.
3235  * 
3236  * PARAMETER(S):
3237  * [I] HWND : window handle
3238  * [I] INT :  column index
3239  * [IO] LPLVCOLUMNA : column information
3240  *
3241  * RETURN:
3242  *   SUCCESS : TRUE
3243  *   FAILURE : FALSE
3244  */
3245 static LRESULT LISTVIEW_GetColumnA(HWND hwnd, INT nItem, LPLVCOLUMNA lpColumn)
3246 {
3247   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3248   HDITEMA hdi;
3249   BOOL bResult = FALSE;
3250   
3251   if (lpColumn != NULL)
3252   {
3253     /* initialize memory */
3254     ZeroMemory(&hdi, sizeof(HDITEMA));
3255     
3256     if (lpColumn->mask & LVCF_FMT)
3257     {
3258       hdi.mask |= HDI_FORMAT;
3259     }
3260
3261     if (lpColumn->mask & LVCF_WIDTH)
3262     {
3263       hdi.mask |= HDI_WIDTH;
3264     }
3265
3266     if (lpColumn->mask & LVCF_TEXT)
3267     {
3268       hdi.mask |= (HDI_TEXT | HDI_FORMAT);
3269     }
3270
3271     if (lpColumn->mask & LVCF_IMAGE)
3272     {
3273       hdi.mask |= HDI_IMAGE;
3274     }
3275
3276     if (lpColumn->mask & LVCF_ORDER)
3277     {
3278       hdi.mask |= HDI_ORDER;
3279     }
3280
3281     bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
3282     if (bResult != FALSE)
3283     {
3284       if (lpColumn->mask & LVCF_FMT) 
3285       {
3286         lpColumn->fmt = 0;
3287
3288         if (hdi.fmt & HDF_LEFT)
3289         {
3290           lpColumn->fmt |= LVCFMT_LEFT;
3291         }
3292         else if (hdi.fmt & HDF_RIGHT)
3293         {
3294           lpColumn->fmt |= LVCFMT_RIGHT;
3295         }
3296         else if (hdi.fmt & HDF_CENTER)
3297         {
3298           lpColumn->fmt |= LVCFMT_CENTER;
3299         }
3300
3301         if (hdi.fmt & HDF_IMAGE)
3302         {
3303           lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
3304         }
3305
3306         if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
3307         {
3308           lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
3309         }
3310       }
3311
3312       if (lpColumn->mask & LVCF_WIDTH)
3313       {
3314         lpColumn->cx = hdi.cxy;
3315       }
3316       
3317       if ((lpColumn->mask & LVCF_TEXT) && (lpColumn->pszText) && (hdi.pszText))
3318       {
3319         lstrcpynA (lpColumn->pszText, hdi.pszText, lpColumn->cchTextMax);
3320       }
3321
3322       if (lpColumn->mask & LVCF_IMAGE)
3323       {
3324         lpColumn->iImage = hdi.iImage;
3325       }
3326
3327       if (lpColumn->mask & LVCF_ORDER)
3328       {
3329         lpColumn->iOrder = hdi.iOrder;
3330       }
3331     }
3332   }
3333
3334   return bResult;
3335 }
3336
3337 /* LISTVIEW_GetColumnW */
3338 /* LISTVIEW_GetColumnOrderArray */
3339
3340 /***
3341  * DESCRIPTION:
3342  * Retrieves the column width.
3343  * 
3344  * PARAMETER(S):
3345  * [I] HWND : window handle
3346  * [I] int : column index
3347  *
3348  * RETURN:
3349  *   SUCCESS : column width
3350  *   FAILURE : zero 
3351  */
3352 static LRESULT LISTVIEW_GetColumnWidth(HWND hwnd, INT nColumn)
3353 {
3354   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3355   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3356   INT nColumnWidth = 0;
3357   HDITEMA hdi;
3358
3359   if (uView == LVS_LIST)
3360   {
3361     nColumnWidth = infoPtr->nItemWidth;
3362   }
3363   else if (uView == LVS_REPORT)
3364   {
3365     /* get column width from header */
3366     ZeroMemory(&hdi, sizeof(HDITEMA));
3367     hdi.mask = HDI_WIDTH;
3368     if (Header_GetItemA(infoPtr->hwndHeader, nColumn, &hdi) != FALSE)
3369     {
3370       nColumnWidth = hdi.cxy;
3371     }
3372   }
3373
3374   return nColumnWidth;
3375 }
3376
3377 /***
3378  * DESCRIPTION:
3379  * In list or report display mode, retrieves the number of items that can fit 
3380  * vertically in the visible area. In icon or small icon display mode, 
3381  * retrieves the total number of visible items.
3382  * 
3383  * PARAMETER(S):
3384  * [I] HWND : window handle
3385  *
3386  * RETURN:
3387  * Number of fully visible items.
3388  */
3389 static LRESULT LISTVIEW_GetCountPerPage(HWND hwnd)
3390 {
3391   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3392   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3393   INT nItemCount = 0;
3394
3395   if (uView == LVS_LIST)
3396   {
3397     if (infoPtr->rcList.right > infoPtr->nItemWidth)
3398     {
3399       nItemCount = LISTVIEW_GetCountPerRow(hwnd) * 
3400                    LISTVIEW_GetCountPerColumn(hwnd);
3401     }
3402   }
3403   else if (uView == LVS_REPORT)
3404   {
3405     nItemCount = LISTVIEW_GetCountPerColumn(hwnd);
3406   }
3407   else
3408   {
3409     nItemCount = GETITEMCOUNT(infoPtr);
3410   }
3411
3412   return nItemCount;
3413 }
3414
3415 /* LISTVIEW_GetEditControl */
3416
3417 /***
3418  * DESCRIPTION:
3419  * Retrieves the extended listview style.
3420  *
3421  * PARAMETERS:
3422  * [I] HWND  : window handle
3423  *
3424  * RETURN:
3425  *   SUCCESS : previous style
3426  *   FAILURE : 0
3427  */
3428 static LRESULT LISTVIEW_GetExtendedListViewStyle(HWND hwnd)
3429 {
3430     LISTVIEW_INFO *infoPtr;
3431
3432     /* make sure we can get the listview info */
3433     if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
3434         return (0);
3435
3436     return (infoPtr->dwExStyle);
3437 }
3438
3439 /***
3440  * DESCRIPTION:
3441  * Retrieves the handle to the header control.
3442  * 
3443  * PARAMETER(S):
3444  * [I] HWND : window handle
3445  *
3446  * RETURN:
3447  * Header handle.
3448  */
3449 static LRESULT LISTVIEW_GetHeader(HWND hwnd)
3450 {
3451   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3452
3453   return infoPtr->hwndHeader;
3454 }
3455
3456 /* LISTVIEW_GetHotCursor */
3457 /* LISTVIEW_GetHotItem */
3458 /* LISTVIEW_GetHoverTime */
3459
3460 /***
3461  * DESCRIPTION:
3462  * Retrieves an image list handle.
3463  * 
3464  * PARAMETER(S):
3465  * [I] HWND : window handle
3466  * [I] INT : image list identifier 
3467  * 
3468  * RETURN:
3469  *   SUCCESS : image list handle
3470  *   FAILURE : NULL
3471  */
3472 static LRESULT LISTVIEW_GetImageList(HWND hwnd, INT nImageList)
3473 {
3474   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3475   HIMAGELIST himl = NULL;
3476
3477   switch (nImageList) 
3478   {
3479   case LVSIL_NORMAL:
3480     himl = infoPtr->himlNormal;
3481     break;
3482   case LVSIL_SMALL:
3483     himl = infoPtr->himlSmall;
3484     break;
3485   case LVSIL_STATE:
3486     himl = infoPtr->himlState;
3487     break;
3488   }
3489
3490   return (LRESULT)himl;
3491 }
3492
3493 /* LISTVIEW_GetISearchString */
3494
3495 /***
3496  * DESCRIPTION:
3497  * Retrieves item attributes.
3498  * 
3499  * PARAMETER(S):
3500  * [I] HWND : window handle
3501  * [IO] LPLVITEMA : item info
3502  * 
3503  * RETURN:
3504  *   SUCCESS : TRUE 
3505  *   FAILURE : FALSE
3506  */
3507 static LRESULT LISTVIEW_GetItemA(HWND hwnd, LPLVITEMA lpLVItem)
3508 {
3509   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3510   LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
3511   BOOL bResult = FALSE;
3512   NMLVDISPINFOA dispInfo;
3513   LISTVIEW_SUBITEM *lpSubItem;
3514   LISTVIEW_ITEM *lpItem;
3515   HDPA hdpaSubItems;
3516
3517   TRACE("(hwnd=%x, lpLVItem=%p)\n", hwnd, lpLVItem);
3518
3519   if (lpLVItem != NULL)
3520   {
3521     if ((lpLVItem->iItem >= 0) && (lpLVItem->iItem < GETITEMCOUNT(infoPtr)))
3522     {
3523       ZeroMemory(&dispInfo, sizeof(NMLVDISPINFOA));
3524       hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3525       if (hdpaSubItems != NULL)
3526       {
3527         lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3528         if (lpItem != NULL)
3529         {
3530           bResult = TRUE;
3531           if (lpLVItem->iSubItem == 0)
3532           {
3533             if ((lpItem->iImage == I_IMAGECALLBACK) &&
3534                 (lpLVItem->mask & LVIF_IMAGE))
3535             {
3536               dispInfo.item.mask |= LVIF_IMAGE;
3537             }
3538             
3539             if ((lpItem->pszText == LPSTR_TEXTCALLBACKA) && 
3540                 (lpLVItem->mask & LVIF_TEXT))
3541             {
3542               dispInfo.item.mask |= LVIF_TEXT;
3543               ZeroMemory(lpLVItem->pszText, sizeof(CHAR)*lpLVItem->cchTextMax);
3544               dispInfo.item.pszText = lpLVItem->pszText;
3545               dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
3546             }
3547             
3548             if ((infoPtr->uCallbackMask != 0) && (lpLVItem->mask & LVIF_STATE))
3549             {        
3550               dispInfo.item.mask |= LVIF_STATE;
3551               dispInfo.item.stateMask = infoPtr->uCallbackMask; 
3552             }
3553             
3554             if (dispInfo.item.mask != 0)
3555             {
3556               dispInfo.hdr.hwndFrom = hwnd;
3557               dispInfo.hdr.idFrom = lCtrlId;
3558               dispInfo.hdr.code = LVN_GETDISPINFOA;
3559               dispInfo.item.iItem = lpLVItem->iItem;
3560               dispInfo.item.iSubItem = 0;
3561               dispInfo.item.lParam = lpItem->lParam;
3562               ListView_Notify(GetParent(hwnd), lCtrlId, &dispInfo);
3563             }
3564             
3565             if (dispInfo.item.mask & LVIF_IMAGE)
3566             {
3567               lpLVItem->iImage = dispInfo.item.iImage;
3568             }
3569             else if (lpLVItem->mask & LVIF_IMAGE)
3570             {
3571               lpLVItem->iImage = lpItem->iImage;
3572             }
3573             
3574             if (dispInfo.item.mask & LVIF_TEXT)
3575             {
3576               if (dispInfo.item.mask & LVIF_DI_SETITEM)
3577               {
3578                 Str_SetPtrA(&lpItem->pszText, dispInfo.item.pszText);
3579               }
3580               strncpy(lpLVItem->pszText, dispInfo.item.pszText, lpLVItem->cchTextMax);
3581             }
3582             else if (lpLVItem->mask & LVIF_TEXT)
3583             {
3584               strncpy(lpLVItem->pszText, lpItem->pszText, lpLVItem->cchTextMax);
3585             }
3586             
3587             if (dispInfo.item.mask & LVIF_STATE)
3588             {
3589               lpLVItem->state = lpItem->state;
3590               lpLVItem->state &= ~dispInfo.item.stateMask;
3591               lpLVItem->state |= (dispInfo.item.state & 
3592                                   dispInfo.item.stateMask);
3593             }
3594             else if (lpLVItem->mask & LVIF_STATE)
3595             {
3596               lpLVItem->state = lpItem->state & lpLVItem->stateMask;
3597             }
3598
3599             if (lpLVItem->mask & LVIF_PARAM)
3600             {
3601               lpLVItem->lParam = lpItem->lParam;
3602             }
3603             
3604             if (lpLVItem->mask & LVIF_INDENT)
3605             {
3606               lpLVItem->iIndent = lpItem->iIndent;
3607             }
3608           }
3609           else
3610           {
3611             lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, 
3612                                                lpLVItem->iSubItem);
3613             if (lpSubItem != NULL)
3614             {
3615               if ((lpSubItem->iImage == I_IMAGECALLBACK) &&
3616                   (lpLVItem->mask & LVIF_IMAGE))
3617               {
3618                 dispInfo.item.mask |= LVIF_IMAGE;
3619               }
3620               
3621               if ((lpSubItem->pszText == LPSTR_TEXTCALLBACKA) && 
3622                   (lpLVItem->mask & LVIF_TEXT))
3623               {
3624                 dispInfo.item.mask |= LVIF_TEXT;
3625                 ZeroMemory(lpLVItem->pszText, 
3626                            sizeof(CHAR)*lpLVItem->cchTextMax);
3627                 dispInfo.item.pszText = lpLVItem->pszText;
3628                 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
3629               }
3630             }
3631             else
3632             {
3633               if (lpLVItem->mask & LVIF_IMAGE)
3634               {
3635                 dispInfo.item.mask |= LVIF_IMAGE;
3636               }
3637           
3638               if (lpLVItem->mask & LVIF_TEXT)
3639               {
3640                 dispInfo.item.mask |= LVIF_TEXT;
3641                 ZeroMemory(lpLVItem->pszText, 
3642                            sizeof(CHAR)*lpLVItem->cchTextMax);
3643                 dispInfo.item.pszText = lpLVItem->pszText;
3644                 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
3645               }
3646             }
3647
3648             if (dispInfo.item.mask != 0)
3649             {
3650               dispInfo.hdr.hwndFrom = hwnd;
3651               dispInfo.hdr.idFrom = lCtrlId;
3652               dispInfo.hdr.code = LVN_GETDISPINFOA;
3653               dispInfo.item.iItem = lpLVItem->iItem;
3654               dispInfo.item.iSubItem = lpLVItem->iSubItem;
3655               dispInfo.item.lParam = lpItem->lParam;
3656               ListView_Notify(GetParent(hwnd), lCtrlId, &dispInfo);
3657             }
3658
3659             if (dispInfo.item.mask & LVIF_IMAGE)
3660             {
3661               lpLVItem->iImage = dispInfo.item.iImage;
3662             }
3663             else if (lpLVItem->mask & LVIF_IMAGE)
3664             {
3665               lpLVItem->iImage = lpItem->iImage;
3666             }
3667
3668             if (dispInfo.item.mask & LVIF_TEXT)
3669             {
3670               if (dispInfo.item.mask & LVIF_DI_SETITEM)
3671               {
3672                 if (lpSubItem)
3673                   Str_SetPtrA(&lpSubItem->pszText, dispInfo.item.pszText);
3674               }
3675               strncpy(lpLVItem->pszText, dispInfo.item.pszText, lpLVItem->cchTextMax);
3676             }
3677             else if (lpLVItem->mask & LVIF_TEXT)
3678             {
3679               strncpy(lpLVItem->pszText, lpSubItem->pszText, lpLVItem->cchTextMax);
3680             }
3681           }
3682         }
3683       }
3684     }
3685   }
3686
3687   return bResult;
3688 }
3689
3690 /* LISTVIEW_GetItemW */
3691 /* LISTVIEW_GetHotCursor */
3692
3693 /***
3694  * DESCRIPTION:
3695  * Retrieves the index of the hot item.
3696  *
3697  * PARAMETERS:
3698  * [I] HWND  : window handle
3699  *
3700  * RETURN:
3701  *   SUCCESS : hot item index
3702  *   FAILURE : -1 (no hot item)
3703  */
3704 static LRESULT LISTVIEW_GetHotItem(HWND hwnd)
3705 {
3706     LISTVIEW_INFO *infoPtr;
3707
3708     /* make sure we can get the listview info */
3709     if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
3710         return (-1);
3711
3712     return (infoPtr->nHotItem);
3713 }
3714
3715 /* LISTVIEW_GetHoverTime */
3716
3717 /***
3718  * DESCRIPTION:
3719  * Retrieves the number of items in the listview control.
3720  * 
3721  * PARAMETER(S):
3722  * [I] HWND : window handle
3723  * 
3724  * RETURN:
3725  * Number of items.
3726  */
3727 static LRESULT LISTVIEW_GetItemCount(HWND hwnd)
3728 {
3729   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3730
3731   return GETITEMCOUNT(infoPtr);
3732 }
3733
3734 /***
3735  * DESCRIPTION:
3736  * Retrieves the position (upper-left) of the listview control item.
3737  * 
3738  * PARAMETER(S):
3739  * [I] HWND : window handle
3740  * [I] INT : item index
3741  * [O] LPPOINT : coordinate information
3742  *
3743  * RETURN:
3744  *   SUCCESS : TRUE
3745  *   FAILURE : FALSE
3746  */
3747 static BOOL LISTVIEW_GetItemPosition(HWND hwnd, INT nItem, 
3748                                      LPPOINT lpptPosition)
3749 {
3750   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3751   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3752   BOOL bResult = FALSE;
3753   HDPA hdpaSubItems;
3754   LISTVIEW_ITEM *lpItem;
3755   INT nCountPerColumn;
3756   INT nRow;
3757
3758   TRACE("(hwnd=%x,nItem=%d,lpptPosition=%p)\n", hwnd, nItem, 
3759         lpptPosition);
3760
3761   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && 
3762       (lpptPosition != NULL))
3763   {
3764     if (uView == LVS_LIST)
3765     {
3766       bResult = TRUE;
3767       nItem = nItem - ListView_GetTopIndex(hwnd);
3768       nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
3769       if (nItem < 0)
3770       {
3771         nRow = nItem % nCountPerColumn;
3772         if (nRow == 0)
3773         {
3774           lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
3775           lpptPosition->y = 0;
3776         }
3777         else
3778         {
3779           lpptPosition->x = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
3780           lpptPosition->y = (nRow + nCountPerColumn) * infoPtr->nItemHeight;  
3781         }
3782       }
3783       else
3784       {
3785         lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
3786         lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
3787       }
3788     }
3789     else if (uView == LVS_REPORT)
3790     {
3791       bResult = TRUE;
3792       lpptPosition->x = REPORT_MARGINX;
3793       lpptPosition->y = ((nItem - ListView_GetTopIndex(hwnd)) * 
3794                          infoPtr->nItemHeight) + infoPtr->rcList.top;
3795     }
3796     else
3797     {
3798       hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
3799       if (hdpaSubItems != NULL)
3800       {
3801         lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3802         if (lpItem != NULL)
3803         {
3804           bResult = TRUE;
3805           lpptPosition->x = lpItem->ptPosition.x;
3806           lpptPosition->y = lpItem->ptPosition.y;
3807         }
3808       }
3809     }
3810   }
3811     
3812   return bResult;
3813 }
3814
3815 /***
3816  * DESCRIPTION:
3817  * Retrieves the bounding rectangle for a listview control item.
3818  * 
3819  * PARAMETER(S):
3820  * [I] HWND : window handle
3821  * [I] INT : item index
3822  * [IO] LPRECT : bounding rectangle coordinates
3823  * 
3824  * RETURN:
3825  *   SUCCESS : TRUE
3826  *   FAILURE : FALSE
3827  */
3828 static LRESULT LISTVIEW_GetItemRect(HWND hwnd, INT nItem, LPRECT lprc)
3829 {
3830   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3831   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3832   BOOL bResult = FALSE;
3833   POINT ptOrigin;
3834   POINT ptItem;
3835   HDC hdc;
3836   HFONT hOldFont;
3837   INT nLeftPos;
3838   INT nLabelWidth;
3839   TEXTMETRICA tm;
3840
3841   TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd, nItem, lprc);
3842   
3843   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
3844   {
3845     if (ListView_GetItemPosition(hwnd, nItem, &ptItem) != FALSE)
3846     {
3847       switch(lprc->left)  
3848       {  
3849       case LVIR_ICON: 
3850         if (uView == LVS_ICON)
3851         {
3852           if (infoPtr->himlNormal != NULL)
3853           {
3854             if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
3855             {
3856               bResult = TRUE;
3857               lprc->left = ptItem.x + ptOrigin.x;
3858               lprc->top = ptItem.y + ptOrigin.y;
3859               lprc->right = lprc->left + infoPtr->iconSize.cx;
3860               lprc->bottom = (lprc->top + infoPtr->iconSize.cy + 
3861                               ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
3862             }
3863           }
3864         }
3865         else if (uView == LVS_SMALLICON)
3866         {
3867           if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
3868           {
3869             bResult = TRUE;
3870             lprc->left = ptItem.x + ptOrigin.x;
3871             lprc->top = ptItem.y + ptOrigin.y;
3872             lprc->bottom = lprc->top + infoPtr->nItemHeight;
3873
3874             if (infoPtr->himlState != NULL)
3875               lprc->left += infoPtr->iconSize.cx;
3876               
3877             if (infoPtr->himlSmall != NULL)
3878               lprc->right = lprc->left + infoPtr->iconSize.cx;
3879             else
3880               lprc->right = lprc->left;
3881           }
3882         }
3883         else 
3884         {
3885           bResult = TRUE;
3886           lprc->left = ptItem.x;
3887           lprc->top = ptItem.y;
3888           lprc->bottom = lprc->top + infoPtr->nItemHeight;
3889
3890           if (infoPtr->himlState != NULL)
3891           {
3892             lprc->left += infoPtr->iconSize.cx;
3893           }
3894             
3895           if (infoPtr->himlSmall != NULL)
3896           {
3897             lprc->right = lprc->left + infoPtr->iconSize.cx;
3898           }
3899           else
3900           {
3901             lprc->right = lprc->left;
3902           }
3903         }
3904         break;
3905
3906       case LVIR_LABEL: 
3907         if (uView == LVS_ICON)
3908         {
3909           if (infoPtr->himlNormal != NULL)
3910           {
3911             if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
3912             {
3913               bResult = TRUE;
3914               lprc->left = ptItem.x + ptOrigin.x;
3915               lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
3916                            ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
3917               nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
3918               if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
3919               {
3920                 lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
3921                 lprc->right = lprc->left + nLabelWidth;
3922               }
3923               else
3924               {
3925                 lprc->left += 1;
3926                 lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
3927               }
3928             
3929               hdc = GetDC(hwnd);
3930               hOldFont = SelectObject(hdc, infoPtr->hFont);
3931               GetTextMetricsA(hdc, &tm);
3932               lprc->bottom = lprc->top + tm.tmHeight + HEIGHT_PADDING;
3933               SelectObject(hdc, hOldFont);
3934               ReleaseDC(hwnd, hdc);
3935             }              
3936           }
3937         }
3938         else if (uView == LVS_SMALLICON)
3939         {
3940           if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
3941           {
3942             bResult = TRUE;
3943             nLeftPos = lprc->left = ptItem.x + ptOrigin.x; 
3944             lprc->top = ptItem.y + ptOrigin.y;
3945             lprc->bottom = lprc->top + infoPtr->nItemHeight;
3946             
3947             if (infoPtr->himlState != NULL)
3948             {
3949               lprc->left += infoPtr->iconSize.cx;
3950             }
3951             
3952             if (infoPtr->himlSmall != NULL)
3953             {
3954               lprc->left += infoPtr->iconSize.cx;
3955             }
3956             
3957             nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
3958             if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
3959             {
3960               lprc->right = lprc->left + nLabelWidth;
3961             }
3962             else
3963             {
3964               lprc->right = nLeftPos + infoPtr->nItemWidth;
3965             }
3966           }
3967         }
3968         else
3969         {
3970           bResult = TRUE;
3971           nLeftPos = lprc->left = ptItem.x; 
3972           lprc->top = ptItem.y;
3973           lprc->bottom = lprc->top + infoPtr->nItemHeight;
3974
3975           if (infoPtr->himlState != NULL)
3976           {
3977             lprc->left += infoPtr->iconSize.cx;
3978           }
3979
3980           if (infoPtr->himlSmall != NULL)
3981           {
3982             lprc->left += infoPtr->iconSize.cx;
3983           }
3984
3985           nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
3986           if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
3987           {
3988             lprc->right = lprc->left + nLabelWidth;
3989           }
3990           else
3991           {
3992             lprc->right = nLeftPos + infoPtr->nItemWidth;
3993           }
3994         }
3995         break;
3996
3997       case LVIR_BOUNDS:
3998         if (uView == LVS_ICON)
3999         {
4000           if (infoPtr->himlNormal != NULL)
4001           {
4002             if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
4003             {
4004               bResult = TRUE;
4005               lprc->left = ptItem.x + ptOrigin.x;
4006               lprc->top = ptItem.y + ptOrigin.y; 
4007               lprc->right = lprc->left + infoPtr->iconSpacing.cx;
4008               lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
4009             }
4010           } 
4011         }
4012         else if (uView == LVS_SMALLICON)
4013         {
4014           if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
4015           {
4016             bResult = TRUE;
4017             lprc->left = ptItem.x + ptOrigin.x; 
4018             lprc->right = lprc->left;
4019             lprc->top = ptItem.y + ptOrigin.y;
4020             lprc->bottom = lprc->top + infoPtr->nItemHeight;
4021             if (infoPtr->himlState != NULL)
4022               lprc->right += infoPtr->iconSize.cx;
4023             if (infoPtr->himlSmall != NULL)
4024               lprc->right += infoPtr->iconSize.cx;
4025
4026             nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
4027             if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
4028             {
4029               lprc->right += nLabelWidth;
4030             }
4031             else
4032             {
4033               lprc->right = lprc->left + infoPtr->nItemWidth;
4034             }
4035           }
4036         }
4037         else
4038         {
4039           bResult = TRUE;
4040           lprc->left = ptItem.x; 
4041           lprc->right = lprc->left;
4042           lprc->top = ptItem.y;
4043           lprc->bottom = lprc->top + infoPtr->nItemHeight;
4044
4045           if (infoPtr->himlState != NULL)
4046           {
4047             lprc->right += infoPtr->iconSize.cx;
4048           }
4049
4050           if (infoPtr->himlSmall != NULL)
4051           {
4052             lprc->right += infoPtr->iconSize.cx;
4053           }
4054
4055           nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
4056           if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
4057           {
4058             lprc->right += nLabelWidth;
4059           }
4060           else
4061           {
4062             lprc->right = lprc->left + infoPtr->nItemWidth;
4063           }
4064         }
4065         break;
4066         
4067       case LVIR_SELECTBOUNDS:
4068         if (uView == LVS_ICON)
4069         {
4070           if (infoPtr->himlNormal != NULL)
4071           {
4072             if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
4073             {
4074               bResult = TRUE;
4075               lprc->left = ptItem.x + ptOrigin.x;
4076               lprc->top = ptItem.y + ptOrigin.y;
4077               lprc->right = lprc->left + infoPtr->iconSpacing.cx;
4078               lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
4079             }
4080           } 
4081         }
4082         else if (uView == LVS_SMALLICON)
4083         {
4084           if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
4085           {
4086             bResult = TRUE;
4087             nLeftPos= lprc->left = ptItem.x + ptOrigin.x;  
4088             lprc->top = ptItem.y + ptOrigin.y;
4089             lprc->bottom = lprc->top + infoPtr->nItemHeight;
4090             
4091             if (infoPtr->himlState != NULL)
4092             {
4093               lprc->left += infoPtr->iconSize.cx;
4094             }
4095             
4096             lprc->right = lprc->left;
4097             
4098             if (infoPtr->himlSmall != NULL)
4099             {
4100               lprc->right += infoPtr->iconSize.cx;
4101             }
4102             
4103             nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
4104             if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
4105             {
4106               lprc->right += nLabelWidth;
4107             }
4108             else
4109             {
4110               lprc->right = nLeftPos + infoPtr->nItemWidth;
4111             }
4112           }
4113         }
4114         else
4115         {
4116           bResult = TRUE;
4117           nLeftPos = lprc->left = ptItem.x; 
4118           lprc->top = ptItem.y;
4119           lprc->bottom = lprc->top + infoPtr->nItemHeight;
4120
4121           if (infoPtr->himlState != NULL)
4122           {
4123             lprc->left += infoPtr->iconSize.cx;
4124           }
4125           
4126           lprc->right = lprc->left;
4127         
4128           if (infoPtr->himlSmall != NULL)
4129           {
4130             lprc->right += infoPtr->iconSize.cx;
4131           }
4132
4133           nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
4134           if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
4135           {
4136             lprc->right += nLabelWidth;
4137           }
4138           else
4139           {
4140             lprc->right = nLeftPos + infoPtr->nItemWidth;
4141           }
4142         }
4143         break;
4144       }
4145     }
4146   }
4147         
4148   return bResult;
4149 }
4150
4151 /***
4152  * DESCRIPTION:
4153  * Retrieves the width of a label.
4154  * 
4155  * PARAMETER(S):
4156  * [I] HWND : window handle
4157  * 
4158  * RETURN:
4159  *   SUCCESS : string width (in pixels)
4160  *   FAILURE : zero
4161  */
4162 static INT LISTVIEW_GetLabelWidth(HWND hwnd, INT nItem)
4163 {
4164   CHAR szDispText[DISP_TEXT_SIZE];
4165   INT nLabelWidth = 0;
4166   LVITEMA lvItem;
4167
4168   TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
4169
4170   ZeroMemory(&lvItem, sizeof(LVITEMA));
4171   lvItem.mask = LVIF_TEXT;
4172   lvItem.iItem = nItem;
4173   lvItem.cchTextMax = DISP_TEXT_SIZE;
4174   lvItem.pszText = szDispText;
4175   if (ListView_GetItemA(hwnd, &lvItem) != FALSE)
4176   {
4177     nLabelWidth = ListView_GetStringWidthA(hwnd, lvItem.pszText); 
4178   }
4179     
4180   return nLabelWidth;
4181 }
4182
4183 /***
4184  * DESCRIPTION:
4185  * Retrieves the spacing between listview control items.
4186  * 
4187  * PARAMETER(S):
4188  * [I] HWND : window handle
4189  * [I] BOOL : flag for small or large icon 
4190  *
4191  * RETURN:
4192  * Horizontal + vertical spacing
4193  */
4194 static LRESULT LISTVIEW_GetItemSpacing(HWND hwnd, BOOL bSmall)
4195 {
4196   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4197   LONG lResult;
4198
4199   if (bSmall == FALSE)
4200   {
4201     lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
4202   }
4203   else
4204   {
4205     /* TODO: need to store width of smallicon item */
4206     lResult = MAKELONG(0, infoPtr->nItemHeight);
4207   }  
4208   
4209   return lResult;
4210 }
4211
4212 /***
4213  * DESCRIPTION:
4214  * Retrieves the state of a listview control item.
4215  * 
4216  * PARAMETER(S):
4217  * [I] HWND : window handle
4218  * [I] INT : item index
4219  * [I] UINT : state mask
4220  * 
4221  * RETURN:
4222  * State specified by the mask.
4223  */
4224 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask)
4225 {
4226   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4227   LVITEMA lvItem;
4228   UINT uState = 0;
4229
4230   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
4231   {
4232     ZeroMemory(&lvItem, sizeof(LVITEMA));
4233     lvItem.iItem = nItem;
4234     lvItem.stateMask = uMask;
4235     lvItem.mask = LVIF_STATE;
4236     if (ListView_GetItemA(hwnd, &lvItem) != FALSE)
4237     {
4238       uState = lvItem.state;
4239     }
4240   }
4241
4242   return uState;
4243 }
4244
4245 /***
4246  * DESCRIPTION:
4247  * Retrieves the text of a listview control item or subitem. 
4248  * 
4249  * PARAMETER(S):
4250  * [I] HWND : window handle
4251  * [I] INT : item index
4252  * [IO] LPLVITEMA : item information
4253  * 
4254  * RETURN:
4255  *   SUCCESS : string length
4256  *   FAILURE : 0
4257  */
4258 static LRESULT LISTVIEW_GetItemTextA(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
4259 {
4260   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4261   INT nLength = 0;
4262   
4263   if (lpLVItem != NULL)
4264   {
4265     if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
4266     {
4267       lpLVItem->mask = LVIF_TEXT;
4268       lpLVItem->iItem = nItem;
4269       if (ListView_GetItemA(hwnd, lpLVItem) != FALSE)
4270       {
4271         nLength = lstrlenA(lpLVItem->pszText);
4272       }
4273     }
4274   }
4275
4276   return nLength;
4277 }
4278
4279 /***
4280  * DESCRIPTION:
4281  * Searches for an item based on properties + relationships.
4282  * 
4283  * PARAMETER(S):
4284  * [I] HWND : window handle
4285  * [I] INT : item index
4286  * [I] INT : relationship flag
4287  * 
4288  * RETURN:
4289  *   SUCCESS : item index
4290  *   FAILURE : -1
4291  */
4292 static LRESULT LISTVIEW_GetNextItem(HWND hwnd, INT nItem, UINT uFlags)
4293 {
4294   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4295   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4296   UINT uMask = 0;
4297   LVFINDINFO lvFindInfo;
4298   INT nCountPerColumn;
4299   INT i;
4300   
4301   if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
4302   { 
4303     ZeroMemory(&lvFindInfo, sizeof(LVFINDINFO));
4304
4305     if (uFlags & LVNI_CUT)
4306       uMask |= LVIS_CUT;
4307     
4308     if (uFlags & LVNI_DROPHILITED)
4309       uMask |= LVIS_DROPHILITED;
4310           
4311     if (uFlags & LVNI_FOCUSED)
4312       uMask |= LVIS_FOCUSED;
4313
4314     if (uFlags & LVNI_SELECTED)
4315       uMask |= LVIS_SELECTED;
4316
4317     if (uFlags & LVNI_ABOVE)
4318     {
4319       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
4320       {
4321         while (nItem >= 0)
4322         {
4323           nItem--;
4324           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
4325             return nItem;
4326         }
4327       }
4328       else
4329       {
4330         lvFindInfo.flags = LVFI_NEARESTXY;
4331         lvFindInfo.vkDirection = VK_UP;
4332         ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
4333         while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
4334         {
4335           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
4336             return nItem;
4337         }
4338       }
4339     }
4340     else if (uFlags & LVNI_BELOW)
4341     {
4342       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
4343       {
4344         while (nItem < GETITEMCOUNT(infoPtr))
4345         {
4346           nItem++;
4347           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
4348             return nItem;
4349         }
4350       }
4351       else
4352       {
4353         lvFindInfo.flags = LVFI_NEARESTXY;
4354         lvFindInfo.vkDirection = VK_DOWN;
4355         ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
4356         while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
4357         {
4358           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
4359             return nItem;
4360         }
4361       }
4362     }
4363     else if (uFlags & LVNI_TOLEFT)
4364     {
4365       if (uView == LVS_LIST)
4366       {
4367         nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
4368         while (nItem - nCountPerColumn >= 0)
4369         {
4370           nItem -= nCountPerColumn;
4371           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
4372             return nItem;
4373         }
4374       }
4375       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4376       {
4377         lvFindInfo.flags = LVFI_NEARESTXY;
4378         lvFindInfo.vkDirection = VK_LEFT;
4379         ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
4380         while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
4381         {
4382           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
4383             return nItem;
4384         }
4385       }
4386     }
4387     else if (uFlags & LVNI_TORIGHT)
4388     {
4389       if (uView == LVS_LIST)
4390       {
4391         nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
4392         while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
4393         {
4394           nItem += nCountPerColumn;
4395           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
4396             return nItem;
4397         }
4398       }
4399       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4400       {
4401         lvFindInfo.flags = LVFI_NEARESTXY;
4402         lvFindInfo.vkDirection = VK_RIGHT;
4403         ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
4404         while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
4405         {
4406           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
4407             return nItem;
4408         }
4409       }
4410     }
4411     else
4412     {
4413       nItem++;
4414
4415       /* search by index */
4416       for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
4417       {
4418         if ((ListView_GetItemState(hwnd, i, uMask) & uMask) == uMask)
4419           return i;
4420       }
4421     }
4422   }
4423
4424   return -1;
4425 }
4426
4427 /* LISTVIEW_GetNumberOfWorkAreas */
4428
4429 /***
4430  * DESCRIPTION:
4431  * Retrieves the origin coordinates when in icon or small icon display mode.
4432  * 
4433  * PARAMETER(S):
4434  * [I] HWND : window handle
4435  * [O] LPPOINT : coordinate information
4436  * 
4437  * RETURN:
4438  *   SUCCESS : TRUE
4439  *   FAILURE : FALSE
4440  */
4441 static LRESULT LISTVIEW_GetOrigin(HWND hwnd, LPPOINT lpptOrigin)
4442 {
4443   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
4444   UINT uView = lStyle & LVS_TYPEMASK;
4445   BOOL bResult = FALSE;
4446   
4447   TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd, lpptOrigin);
4448
4449   if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4450   {
4451     SCROLLINFO scrollInfo;
4452     ZeroMemory(lpptOrigin, sizeof(POINT));
4453     ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
4454     scrollInfo.cbSize = sizeof(SCROLLINFO);
4455
4456     if (lStyle & WS_HSCROLL)
4457     {
4458       scrollInfo.fMask = SIF_POS;
4459       if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE) 
4460       {
4461         lpptOrigin->x = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE; 
4462       }
4463     }
4464
4465     if (lStyle & WS_VSCROLL)
4466     {
4467       scrollInfo.fMask = SIF_POS;
4468       if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4469       {
4470         lpptOrigin->y = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
4471       }
4472     }
4473       
4474     bResult = TRUE;
4475   }
4476   
4477   return bResult;
4478 }
4479
4480 /***
4481  * DESCRIPTION:
4482  * Retrieves the number of items that are marked as selected.
4483  * 
4484  * PARAMETER(S):
4485  * [I] HWND : window handle
4486  * 
4487  * RETURN:
4488  * Number of items selected.
4489  */
4490 static LRESULT LISTVIEW_GetSelectedCount(HWND hwnd)
4491 {
4492   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4493   INT nSelectedCount = 0;
4494   INT i;
4495
4496   for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
4497   {
4498     if (ListView_GetItemState(hwnd, i, LVIS_SELECTED) & LVIS_SELECTED)
4499     {
4500       nSelectedCount++;
4501     }
4502   }
4503   
4504   return nSelectedCount;
4505 }
4506
4507 /***
4508  * DESCRIPTION:
4509  * Retrieves item index that marks the start of a multiple selection.
4510  * 
4511  * PARAMETER(S):
4512  * [I] HWND : window handle
4513  * 
4514  * RETURN:
4515  * Index number or -1 if there is no selection mark.
4516  */
4517 static LRESULT LISTVIEW_GetSelectionMark(HWND hwnd)
4518 {
4519   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4520
4521   return infoPtr->nSelectionMark;
4522 }
4523
4524 /***
4525  * DESCRIPTION:
4526  * Retrieves the width of a string.
4527  * 
4528  * PARAMETER(S):
4529  * [I] HWND : window handle
4530  * 
4531  * RETURN:
4532  *   SUCCESS : string width (in pixels)
4533  *   FAILURE : zero
4534  */
4535 static LRESULT LISTVIEW_GetStringWidthA(HWND hwnd, LPCSTR lpszText)
4536 {
4537   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4538   HFONT hFont, hOldFont;
4539   SIZE stringSize;
4540   HDC hdc;
4541
4542   ZeroMemory(&stringSize, sizeof(SIZE));
4543   if (lpszText != NULL)
4544   {
4545     hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
4546     hdc = GetDC(hwnd);
4547     hOldFont = SelectObject(hdc, hFont);
4548     GetTextExtentPointA(hdc, lpszText, lstrlenA(lpszText), &stringSize);
4549     SelectObject(hdc, hOldFont);
4550     ReleaseDC(hwnd, hdc);
4551   }
4552
4553   return stringSize.cx;
4554 }
4555
4556 /***
4557  * DESCRIPTION:
4558  * Retrieves the text backgound color.
4559  * 
4560  * PARAMETER(S):
4561  * [I] HWND : window handle
4562  * 
4563  * RETURN:
4564  * COLORREF associated with the the background.
4565  */
4566 static LRESULT LISTVIEW_GetTextBkColor(HWND hwnd)
4567 {
4568   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
4569
4570   return infoPtr->clrTextBk;
4571 }
4572
4573 /***
4574  * DESCRIPTION:
4575  * Retrieves the text color.
4576  * 
4577  * PARAMETER(S):
4578  * [I] HWND : window handle
4579  * 
4580  * RETURN:
4581  * COLORREF associated with the text.
4582  */
4583 static LRESULT LISTVIEW_GetTextColor(HWND hwnd)
4584 {
4585   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
4586
4587   return infoPtr->clrText;
4588 }
4589
4590 /***
4591  * DESCRIPTION:
4592  * Determines which section of the item was selected (if any).
4593  * 
4594  * PARAMETER(S):
4595  * [I] HWND : window handle
4596  * [IO] LPLVHITTESTINFO : hit test information
4597  *
4598  * RETURN:
4599  *   SUCCESS : item index
4600  *   FAILURE : -1
4601  */
4602 static INT LISTVIEW_HitTestItem(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
4603 {
4604   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4605   RECT rcItem;
4606   INT i;
4607   
4608   TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
4609         lpHitTestInfo->pt.y);
4610
4611   for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
4612   {
4613     rcItem.left = LVIR_BOUNDS;
4614     if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
4615     {
4616       if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
4617       {
4618         rcItem.left = LVIR_ICON;
4619         if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
4620         {
4621           if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
4622           {
4623             lpHitTestInfo->flags = LVHT_ONITEMICON;
4624             lpHitTestInfo->iItem = i;
4625             lpHitTestInfo->iSubItem = 0;
4626             return i;
4627           }
4628         }
4629       
4630         rcItem.left = LVIR_LABEL;
4631         if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
4632         {
4633           if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
4634           {
4635             lpHitTestInfo->flags = LVHT_ONITEMLABEL;
4636             lpHitTestInfo->iItem = i;
4637             lpHitTestInfo->iSubItem = 0;
4638             return i;
4639           }
4640         }
4641         
4642         lpHitTestInfo->flags = LVHT_ONITEMSTATEICON;
4643         lpHitTestInfo->iItem = i;
4644         lpHitTestInfo->iSubItem = 0;
4645         return i;
4646       }
4647     }
4648   }
4649      
4650   lpHitTestInfo->flags = LVHT_NOWHERE;
4651
4652   return -1;
4653 }
4654
4655 /***
4656  * DESCRIPTION:
4657  * Determines which listview item is located at the specified position.
4658  * 
4659  * PARAMETER(S):
4660  * [I] HWND : window handle
4661  * [IO} LPLVHITTESTINFO : hit test information
4662  *
4663  * RETURN:
4664  *   SUCCESS : item index
4665  *   FAILURE : -1
4666  */
4667 static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
4668 {
4669   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4670   INT nItem = -1;
4671
4672   lpHitTestInfo->flags = 0;
4673
4674   if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
4675   {
4676     lpHitTestInfo->flags = LVHT_TOLEFT;
4677   }
4678   else if (infoPtr->rcList.right < lpHitTestInfo->pt.x) 
4679   {
4680     lpHitTestInfo->flags = LVHT_TORIGHT;
4681   }
4682   if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
4683   {
4684     lpHitTestInfo->flags |= LVHT_ABOVE;
4685   }
4686   else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y) 
4687   {
4688     lpHitTestInfo->flags |= LVHT_BELOW;
4689   }
4690
4691   if (lpHitTestInfo->flags == 0)
4692   {
4693     nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo);
4694   }
4695   
4696   return nItem;
4697 }
4698
4699 /***
4700  * DESCRIPTION:
4701  * Inserts a new column.
4702  * 
4703  * PARAMETER(S):
4704  * [I] HWND : window handle
4705  * [I] INT : column index
4706  * [I] LPLVCOLUMNA : column information
4707  *
4708  * RETURN:
4709  *   SUCCESS : new column index
4710  *   FAILURE : -1
4711  */
4712 static LRESULT LISTVIEW_InsertColumnA(HWND hwnd, INT nColumn, 
4713                                       LPLVCOLUMNA lpColumn)
4714 {
4715   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4716   HDITEMA hdi;
4717   INT nNewColumn = -1;
4718
4719   TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn, 
4720         lpColumn);
4721
4722   if (lpColumn != NULL) 
4723   {
4724     /* initialize memory */
4725     ZeroMemory(&hdi, sizeof(HDITEMA));
4726
4727     if (lpColumn->mask & LVCF_FMT) 
4728     {
4729       /* format member is valid */
4730       hdi.mask |= HDI_FORMAT;
4731
4732       /* set text alignment (leftmost column must be left-aligned) */
4733       if (nColumn == 0)
4734       {
4735         hdi.fmt |= HDF_LEFT;
4736       }
4737       else
4738       {
4739         if (lpColumn->fmt & LVCFMT_LEFT)
4740         {
4741           hdi.fmt |= HDF_LEFT;
4742         }
4743         else if (lpColumn->fmt & LVCFMT_RIGHT)
4744         {
4745           hdi.fmt |= HDF_RIGHT;
4746         }
4747         else if (lpColumn->fmt & LVCFMT_CENTER)
4748         {
4749           hdi.fmt |= HDF_CENTER;
4750         }
4751       }
4752  
4753       if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
4754       {
4755         hdi.fmt |= HDF_BITMAP_ON_RIGHT;
4756         /* ??? */
4757       }
4758
4759       if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
4760       {
4761         /* ??? */
4762       }
4763       
4764       if (lpColumn->fmt & LVCFMT_IMAGE)
4765       {
4766         hdi.fmt |= HDF_IMAGE;
4767         hdi.iImage = I_IMAGECALLBACK;
4768       }
4769     }
4770
4771     if (lpColumn->mask & LVCF_WIDTH) 
4772     {
4773       hdi.mask |= HDI_WIDTH;
4774       hdi.cxy = lpColumn->cx;
4775     }
4776   
4777     if (lpColumn->mask & LVCF_TEXT) 
4778     {
4779       hdi.mask |= HDI_TEXT | HDI_FORMAT;
4780       hdi.pszText = lpColumn->pszText;
4781       hdi.cchTextMax = lstrlenA(lpColumn->pszText);
4782       hdi.fmt |= HDF_STRING;
4783     }
4784   
4785     if (lpColumn->mask & LVCF_IMAGE) 
4786     {
4787       hdi.mask |= HDI_IMAGE;
4788       hdi.iImage = lpColumn->iImage;
4789     }
4790
4791     if (lpColumn->mask & LVCF_ORDER) 
4792     {
4793       hdi.mask |= HDI_ORDER;
4794       hdi.iOrder = lpColumn->iOrder;
4795     }
4796
4797     /* insert item in header control */
4798     nNewColumn = SendMessageA(infoPtr->hwndHeader, HDM_INSERTITEMA,
4799                              (WPARAM)nColumn, (LPARAM)&hdi);
4800     
4801     /* Need to reset the item width when inserting a new column */
4802     infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
4803
4804     LISTVIEW_UpdateScroll(hwnd);
4805     InvalidateRect(hwnd, NULL, FALSE);
4806   }
4807
4808   return nNewColumn;
4809 }
4810
4811 static LRESULT LISTVIEW_InsertColumnW(HWND hwnd, INT nColumn, 
4812                                       LPLVCOLUMNW lpColumn)
4813 {
4814   LVCOLUMNA     lvca;
4815   LRESULT               lres;
4816       
4817   memcpy(&lvca,lpColumn,sizeof(lvca));
4818   if (lpColumn->mask & LVCF_TEXT)
4819     lvca.pszText = HEAP_strdupWtoA(GetProcessHeap(),0,lpColumn->pszText);
4820   lres = LISTVIEW_InsertColumnA(hwnd,nColumn,&lvca);
4821   if (lpColumn->mask & LVCF_TEXT)
4822     HeapFree(GetProcessHeap(),0,lvca.pszText);
4823   return lres;
4824 }
4825
4826
4827 /***
4828  * DESCRIPTION:
4829  * Inserts a new item in the listview control.
4830  * 
4831  * PARAMETER(S):
4832  * [I] HWND : window handle
4833  * [I] LPLVITEMA : item information
4834  *
4835  * RETURN:
4836  *   SUCCESS : new item index
4837  *   FAILURE : -1
4838  */
4839 static LRESULT LISTVIEW_InsertItemA(HWND hwnd, LPLVITEMA lpLVItem)
4840 {
4841   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4842   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
4843   UINT uView = lStyle & LVS_TYPEMASK;
4844   LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
4845   NMLISTVIEW nmlv;
4846   INT nItem = -1;
4847   HDPA hdpaSubItems;
4848   INT nItemWidth = 0;
4849   LISTVIEW_ITEM *lpItem = NULL;
4850
4851   TRACE("(hwnd=%x,lpLVItem=%p)\n", hwnd, lpLVItem);
4852
4853   if (lpLVItem != NULL)
4854   {
4855     /* make sure it's not a subitem; cannot insert a subitem */
4856     if (lpLVItem->iSubItem == 0)
4857     {
4858       lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM));
4859       if (lpItem != NULL)
4860       {
4861         ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
4862         if (LISTVIEW_InitItem(hwnd, lpItem, lpLVItem) != FALSE)
4863         {
4864           /* insert item in listview control data structure */
4865           hdpaSubItems = DPA_Create(8);
4866           if (hdpaSubItems != NULL)
4867           {
4868             nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
4869             if (nItem != -1)
4870             {
4871               nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem, 
4872                                     hdpaSubItems);
4873               if (nItem != -1)
4874               {
4875                 /* manage item focus */
4876                 if (lpLVItem->mask & LVIF_STATE)
4877                 {
4878                   if (lpLVItem->stateMask & LVIS_FOCUSED)
4879                   {
4880                     LISTVIEW_SetItemFocus(hwnd, nItem);
4881                   }           
4882                 }
4883                 
4884                 /* send LVN_INSERTITEM notification */
4885                 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4886                 nmlv.hdr.hwndFrom = hwnd;
4887                 nmlv.hdr.idFrom = lCtrlId;
4888                 nmlv.hdr.code = LVN_INSERTITEM;
4889                 nmlv.iItem = nItem;
4890                 nmlv.lParam = lpItem->lParam;;
4891                 ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
4892                 
4893                 if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
4894                 {
4895                   nItemWidth = LISTVIEW_CalculateWidth(hwnd, lpLVItem->iItem); 
4896                   if (nItemWidth > infoPtr->nItemWidth)
4897                   {
4898                     infoPtr->nItemWidth = nItemWidth;
4899                   }
4900                 }
4901
4902                 /* align items (set position of each item) */
4903                 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4904                 {
4905                   if (lStyle & LVS_ALIGNLEFT)
4906                   {
4907                     LISTVIEW_AlignLeft(hwnd);
4908                   }
4909                   else
4910                   {
4911                     LISTVIEW_AlignTop(hwnd);
4912                   }
4913                 }
4914                 
4915                 LISTVIEW_UpdateScroll(hwnd);
4916                 /* refresh client area */
4917                 InvalidateRect(hwnd, NULL, FALSE);
4918               }
4919             }
4920           }
4921         }
4922       }
4923     }
4924   }
4925
4926   /* free memory if unsuccessful */
4927   if ((nItem == -1) && (lpItem != NULL))
4928   {
4929     COMCTL32_Free(lpItem);
4930   }
4931   
4932   return nItem;
4933 }
4934
4935 static LRESULT LISTVIEW_InsertItemW(HWND hwnd, LPLVITEMW lpLVItem) {
4936   LVITEMA lvia;
4937   LRESULT lres;
4938
4939   memcpy(&lvia,lpLVItem,sizeof(LVITEMA));
4940   if (lvia.mask & LVIF_TEXT) {
4941     if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
4942       lvia.pszText = LPSTR_TEXTCALLBACKA;
4943     else
4944       lvia.pszText = HEAP_strdupWtoA(GetProcessHeap(),0,lpLVItem->pszText);
4945   }
4946   lres = LISTVIEW_InsertItemA(hwnd, &lvia);
4947   if (lvia.mask & LVIF_TEXT) {
4948     if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW)
4949       HeapFree(GetProcessHeap(),0,lvia.pszText);
4950   }
4951   return lres;
4952 }
4953
4954 /* LISTVIEW_InsertItemW */
4955
4956 /***
4957  * DESCRIPTION:
4958  * Redraws a range of items.
4959  * 
4960  * PARAMETER(S):
4961  * [I] HWND : window handle
4962  * [I] INT : first item
4963  * [I] INT : last item
4964  *
4965  * RETURN:
4966  *   SUCCESS : TRUE
4967  *   FAILURE : FALSE
4968  */
4969 static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast)
4970 {
4971   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); 
4972   BOOL bResult = FALSE;
4973   RECT rc;
4974
4975   if (nFirst <= nLast)
4976   {
4977     if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
4978     {
4979       if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
4980       {
4981         /* bResult = */
4982         InvalidateRect(hwnd, &rc, FALSE);
4983       }
4984     }
4985   }
4986
4987   return bResult;
4988 }
4989
4990 /* LISTVIEW_Scroll */
4991
4992 /***
4993  * DESCRIPTION:
4994  * Sets the background color.
4995  * 
4996  * PARAMETER(S):
4997  * [I] HWND : window handle
4998  * [I] COLORREF : background color
4999  *
5000  * RETURN:
5001  *   SUCCESS : TRUE
5002  *   FAILURE : FALSE
5003  */
5004 static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk)
5005 {
5006   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5007
5008   infoPtr->clrBk = clrBk;
5009   InvalidateRect(hwnd, NULL, TRUE);
5010   
5011   return TRUE;
5012 }
5013
5014 /* LISTVIEW_SetBkImage */
5015
5016 /***
5017  * DESCRIPTION:
5018  * Sets the callback mask. This mask will be used when the parent
5019  * window stores state information (some or all).
5020  * 
5021  * PARAMETER(S):
5022  * [I] HWND : window handle
5023  * [I] UINT : state mask
5024  *
5025  * RETURN:
5026  *   SUCCESS : TRUE
5027  *   FAILURE : FALSE
5028  */
5029 static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask)
5030 {
5031   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5032
5033   infoPtr->uCallbackMask = uMask;
5034
5035   return TRUE;
5036 }
5037
5038 /***
5039  * DESCRIPTION:
5040  * Sets the attributes of a header item.
5041  * 
5042  * PARAMETER(S):
5043  * [I] HWND : window handle
5044  * [I] INT : column index
5045  * [I] LPLVCOLUMNA : column attributes
5046  *
5047  * RETURN:
5048  *   SUCCESS : TRUE
5049  *   FAILURE : FALSE
5050  */
5051 static LRESULT LISTVIEW_SetColumnA(HWND hwnd, INT nColumn, 
5052                                    LPLVCOLUMNA lpColumn)
5053 {
5054   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5055   BOOL bResult = FALSE;
5056   HDITEMA hdi, hdiget;
5057
5058   if ((lpColumn != NULL) && (nColumn >= 0) && 
5059       (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
5060   {
5061     /* initialize memory */
5062     ZeroMemory(&hdi, sizeof(HDITEMA));
5063
5064     if (lpColumn->mask & LVCF_FMT) 
5065     {
5066       /* format member is valid */
5067       hdi.mask |= HDI_FORMAT;
5068
5069       /* get current format first */
5070       hdiget.mask = HDI_FORMAT;
5071       if (Header_GetItemA(infoPtr->hwndHeader, nColumn, &hdiget))
5072               /* preserve HDF_STRING if present */
5073               hdi.fmt = hdiget.fmt & HDF_STRING;
5074
5075       /* set text alignment (leftmost column must be left-aligned) */
5076       if (nColumn == 0)
5077       {
5078         hdi.fmt |= HDF_LEFT;
5079       }
5080       else
5081       {
5082         if (lpColumn->fmt & LVCFMT_LEFT)
5083         {
5084           hdi.fmt |= HDF_LEFT;
5085         }
5086         else if (lpColumn->fmt & LVCFMT_RIGHT)
5087         {
5088           hdi.fmt |= HDF_RIGHT;
5089         }
5090         else if (lpColumn->fmt & LVCFMT_CENTER)
5091         {
5092           hdi.fmt |= HDF_CENTER;
5093         }
5094       }
5095       
5096       if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
5097       {
5098         hdi.fmt |= HDF_BITMAP_ON_RIGHT;
5099       }
5100
5101       if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
5102       {
5103         hdi.fmt |= HDF_IMAGE;
5104       }
5105       
5106       if (lpColumn->fmt & LVCFMT_IMAGE)
5107       {
5108         hdi.fmt |= HDF_IMAGE;
5109         hdi.iImage = I_IMAGECALLBACK;
5110       }
5111     }
5112
5113     if (lpColumn->mask & LVCF_WIDTH) 
5114     {
5115       hdi.mask |= HDI_WIDTH;
5116       hdi.cxy = lpColumn->cx;
5117     }
5118     
5119     if (lpColumn->mask & LVCF_TEXT) 
5120     {
5121       hdi.mask |= HDI_TEXT | HDI_FORMAT;
5122       hdi.pszText = lpColumn->pszText;
5123       hdi.cchTextMax = lstrlenA(lpColumn->pszText);
5124       hdi.fmt |= HDF_STRING;
5125     }
5126   
5127     if (lpColumn->mask & LVCF_IMAGE) 
5128     {
5129       hdi.mask |= HDI_IMAGE;
5130       hdi.iImage = lpColumn->iImage;
5131     }
5132
5133     if (lpColumn->mask & LVCF_ORDER) 
5134     {
5135       hdi.mask |= HDI_ORDER;
5136       hdi.iOrder = lpColumn->iOrder;
5137     }
5138
5139     /* set header item attributes */
5140     bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
5141   }
5142   
5143   return bResult;
5144 }
5145
5146 /* LISTVIEW_SetColumnW */
5147 /* LISTVIEW_SetColumnOrderArray */
5148
5149 /***
5150  * DESCRIPTION:
5151  * Sets the width of a column
5152  *
5153  * PARAMETERS:
5154  * [I] HWND : window handle
5155  * [I] INT : column index
5156  * [I] INT : column width
5157  *
5158  * RETURN:
5159  *   SUCCESS : TRUE
5160  *   FAILURE : FALSE
5161  */
5162 static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx)
5163 {
5164     LISTVIEW_INFO *infoPtr;
5165     HDITEMA hdi;
5166     LRESULT lret;
5167     LONG lStyle;
5168
5169     /* set column width only if in report mode */
5170     lStyle = GetWindowLongA(hwnd, GWL_STYLE);
5171     if ((lStyle & LVS_TYPEMASK) != LVS_REPORT)
5172         return (FALSE);
5173
5174     /* make sure we can get the listview info */
5175     if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
5176         return (FALSE);  
5177     if (!infoPtr->hwndHeader) /* make sure we have a header */
5178         return (FALSE); 
5179         
5180     /* FIXME: currently ignoring LVSCW_AUTOSIZE (-1) and
5181           * LVSCV_AUTOSIZE_USEHEADER (-2)
5182           */
5183     if (cx < 0) 
5184         return (FALSE);
5185         
5186     hdi.mask = HDI_WIDTH;
5187     hdi.cxy = cx;
5188
5189     /* call header to update the column change */
5190     lret = Header_SetItemA(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
5191
5192     infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
5193
5194     InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
5195
5196     return lret;
5197 }
5198
5199 /***
5200  * DESCRIPTION:
5201  * Sets the extended listview style.
5202  *
5203  * PARAMETERS:
5204  * [I] HWND  : window handle
5205  * [I] DWORD : mask
5206  * [I] DWORD : style
5207  *
5208  * RETURN:
5209  *   SUCCESS : previous style
5210  *   FAILURE : 0
5211  */
5212 static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle)
5213 {
5214     LISTVIEW_INFO *infoPtr;
5215     DWORD dwOldStyle;
5216
5217     /* make sure we can get the listview info */
5218     if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
5219         return (0);
5220
5221     /* store previous style */
5222     dwOldStyle = infoPtr->dwExStyle;
5223
5224     /* set new style */
5225     infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
5226
5227     return (dwOldStyle);
5228 }
5229
5230 /* LISTVIEW_SetHotCursor */
5231
5232 /***
5233  * DESCRIPTION:
5234  * Sets the hot item index.
5235  *
5236  * PARAMETERS:
5237  * [I] HWND  : window handle
5238  * [I] INT   : index
5239  *
5240  * RETURN:
5241  *   SUCCESS : previous hot item index
5242  *   FAILURE : -1 (no hot item)
5243  */
5244 static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex)
5245 {
5246     LISTVIEW_INFO *infoPtr;
5247     INT iOldIndex;
5248
5249     /* make sure we can get the listview info */
5250     if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
5251         return (-1);
5252
5253     /* store previous index */
5254     iOldIndex = infoPtr->nHotItem;
5255
5256     /* set new style */
5257     infoPtr->nHotItem = iIndex;
5258
5259     return (iOldIndex);
5260 }
5261
5262 /* LISTVIEW_SetIconSpacing */
5263
5264 /***
5265  * DESCRIPTION:
5266  * Sets image lists.
5267  * 
5268  * PARAMETER(S):
5269  * [I] HWND : window handle
5270  * [I] INT : image list type  
5271  * [I] HIMAGELIST : image list handle
5272  *
5273  * RETURN:
5274  *   SUCCESS : old image list
5275  *   FAILURE : NULL
5276  */
5277 static LRESULT LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
5278 {
5279   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5280   HIMAGELIST himlTemp = 0;
5281
5282   switch (nType) 
5283   {
5284   case LVSIL_NORMAL:
5285     himlTemp = infoPtr->himlNormal;
5286     infoPtr->himlNormal = himl;
5287     return (LRESULT)himlTemp;
5288
5289   case LVSIL_SMALL:
5290     himlTemp = infoPtr->himlSmall;
5291     infoPtr->himlSmall = himl;
5292     return (LRESULT)himlTemp;
5293
5294   case LVSIL_STATE:
5295     himlTemp = infoPtr->himlState;
5296     infoPtr->himlState = himl;
5297     ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
5298     return (LRESULT)himlTemp;
5299   }
5300
5301   return (LRESULT)NULL;
5302 }
5303
5304
5305 /***
5306  * DESCRIPTION:
5307  * Sets the attributes of an item.
5308  * 
5309  * PARAMETER(S):
5310  * [I] HWND : window handle
5311  * [I] LPLVITEM : item information 
5312  *
5313  * RETURN:
5314  *   SUCCESS : TRUE
5315  *   FAILURE : FALSE
5316  */
5317 static LRESULT LISTVIEW_SetItemA(HWND hwnd, LPLVITEMA lpLVItem)
5318 {
5319   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5320   BOOL bResult = FALSE;
5321
5322   if (lpLVItem != NULL)
5323   {
5324     if ((lpLVItem->iItem >= 0) && (lpLVItem->iItem < GETITEMCOUNT(infoPtr)))
5325     {
5326       if (lpLVItem->iSubItem == 0)
5327       {
5328         bResult = LISTVIEW_SetItem(hwnd, lpLVItem);
5329       }
5330       else
5331       {
5332         bResult = LISTVIEW_SetSubItem(hwnd, lpLVItem);
5333       }
5334     }
5335   }
5336
5337
5338   return bResult;
5339 }
5340
5341 /* LISTVIEW_SetItemW  */
5342
5343 /***
5344  * DESCRIPTION:
5345  * Preallocates memory.
5346  * 
5347  * PARAMETER(S):
5348  * [I] HWND : window handle
5349  * [I] INT : item count (prjected number of items)
5350  * [I] DWORD : update flags
5351  *
5352  * RETURN:
5353  *   SUCCESS : TRUE
5354  *   FAILURE : FALSE
5355  */
5356 static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags)
5357 {
5358   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
5359
5360   FIXME("(%d %08lx)empty stub!\n", nItems, dwFlags);
5361
5362   if (nItems == 0)
5363     return LISTVIEW_DeleteAllItems (hwnd);
5364
5365   if (nItems > GETITEMCOUNT(infoPtr))
5366 {
5367     /* append items */
5368     FIXME("append items\n");
5369
5370   }
5371   else if (nItems < GETITEMCOUNT(infoPtr))
5372   {
5373     /* remove items */
5374     FIXME("remove items\n");
5375
5376   }
5377
5378   return TRUE;
5379 }
5380
5381 /***
5382  * DESCRIPTION:
5383  * Sets the position of an item.
5384  * 
5385  * PARAMETER(S):
5386  * [I] HWND : window handle
5387  * [I] INT : item index
5388  * [I] INT : x coordinate
5389  * [I] INT : y coordinate
5390  *
5391  * RETURN:
5392  *   SUCCESS : TRUE
5393  *   FAILURE : FALSE
5394  */
5395 static BOOL LISTVIEW_SetItemPosition(HWND hwnd, INT nItem, 
5396                                      INT nPosX, INT nPosY)
5397 {
5398   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
5399   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5400   LISTVIEW_ITEM *lpItem;
5401   HDPA hdpaSubItems;
5402   BOOL bResult = FALSE;
5403
5404   TRACE("(hwnd=%x,nItem=%d,X=%d,Y=%d)\n", hwnd, nItem, nPosX, nPosY);
5405
5406   if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
5407   {
5408     if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
5409     {
5410       hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
5411       if (hdpaSubItems != NULL)
5412       {
5413         lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
5414         if (lpItem != NULL)
5415         {
5416           bResult = TRUE;
5417           lpItem->ptPosition.x = nPosX;
5418           lpItem->ptPosition.y = nPosY;
5419         }
5420       }
5421     }
5422   }
5423
5424   return bResult;
5425 }
5426
5427 /* LISTVIEW_SetItemPosition32 */
5428
5429 /***
5430  * DESCRIPTION:
5431  * Sets the state of one or many items.
5432  * 
5433  * PARAMETER(S):
5434  * [I] HWND : window handle
5435  * [I]INT : item index
5436  * [I] LPLVITEM : item or subitem info
5437  *
5438  * RETURN:
5439  *   SUCCESS : TRUE
5440  *   FAILURE : FALSE
5441  */
5442 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
5443 {
5444   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5445   BOOL bResult = FALSE;
5446   LVITEMA lvItem;
5447   INT i;
5448
5449   if (nItem == -1)
5450   {
5451     bResult = TRUE;
5452     ZeroMemory(&lvItem, sizeof(LVITEMA));
5453     lvItem.mask = LVIF_STATE;
5454     lvItem.state = lpLVItem->state;
5455     lvItem.stateMask = lpLVItem->stateMask ;
5456     
5457     /* apply to all items */
5458     for (i = 0; i< GETITEMCOUNT(infoPtr); i++)
5459     {
5460       lvItem.iItem = i;
5461       if (ListView_SetItemA(hwnd, &lvItem) == FALSE)
5462       {
5463         bResult = FALSE;
5464       }
5465     }
5466   }
5467   else
5468   {
5469     ZeroMemory(&lvItem, sizeof(LVITEMA));
5470     lvItem.mask = LVIF_STATE;
5471     lvItem.state = lpLVItem->state;
5472     lvItem.stateMask = lpLVItem->stateMask;
5473     lvItem.iItem = nItem;
5474     bResult = ListView_SetItemA(hwnd, &lvItem);
5475   }
5476
5477   return bResult;
5478 }
5479
5480 /***
5481  * DESCRIPTION:
5482  * Sets the text of an item or subitem.
5483  * 
5484  * PARAMETER(S):
5485  * [I] HWND : window handle
5486  * [I] INT : item index
5487  * [I] LPLVITEMA : item or subitem info
5488  *
5489  * RETURN:
5490  *   SUCCESS : TRUE
5491  *   FAILURE : FALSE
5492  */
5493 static BOOL LISTVIEW_SetItemTextA(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
5494 {
5495   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5496   BOOL bResult = FALSE;
5497   LVITEMA lvItem;
5498
5499   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
5500   {
5501     ZeroMemory(&lvItem, sizeof(LVITEMA));
5502     lvItem.mask = LVIF_TEXT;
5503     lvItem.pszText = lpLVItem->pszText;
5504     lvItem.iItem = nItem;
5505     lvItem.iSubItem = lpLVItem->iSubItem;
5506     bResult = ListView_SetItemA(hwnd, &lvItem);
5507   }
5508   
5509   return bResult;
5510 }
5511
5512 /* LISTVIEW_SetItemTextW */
5513
5514 /***
5515  * DESCRIPTION:
5516  * Set item index that marks the start of a multiple selection.
5517  *
5518  * PARAMETER(S):
5519  * [I] HWND : window handle
5520  * [I] INT  : index
5521  *
5522  * RETURN:
5523  * Index number or -1 if there is no selection mark.
5524  */
5525 static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex)
5526 {
5527   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5528   INT nOldIndex = infoPtr->nSelectionMark;
5529
5530   infoPtr->nSelectionMark = nIndex;
5531
5532   return nOldIndex;
5533 }
5534
5535 /***
5536  * DESCRIPTION:
5537  * Sets the text background color.
5538  * 
5539  * PARAMETER(S):
5540  * [I] HWND : window handle
5541  * [I] COLORREF : text background color
5542  *
5543  * RETURN:
5544  *   SUCCESS : TRUE
5545  *   FAILURE : FALSE
5546  */
5547 static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk)
5548 {
5549   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5550
5551   infoPtr->clrTextBk = clrTextBk;
5552   InvalidateRect(hwnd, NULL, TRUE);
5553
5554   return TRUE;
5555 }
5556
5557 /***
5558  * DESCRIPTION:
5559  * Sets the text foreground color.
5560  * 
5561  * PARAMETER(S):
5562  * [I] HWND : window handle
5563  * [I] COLORREF : text color 
5564  *
5565  * RETURN:
5566  *   SUCCESS : TRUE
5567  *   FAILURE : FALSE
5568  */
5569 static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText)
5570 {
5571   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5572
5573   infoPtr->clrText = clrText;
5574   InvalidateRect(hwnd, NULL, TRUE);
5575
5576   return TRUE;
5577 }
5578
5579 /* LISTVIEW_SetToolTips */
5580 /* LISTVIEW_SetUnicodeFormat */
5581 /* LISTVIEW_SetWorkAreas */
5582
5583 /***
5584  * DESCRIPTION:
5585  * Callback internally used by LISTVIEW_SortItems()
5586  * 
5587  * PARAMETER(S):
5588  * [I] LPVOID : first LISTVIEW_ITEM to compare
5589  * [I] LPVOID : second LISTVIEW_ITEM to compare
5590  * [I] LPARAM : HWND of control
5591  *
5592  * RETURN:
5593  *   if first comes before second : negative
5594  *   if first comes after second : positive
5595  *   if first and second are equivalent : zero
5596  */
5597 static INT WINAPI LISTVIEW_CallBackCompare( 
5598   LPVOID first, 
5599   LPVOID second, 
5600   LPARAM lParam)
5601 {
5602   /* Forward the call to the client defined callback */
5603   HWND hwnd = (HWND)lParam;
5604   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5605
5606   return (infoPtr->pfnCompare)(
5607     ((LISTVIEW_ITEM *)first)->lParam,
5608     ((LISTVIEW_ITEM *)second)->lParam,
5609     infoPtr->lParamSort);
5610 }
5611
5612 /***
5613  * DESCRIPTION:
5614  * Sorts the listview items.
5615  * 
5616  * PARAMETER(S):
5617  * [I] HWND : window handle
5618  * [I] WPARAM : application-defined value
5619  * [I] LPARAM : pointer to comparision callback
5620  *
5621  * RETURN:
5622  *   SUCCESS : TRUE
5623  *   FAILURE : FALSE
5624  */
5625 static LRESULT LISTVIEW_SortItems(HWND hwnd, WPARAM wParam, LPARAM lParam)
5626 {
5627     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5628     HDPA hdpaSubItems;
5629     LISTVIEW_ITEM *lpItem;
5630     int nCount, i;
5631     HDPA sortList;
5632     
5633     if (!infoPtr || !infoPtr->hdpaItems)
5634         return FALSE;
5635     
5636     nCount = GETITEMCOUNT(infoPtr);
5637     /* if there are 0 or 1 items, there is no need to sort */
5638     if (nCount > 1)
5639     {
5640         sortList = DPA_Create(nCount);
5641
5642         infoPtr->pfnCompare = (PFNLVCOMPARE)lParam;
5643         infoPtr->lParamSort = (LPARAM)wParam;
5644         
5645         /* append pointers one by one to sortList */
5646         for (i = 0; i < nCount; i++)
5647         {
5648             if ((hdpaSubItems = (HDPA) DPA_GetPtr(infoPtr->hdpaItems, i)))
5649                 if ((lpItem = (LISTVIEW_ITEM *) DPA_GetPtr(hdpaSubItems, 0)))
5650                     DPA_InsertPtr(sortList, nCount + 1, lpItem);
5651         }
5652
5653         /* sort the sortList */
5654         DPA_Sort(sortList, LISTVIEW_CallBackCompare, hwnd);
5655
5656         /* copy the pointers back */
5657         for (i = 0; i < nCount; i++)
5658         {
5659             if ((hdpaSubItems = (HDPA) DPA_GetPtr(infoPtr->hdpaItems, i)) &&
5660                 (lpItem = (LISTVIEW_ITEM *) DPA_GetPtr(sortList, i)))
5661                 DPA_SetPtr(hdpaSubItems, 0, lpItem);
5662         }
5663
5664         DPA_Destroy(sortList);
5665     }
5666
5667     return TRUE;
5668 }
5669
5670 /* LISTVIEW_SubItemHitTest */
5671
5672 /***
5673  * DESCRIPTION:
5674  * Updates an items or rearranges the listview control.
5675  * 
5676  * PARAMETER(S):
5677  * [I] HWND : window handle
5678  * [I] INT : item index
5679  *
5680  * RETURN:
5681  *   SUCCESS : TRUE
5682  *   FAILURE : FALSE
5683  */
5684 static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
5685 {
5686   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5687   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
5688   BOOL bResult = FALSE;
5689   RECT rc;
5690
5691   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
5692   {
5693     bResult = TRUE;
5694
5695     /* rearrange with default alignment style */
5696     if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
5697         ((lStyle & LVS_TYPEMASK)  == LVS_SMALLICON)))
5698     {
5699       ListView_Arrange(hwnd, 0);
5700     }
5701     else
5702     {
5703       /* get item bounding rectangle */
5704       rc.left = LVIR_BOUNDS;
5705       ListView_GetItemRect(hwnd, nItem, &rc);
5706       InvalidateRect(hwnd, &rc, TRUE);
5707     }
5708   }
5709
5710   return bResult;
5711 }
5712
5713 /***
5714  * DESCRIPTION:
5715  * Creates the listview control.
5716  * 
5717  * PARAMETER(S):
5718  * [I] HWND : window handle
5719  *
5720  * RETURN:
5721  * Zero
5722  */
5723 static LRESULT LISTVIEW_Create(HWND hwnd, WPARAM wParam, LPARAM lParam)
5724 {
5725   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5726   LPCREATESTRUCTA lpcs = (LPCREATESTRUCTA)lParam;
5727   UINT uView = lpcs->style & LVS_TYPEMASK;
5728   LOGFONTA logFont;
5729
5730   /* initialize info pointer */
5731   ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
5732
5733   /* determine the type of structures to use */
5734   infoPtr->notifyFormat = SendMessageA(GetParent(hwnd), WM_NOTIFYFORMAT, 
5735                                        (WPARAM)hwnd, (LPARAM)NF_QUERY);
5736   if (infoPtr->notifyFormat != NFR_ANSI)
5737   {
5738     FIXME("ANSI notify format is NOT used\n");
5739   }
5740   
5741   /* initialize color information  */
5742   infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
5743   infoPtr->clrText = GetSysColor(COLOR_WINDOWTEXT);
5744   infoPtr->clrTextBk = GetSysColor(COLOR_WINDOW); 
5745
5746   /* set default values */
5747   infoPtr->uCallbackMask = 0;
5748   infoPtr->nFocusedItem = -1;
5749   infoPtr->nSelectionMark = -1;
5750   infoPtr->nHotItem = -1;
5751   infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
5752   infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
5753   ZeroMemory(&infoPtr->rcList, sizeof(RECT));
5754   infoPtr->hwndEdit = 0;
5755   infoPtr->pedititem = NULL;
5756
5757   /* get default font (icon title) */
5758   SystemParametersInfoA(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
5759   infoPtr->hDefaultFont = CreateFontIndirectA(&logFont);
5760   infoPtr->hFont = infoPtr->hDefaultFont;
5761   
5762   /* create header */
5763   infoPtr->hwndHeader = CreateWindowA(WC_HEADERA, (LPCSTR)NULL, 
5764                                       WS_CHILD | HDS_HORZ | HDS_BUTTONS, 
5765                                       0, 0, 0, 0, hwnd, (HMENU)0, 
5766                                       lpcs->hInstance, NULL);
5767
5768   /* set header font */
5769   SendMessageA(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, 
5770                (LPARAM)TRUE);
5771   
5772   if (uView == LVS_ICON)
5773   {
5774     infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
5775     infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
5776   }
5777   else if (uView == LVS_REPORT)
5778   {
5779     if (!(LVS_NOCOLUMNHEADER & lpcs->style))
5780     {
5781       ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
5782     }
5783
5784     infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
5785     infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
5786   }
5787   else
5788   {
5789     infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
5790     infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
5791   }
5792
5793   /* display unsupported listview window styles */
5794   LISTVIEW_UnsupportedStyles(lpcs->style);
5795
5796   /* allocate memory for the data structure */
5797   infoPtr->hdpaItems = DPA_Create(10);
5798
5799   /* initialize size of items */
5800   infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
5801   infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
5802
5803   return 0;
5804 }
5805
5806 /***
5807  * DESCRIPTION:
5808  * Erases the background of the listview control.
5809  * 
5810  * PARAMETER(S):
5811  * [I] HWND : window handle
5812  * [I] WPARAM : device context handle
5813  * [I] LPARAM : not used
5814  * 
5815  * RETURN:
5816  *   SUCCESS : TRUE
5817  *   FAILURE : FALSE
5818  */
5819 static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam, 
5820                                         LPARAM lParam)
5821 {
5822   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5823   BOOL bResult;
5824
5825   if (infoPtr->clrBk == CLR_NONE) 
5826   {
5827     bResult = SendMessageA(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam);
5828   }
5829   else 
5830   {
5831     RECT rc;
5832     HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
5833     GetClientRect(hwnd, &rc);
5834     FillRect((HDC)wParam, &rc, hBrush);
5835     DeleteObject(hBrush);
5836     bResult = TRUE;
5837   }
5838
5839   return bResult;
5840 }
5841
5842 /***
5843  * DESCRIPTION:
5844  * Retrieves the listview control font.
5845  * 
5846  * PARAMETER(S):
5847  * [I] HWND : window handle
5848  *
5849  * RETURN:
5850  * Font handle.
5851  */
5852 static LRESULT LISTVIEW_GetFont(HWND hwnd)
5853 {
5854   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5855
5856   return infoPtr->hFont;
5857 }
5858
5859 /***
5860  * DESCRIPTION:
5861  * Performs vertical scrolling.
5862  * 
5863  * PARAMETER(S):
5864  * [I] HWND : window handle
5865  * [I] INT : scroll code
5866  * [I] SHORT : current scroll position if scroll code is SB_THIMBPOSITION 
5867  *             or SB_THUMBTRACK.
5868  * [I] HWND : scrollbar control window handle
5869  *
5870  * RETURN:
5871  * Zero
5872  */
5873 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
5874                                 HWND hScrollWnd)
5875 {
5876   SCROLLINFO scrollInfo;
5877
5878   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5879   scrollInfo.cbSize = sizeof(SCROLLINFO);
5880   scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
5881  
5882   if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
5883   {
5884     INT nOldScrollPos = scrollInfo.nPos;
5885     switch (nScrollCode)
5886     {
5887     case SB_LINEUP:
5888       if (scrollInfo.nPos > scrollInfo.nMin)
5889       {
5890         scrollInfo.nPos--;
5891       }
5892     break;
5893     
5894     case SB_LINEDOWN:
5895       if (scrollInfo.nPos < scrollInfo.nMax)
5896       {
5897         scrollInfo.nPos++;
5898       }
5899       break;
5900       
5901     case SB_PAGEUP:
5902       if (scrollInfo.nPos > scrollInfo.nMin)
5903       {
5904
5905         if (scrollInfo.nPos >= scrollInfo.nPage)
5906         {
5907           scrollInfo.nPos -= scrollInfo.nPage;
5908         }
5909         else
5910         {
5911           scrollInfo.nPos = scrollInfo.nMin;
5912         }
5913       }
5914       break;
5915       
5916     case SB_PAGEDOWN:
5917       if (scrollInfo.nPos < scrollInfo.nMax)
5918       {
5919         if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
5920         {
5921           scrollInfo.nPos += scrollInfo.nPage;
5922         }
5923         else
5924         {
5925           scrollInfo.nPos = scrollInfo.nMax;
5926         }
5927       }
5928       break;
5929
5930     case SB_THUMBTRACK:
5931         scrollInfo.nPos = nCurrentPos;
5932       break;
5933     }
5934
5935     if (nOldScrollPos != scrollInfo.nPos)
5936     {
5937       scrollInfo.fMask = SIF_POS;
5938       SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
5939       InvalidateRect(hwnd, NULL, TRUE);
5940     }
5941   }
5942     
5943   return 0;
5944 }
5945
5946 /***
5947  * DESCRIPTION:
5948  * Performs horizontal scrolling.
5949  * 
5950  * PARAMETER(S):
5951  * [I] HWND : window handle
5952  * [I] INT : scroll code
5953  * [I] SHORT : current scroll position if scroll code is SB_THIMBPOSITION 
5954  *             or SB_THUMBTRACK.
5955  * [I] HWND : scrollbar control window handle
5956  *
5957  * RETURN:
5958  * Zero
5959  */
5960 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
5961                                 HWND hScrollWnd)
5962 {
5963   SCROLLINFO scrollInfo;
5964
5965   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5966   scrollInfo.cbSize = sizeof(SCROLLINFO);
5967   scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
5968  
5969   if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
5970   {
5971     INT nOldScrollPos = scrollInfo.nPos;
5972
5973     switch (nScrollCode)
5974     {
5975     case SB_LINELEFT:
5976       if (scrollInfo.nPos > scrollInfo.nMin)
5977       {
5978         scrollInfo.nPos--;
5979       }
5980       break;
5981     
5982     case SB_LINERIGHT:
5983       if (scrollInfo.nPos < scrollInfo.nMax)
5984       {
5985         scrollInfo.nPos++;
5986       }
5987       break;
5988       
5989     case SB_PAGELEFT:
5990       if (scrollInfo.nPos > scrollInfo.nMin)
5991       {
5992         if (scrollInfo.nPos >= scrollInfo.nPage)
5993         {
5994           scrollInfo.nPos -= scrollInfo.nPage;
5995         }
5996         else
5997         {
5998           scrollInfo.nPos = scrollInfo.nMin;
5999         }
6000       }
6001       break;
6002       
6003     case SB_PAGERIGHT:
6004       if (scrollInfo.nPos < scrollInfo.nMax)
6005       {
6006         if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
6007         {
6008           scrollInfo.nPos += scrollInfo.nPage;
6009         }
6010         else
6011         {
6012           scrollInfo.nPos = scrollInfo.nMax;
6013         }
6014       }
6015       break;
6016
6017     case SB_THUMBTRACK:
6018         scrollInfo.nPos = nCurrentPos;
6019       break;
6020     }
6021
6022     if (nOldScrollPos != scrollInfo.nPos)
6023     {
6024       UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
6025       scrollInfo.fMask = SIF_POS;
6026       SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
6027       if(uView == LVS_REPORT)
6028       {
6029           scrollInfo.fMask = SIF_POS;
6030           GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
6031           LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
6032       }
6033       InvalidateRect(hwnd, NULL, TRUE);
6034     }
6035   }
6036     
6037   return 0;
6038 }
6039
6040 /***
6041  * DESCRIPTION:
6042  * ??? 
6043  * 
6044  * PARAMETER(S):
6045  * [I] HWND : window handle
6046  * [I] INT : virtual key 
6047  * [I] LONG : key data
6048  *
6049  * RETURN:
6050  * Zero
6051  */
6052 static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData)
6053 {
6054   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6055   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
6056   HWND hwndParent = GetParent(hwnd);
6057   NMLVKEYDOWN nmKeyDown; 
6058   NMHDR nmh;
6059   INT nItem = -1;
6060   BOOL bRedraw = FALSE;
6061
6062   /* send LVN_KEYDOWN notification */
6063   ZeroMemory(&nmKeyDown, sizeof(NMLVKEYDOWN));
6064   nmKeyDown.hdr.hwndFrom = hwnd;  
6065   nmKeyDown.hdr.idFrom = nCtrlId;  
6066   nmKeyDown.hdr.code = LVN_KEYDOWN;  
6067   nmKeyDown.wVKey = nVirtualKey; 
6068   nmKeyDown.flags = 0; 
6069   SendMessageA(hwndParent, WM_NOTIFY, (WPARAM)nCtrlId, (LPARAM)&nmKeyDown); 
6070   
6071   /* initialize */
6072   nmh.hwndFrom = hwnd;
6073   nmh.idFrom = nCtrlId;
6074
6075   switch (nVirtualKey)
6076   {
6077   case VK_RETURN:
6078     if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
6079     {
6080       /* send NM_RETURN notification */
6081       nmh.code = NM_RETURN;
6082       ListView_Notify(hwndParent, nCtrlId, &nmh);
6083       
6084       /* send LVN_ITEMACTIVATE notification */
6085       nmh.code = LVN_ITEMACTIVATE;
6086       ListView_Notify(hwndParent, nCtrlId, &nmh);
6087     }
6088     break;
6089
6090   case VK_HOME:
6091     if (GETITEMCOUNT(infoPtr) > 0)
6092     {
6093       nItem = 0;
6094     }
6095     break;
6096
6097   case VK_END:
6098     if (GETITEMCOUNT(infoPtr) > 0)
6099     {
6100       nItem = GETITEMCOUNT(infoPtr) - 1;
6101     }
6102     break;
6103
6104   case VK_LEFT:
6105     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT);
6106     break;
6107
6108   case VK_UP:
6109     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE);
6110     break;
6111     
6112   case VK_RIGHT:
6113     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT);
6114     break;
6115
6116   case VK_DOWN:
6117     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW);
6118     break;
6119
6120   case VK_PRIOR:
6121     /* TO DO */
6122     break;
6123
6124   case VK_NEXT:
6125     /* TO DO */
6126     break;
6127   }
6128
6129   if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
6130   {
6131     bRedraw = LISTVIEW_KeySelection(hwnd, nItem);
6132     if (bRedraw != FALSE)
6133     {
6134       /* refresh client area */
6135       InvalidateRect(hwnd, NULL, TRUE);
6136       UpdateWindow(hwnd);
6137     }
6138   }
6139
6140   return 0;
6141 }
6142
6143 /***
6144  * DESCRIPTION:
6145  * Kills the focus.
6146  * 
6147  * PARAMETER(S):
6148  * [I] HWND : window handle
6149  *
6150  * RETURN:
6151  * Zero
6152  */
6153 static LRESULT LISTVIEW_KillFocus(HWND hwnd)
6154 {
6155   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
6156   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
6157   NMHDR nmh;
6158
6159   TRACE("(hwnd=%x)\n", hwnd);
6160
6161   /* send NM_KILLFOCUS notification */
6162   nmh.hwndFrom = hwnd;
6163   nmh.idFrom = nCtrlId;
6164   nmh.code = NM_KILLFOCUS;
6165   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
6166
6167   /* set window focus flag */
6168   infoPtr->bFocus = FALSE;
6169
6170   /* NEED drawing optimization ; redraw the selected items */
6171   InvalidateRect(hwnd, NULL, FALSE);
6172
6173   return 0;
6174 }
6175
6176 /***
6177  * DESCRIPTION:
6178  * Processes double click messages (left mouse button).
6179  * 
6180  * PARAMETER(S):
6181  * [I] HWND : window handle
6182  * [I] WORD : key flag
6183  * [I] WORD : x coordinate
6184  * [I] WORD : y coordinate
6185  *
6186  * RETURN:
6187  * Zero
6188  */
6189 static LRESULT LISTVIEW_LButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX, 
6190                                       WORD wPosY)
6191 {
6192   LONG nCtrlId = GetWindowLongA(hwnd, GWL_ID);
6193   NMHDR nmh;
6194
6195   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
6196
6197   /* send NM_DBLCLK notification */
6198   nmh.hwndFrom = hwnd;
6199   nmh.idFrom = nCtrlId;
6200   nmh.code = NM_DBLCLK;
6201   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
6202
6203   /* send LVN_ITEMACTIVATE notification */
6204   nmh.code = LVN_ITEMACTIVATE;
6205   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
6206
6207   return 0;
6208 }
6209
6210 /***
6211  * DESCRIPTION:
6212  * Processes mouse down messages (left mouse button).
6213  * 
6214  * PARAMETER(S):
6215  * [I] HWND : window handle
6216  * [I] WORD : key flag
6217  * [I] WORD : x coordinate
6218  * [I] WORD : y coordinate
6219  *
6220  * RETURN:
6221  * Zero
6222  */
6223 static LRESULT LISTVIEW_LButtonDown(HWND hwnd, WORD wKey, WORD wPosX, 
6224                                     WORD wPosY)
6225 {
6226   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6227   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6228   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
6229   static BOOL bGroupSelect = TRUE;
6230   POINT ptPosition;
6231   NMHDR nmh;
6232   INT nItem;
6233
6234   TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, 
6235         wPosY);
6236
6237   /* send NM_RELEASEDCAPTURE notification */ 
6238   nmh.hwndFrom = hwnd;
6239   nmh.idFrom = nCtrlId;
6240   nmh.code = NM_RELEASEDCAPTURE;
6241   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
6242  
6243   if (infoPtr->bFocus == FALSE)
6244   {
6245     SetFocus(hwnd);
6246   }
6247
6248   /* set left button down flag */
6249   infoPtr->bLButtonDown = TRUE;
6250   
6251   ptPosition.x = wPosX;
6252   ptPosition.y = wPosY;
6253   nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
6254   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6255   {
6256     if (lStyle & LVS_SINGLESEL)
6257     {
6258       LISTVIEW_SetSelection(hwnd, nItem);
6259     }
6260     else
6261     {
6262       if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
6263       {
6264         if (bGroupSelect != FALSE)
6265         {
6266           LISTVIEW_AddGroupSelection(hwnd, nItem);
6267         }
6268         else
6269         {
6270           LISTVIEW_AddSelection(hwnd, nItem);
6271         }
6272       }
6273       else if (wKey & MK_CONTROL)
6274       {
6275         bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem);
6276       }
6277       else  if (wKey & MK_SHIFT)
6278       {
6279         LISTVIEW_SetGroupSelection(hwnd, nItem);
6280       }
6281       else
6282       {
6283         LISTVIEW_SetSelection(hwnd, nItem);
6284       }
6285     }
6286   }
6287   else
6288   {
6289     /* remove all selections */
6290     LISTVIEW_RemoveSelections(hwnd, 0, GETITEMCOUNT(infoPtr));
6291   }
6292
6293   InvalidateRect(hwnd, NULL, TRUE);
6294
6295   return 0;
6296 }
6297
6298 /***
6299  * DESCRIPTION:
6300  * Processes mouse up messages (left mouse button).
6301  * 
6302  * PARAMETER(S):
6303  * [I] HWND : window handle
6304  * [I] WORD : key flag
6305  * [I] WORD : x coordinate
6306  * [I] WORD : y coordinate
6307  *
6308  * RETURN:
6309  * Zero
6310  */
6311 static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX, 
6312                                   WORD wPosY)
6313 {
6314   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6315
6316   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
6317
6318   if (infoPtr->bLButtonDown != FALSE) 
6319   {
6320     INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
6321     NMHDR nmh;
6322
6323     /* send NM_CLICK notification */
6324     nmh.hwndFrom = hwnd;
6325     nmh.idFrom = nCtrlId;
6326     nmh.code = NM_CLICK;
6327     ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
6328
6329     /* set left button flag */
6330     infoPtr->bLButtonDown = FALSE;
6331   }
6332
6333   return 0;
6334 }
6335
6336 /***
6337  * DESCRIPTION:
6338  * Creates the listview control (called before WM_CREATE).
6339  * 
6340  * PARAMETER(S):
6341  * [I] HWND : window handle
6342  * [I] WPARAM : unhandled 
6343  * [I] LPARAM : widow creation info
6344  * 
6345  * RETURN:
6346  * Zero
6347  */
6348 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
6349 {
6350   LISTVIEW_INFO *infoPtr;
6351
6352   TRACE("(hwnd=%x,wParam=%x,lParam=%lx)\n", hwnd, wParam, lParam);
6353
6354   /* allocate memory for info structure */
6355   infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
6356   SetWindowLongA(hwnd, 0, (LONG)infoPtr);
6357   if (infoPtr == NULL) 
6358   {
6359     ERR("could not allocate info memory!\n");
6360     return 0;
6361   }
6362
6363   if ((LISTVIEW_INFO *)GetWindowLongA(hwnd, 0) != infoPtr) 
6364   {
6365     ERR("pointer assignment error!\n");
6366     return 0;
6367   }
6368
6369   return DefWindowProcA(hwnd, WM_NCCREATE, wParam, lParam);
6370 }
6371
6372 /***
6373  * DESCRIPTION:
6374  * Destroys the listview control (called after WM_DESTROY).
6375  * 
6376  * PARAMETER(S):
6377  * [I] HWND : window handle
6378  * 
6379  * RETURN:
6380  * Zero
6381  */
6382 static LRESULT LISTVIEW_NCDestroy(HWND hwnd)
6383 {
6384   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6385
6386   TRACE("(hwnd=%x)\n", hwnd);
6387
6388   /* delete all items */
6389   LISTVIEW_DeleteAllItems(hwnd);
6390
6391   /* destroy data structure */
6392   DPA_Destroy(infoPtr->hdpaItems);
6393
6394   /* destroy font */
6395   infoPtr->hFont = (HFONT)0;
6396   if (infoPtr->hDefaultFont)
6397   {
6398     DeleteObject(infoPtr->hDefaultFont);
6399   }
6400
6401   /* free listview info pointer*/
6402   COMCTL32_Free(infoPtr);
6403
6404   return 0;
6405 }
6406
6407 /***
6408  * DESCRIPTION:
6409  * Handles notifications from children.
6410  * 
6411  * PARAMETER(S):
6412  * [I] HWND : window handle
6413  * [I] INT : control identifier
6414  * [I] LPNMHDR : notification information
6415  * 
6416  * RETURN:
6417  * Zero
6418  */
6419 static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh)
6420 {
6421   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6422   
6423   if (lpnmh->hwndFrom == infoPtr->hwndHeader) 
6424   {
6425     /* handle notification from header control */
6426     if (lpnmh->code == HDN_ENDTRACKA)
6427     {
6428       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6429       InvalidateRect(hwnd, NULL, TRUE);
6430     }
6431     else if(lpnmh->code ==  HDN_ITEMCLICKA)
6432     {
6433         /* Handle sorting by Header Column */
6434         NMLISTVIEW nmlv;
6435         LPNMHEADERA pnmHeader = (LPNMHEADERA) lpnmh;
6436         LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
6437
6438         ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6439         nmlv.hdr.hwndFrom = hwnd;
6440         nmlv.hdr.idFrom = lCtrlId;
6441         nmlv.hdr.code = LVN_COLUMNCLICK;
6442         nmlv.iItem = -1;
6443         nmlv.iSubItem = pnmHeader->iItem;
6444         
6445         ListView_LVNotify(GetParent(hwnd),lCtrlId, &nmlv);
6446
6447     }
6448     else if(lpnmh->code == NM_RELEASEDCAPTURE)
6449     {
6450       /* Idealy this should be done in HDN_ENDTRACKA
6451        * but since SetItemBounds in Header.c is called after
6452        * the notification is sent, it is neccessary to handle the
6453        * update of the scroll bar here (Header.c works fine as it is,
6454        * no need to disturb it)
6455        */
6456       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6457       LISTVIEW_UpdateScroll(hwnd);
6458       InvalidateRect(hwnd, NULL, TRUE);
6459     }
6460
6461   }
6462
6463   return 0;
6464 }
6465
6466 /***
6467  * DESCRIPTION:
6468  * Determines the type of structure to use.
6469  * 
6470  * PARAMETER(S):
6471  * [I] HWND : window handle of the sender
6472  * [I] HWND : listview window handle 
6473  * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
6474  *
6475  * RETURN:
6476  * Zero
6477  */
6478 static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand)
6479 {
6480   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6481
6482   if (nCommand == NF_REQUERY)
6483   {
6484     /* determine the type of structure to use */
6485     infoPtr->notifyFormat = SendMessageA(hwndFrom, WM_NOTIFYFORMAT, 
6486                                          (WPARAM)hwnd, (LPARAM)NF_QUERY);
6487     if (infoPtr->notifyFormat == NFR_UNICODE)
6488     {
6489       FIXME("NO support for unicode structures");
6490     }
6491   }
6492
6493   return 0;
6494 }
6495
6496 /***
6497  * DESCRIPTION:
6498  * Paints/Repaints the listview control.
6499  * 
6500  * PARAMETER(S):
6501  * [I] HWND : window handle
6502  * [I] HDC : device context handle
6503  *
6504  * RETURN:
6505  * Zero
6506  */
6507 static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc)
6508 {
6509   PAINTSTRUCT ps;
6510
6511    TRACE("(hwnd=%x,hdc=%x)\n", hwnd, hdc);
6512
6513   if (hdc == 0)
6514   {
6515     hdc = BeginPaint(hwnd, &ps);
6516     LISTVIEW_Refresh(hwnd, hdc);
6517     EndPaint(hwnd, &ps);
6518   }
6519   else
6520   {
6521     LISTVIEW_Refresh(hwnd, hdc);
6522   }
6523
6524   return 0;
6525 }
6526
6527 /***
6528  * DESCRIPTION:
6529  * Processes double click messages (right mouse button).
6530  * 
6531  * PARAMETER(S):
6532  * [I] HWND : window handle
6533  * [I] WORD : key flag
6534  * [I] WORD : x coordinate
6535  * [I] WORD : y coordinate
6536  *
6537  * RETURN:
6538  * Zero
6539  */
6540 static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX, 
6541                                       WORD wPosY)
6542 {
6543   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
6544   NMHDR nmh;
6545
6546   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
6547
6548   /* send NM_RELEASEDCAPTURE notification */ 
6549   nmh.hwndFrom = hwnd;
6550   nmh.idFrom = nCtrlId;
6551   nmh.code = NM_RELEASEDCAPTURE;
6552   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
6553
6554   /* send NM_RDBLCLK notification */
6555   nmh.code = NM_RDBLCLK;
6556   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
6557
6558   return 0;
6559 }
6560
6561 /***
6562  * DESCRIPTION:
6563  * Processes mouse down messages (right mouse button).
6564  * 
6565  * PARAMETER(S):
6566  * [I] HWND : window handle
6567  * [I] WORD : key flag
6568  * [I] WORD : x coordinate
6569  * [I] WORD : y coordinate
6570  *
6571  * RETURN:
6572  * Zero
6573  */
6574 static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX, 
6575                                     WORD wPosY)
6576 {
6577   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6578   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
6579   POINT ptPosition;
6580   NMHDR nmh;
6581   INT nItem;
6582
6583   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
6584
6585   /* send NM_RELEASEDCAPTURE notification */
6586   nmh.hwndFrom = hwnd;
6587   nmh.idFrom = nCtrlId;
6588   nmh.code = NM_RELEASEDCAPTURE;
6589   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
6590  
6591   /* make sure the listview control window has the focus */
6592   if (infoPtr->bFocus == FALSE)
6593   {
6594     SetFocus(hwnd);
6595   }
6596
6597   /* set right button down flag */
6598   infoPtr->bRButtonDown = TRUE;
6599
6600   /* determine the index of the selected item */
6601   ptPosition.x = wPosX;
6602   ptPosition.y = wPosY;
6603   nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
6604   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6605   {
6606     if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)))
6607     {
6608       LISTVIEW_SetSelection(hwnd, nItem);
6609     }
6610   }
6611   else
6612   {
6613     LISTVIEW_RemoveSelections(hwnd, 0, GETITEMCOUNT(infoPtr));
6614   }
6615   
6616   return 0;
6617 }
6618
6619 /***
6620  * DESCRIPTION:
6621  * Processes mouse up messages (right mouse button).
6622  * 
6623  * PARAMETER(S):
6624  * [I] HWND : window handle
6625  * [I] WORD : key flag
6626  * [I] WORD : x coordinate
6627  * [I] WORD : y coordinate
6628  *
6629  * RETURN:
6630  * Zero
6631  */
6632 static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX, 
6633                                   WORD wPosY)
6634 {
6635   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6636   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
6637   NMHDR nmh;
6638
6639   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
6640
6641   if (infoPtr->bRButtonDown != FALSE) 
6642   {
6643     /* send NM_RClICK notification */
6644     ZeroMemory(&nmh, sizeof(NMHDR));
6645     nmh.hwndFrom = hwnd;
6646     nmh.idFrom = nCtrlId;
6647     nmh.code = NM_RCLICK;
6648     ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
6649
6650     /* set button flag */
6651     infoPtr->bRButtonDown = FALSE;
6652   }
6653   
6654   return 0;
6655 }
6656
6657 /***
6658  * DESCRIPTION:
6659  * Sets the focus.  
6660  * 
6661  * PARAMETER(S):
6662  * [I] HWND : window handle
6663  * [I] HWND : window handle of previously focused window
6664  *
6665  * RETURN:
6666  * Zero
6667  */
6668 static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus)
6669 {
6670   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6671   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
6672   NMHDR nmh;
6673
6674   TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
6675
6676   /* send NM_SETFOCUS notification */
6677   nmh.hwndFrom = hwnd;
6678   nmh.idFrom = nCtrlId;
6679   nmh.code = NM_SETFOCUS;
6680   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
6681
6682   /* set window focus flag */
6683   infoPtr->bFocus = TRUE;
6684
6685   InvalidateRect(hwnd, NULL, TRUE);
6686   UpdateWindow(hwnd);
6687
6688   return 0;
6689 }
6690
6691 /***
6692  * DESCRIPTION:
6693  * Sets the font.  
6694  * 
6695  * PARAMETER(S):
6696  * [I] HWND : window handle
6697  * [I] HFONT : font handle
6698  * [I] WORD : redraw flag
6699  *
6700  * RETURN:
6701  * Zero
6702  */
6703 static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw)
6704 {
6705   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6706   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
6707
6708   TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
6709
6710   if (hFont == 0)
6711   {
6712     infoPtr->hFont = infoPtr->hDefaultFont;
6713   }
6714   else
6715   {
6716     infoPtr->hFont = hFont;
6717   }
6718
6719   if (uView == LVS_REPORT)
6720   {
6721     /* set header font */
6722     SendMessageA(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, 
6723                    MAKELPARAM(fRedraw, 0));
6724   }
6725
6726   /* invalidate listview control client area */
6727   InvalidateRect(hwnd, NULL, TRUE);
6728   
6729   if (fRedraw != FALSE)
6730   {
6731     UpdateWindow(hwnd);
6732   }
6733
6734   return 0;
6735 }
6736
6737 /***
6738  * DESCRIPTION:
6739  * Resizes the listview control. This function processes WM_SIZE
6740  * messages.  At this time, the width and height are not used.
6741  * 
6742  * PARAMETER(S):
6743  * [I] HWND : window handle
6744  * [I] WORD : new width
6745  * [I] WORD : new height
6746  *
6747  * RETURN:
6748  * Zero
6749  */
6750 static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height)
6751 {
6752   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE); 
6753   UINT uView = lStyle & LVS_TYPEMASK;
6754
6755   TRACE("(hwnd=%x, width=%d, height=%d)\n",hwnd, Width, Height);
6756
6757   LISTVIEW_UpdateSize(hwnd);
6758
6759   if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6760   {
6761     if (lStyle & LVS_ALIGNLEFT)
6762     {
6763       LISTVIEW_AlignLeft(hwnd);
6764     }
6765     else
6766     {
6767       LISTVIEW_AlignTop(hwnd);
6768     }
6769   }
6770
6771   LISTVIEW_UpdateScroll(hwnd);
6772   
6773   /* invalidate client area + erase background */
6774   InvalidateRect(hwnd, NULL, TRUE);
6775
6776   return 0;
6777 }
6778
6779 /***
6780  * DESCRIPTION:
6781  * Sets the size information.
6782  * 
6783  * PARAMETER(S):
6784  * [I] HWND : window handle
6785  *
6786  * RETURN:
6787  * Zero
6788  */
6789 static VOID LISTVIEW_UpdateSize(HWND hwnd)
6790 {
6791   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6792   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6793   UINT uView = lStyle & LVS_TYPEMASK;
6794   RECT rcList;
6795   
6796   GetClientRect(hwnd, &rcList);
6797   infoPtr->rcList.left = 0;
6798   infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
6799   infoPtr->rcList.top = 0;
6800   infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
6801      
6802   if (uView == LVS_LIST)
6803   {
6804     if ((lStyle & WS_HSCROLL) == 0)
6805     {
6806       INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
6807       if (infoPtr->rcList.bottom > nHScrollHeight)
6808       { 
6809         infoPtr->rcList.bottom -= nHScrollHeight;
6810       }
6811     }
6812   }
6813   else if (uView == LVS_REPORT)
6814   {
6815     HDLAYOUT hl;
6816     WINDOWPOS wp;
6817
6818     hl.prc = &rcList;
6819     hl.pwpos = &wp;
6820     Header_Layout(infoPtr->hwndHeader, &hl);
6821     if (!(LVS_NOCOLUMNHEADER & lStyle))
6822     {
6823       infoPtr->rcList.top = max(wp.cy, 0);
6824     }
6825   }
6826 }
6827
6828 /***
6829  * DESCRIPTION:
6830  * Processes WM_STYLECHANGED messages. 
6831  * 
6832  * PARAMETER(S):
6833  * [I] HWND : window handle
6834  * [I] WPARAM : window style type (normal or extended)
6835  * [I] LPSTYLESTRUCT : window style information
6836  *
6837  * RETURN:
6838  * Zero
6839  */
6840 static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType, 
6841                                  LPSTYLESTRUCT lpss)
6842 {
6843   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6844   UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
6845   UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
6846   RECT rcList = infoPtr->rcList;
6847
6848   TRACE("(hwnd=%x, styletype=%x, stylestruct=%p)\n", 
6849         hwnd, wStyleType, lpss);
6850
6851   if (wStyleType == GWL_STYLE)
6852   {
6853     if (uOldView == LVS_REPORT)
6854     {
6855       ShowWindow(infoPtr->hwndHeader, SW_HIDE);
6856     }
6857  
6858     if ((lpss->styleOld & WS_HSCROLL) != 0)
6859     {
6860        ShowScrollBar(hwnd, SB_HORZ, FALSE);
6861     }
6862  
6863     if ((lpss->styleOld & WS_VSCROLL) != 0)
6864     {
6865        ShowScrollBar(hwnd, SB_VERT, FALSE);
6866     }
6867  
6868     if (uNewView == LVS_ICON)
6869     {
6870       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
6871       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
6872       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6873       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
6874       if (lpss->styleNew & LVS_ALIGNLEFT)
6875       {
6876         LISTVIEW_AlignLeft(hwnd);
6877       }
6878       else
6879       {
6880         LISTVIEW_AlignTop(hwnd);
6881       }
6882     }
6883     else if (uNewView == LVS_REPORT)
6884     {
6885       HDLAYOUT hl;
6886       WINDOWPOS wp;
6887
6888       hl.prc = &rcList;
6889       hl.pwpos = &wp;
6890       Header_Layout(infoPtr->hwndHeader, &hl);
6891       SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy, 
6892                    wp.flags);
6893       if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
6894       {
6895         ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
6896       }
6897       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
6898       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
6899       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6900       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
6901     }
6902     else if (uNewView == LVS_LIST)
6903     {
6904       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
6905       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
6906       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6907       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
6908     }
6909     else
6910     {
6911       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
6912       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
6913       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6914       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
6915       if (lpss->styleNew & LVS_ALIGNLEFT)
6916       {
6917         LISTVIEW_AlignLeft(hwnd);
6918       }
6919       else
6920       {
6921         LISTVIEW_AlignTop(hwnd);
6922       }
6923     }
6924
6925     /* update the size of the client area */
6926     LISTVIEW_UpdateSize(hwnd);
6927
6928     /* add scrollbars if needed */
6929     LISTVIEW_UpdateScroll(hwnd);
6930     
6931     /* invalidate client area + erase background */
6932     InvalidateRect(hwnd, NULL, TRUE);
6933
6934     /* print the list of unsupported window styles */
6935     LISTVIEW_UnsupportedStyles(lpss->styleNew);
6936   }
6937
6938   return 0;
6939 }
6940
6941 /***
6942  * DESCRIPTION:
6943  * Window procedure of the listview control.
6944  *
6945  */
6946 static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
6947                                    LPARAM lParam)
6948 {
6949   switch (uMsg)
6950   {
6951   case LVM_APPROXIMATEVIEWRECT: 
6952     return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam, 
6953                                         LOWORD(lParam), HIWORD(lParam));
6954   case LVM_ARRANGE: 
6955     return LISTVIEW_Arrange(hwnd, (INT)wParam);
6956
6957 /* case LVM_CREATEDRAGIMAGE: */
6958
6959   case LVM_DELETEALLITEMS:
6960     return LISTVIEW_DeleteAllItems(hwnd);
6961
6962   case LVM_DELETECOLUMN:
6963     return LISTVIEW_DeleteColumn(hwnd, (INT)wParam);
6964
6965   case LVM_DELETEITEM:
6966     return LISTVIEW_DeleteItem(hwnd, (INT)wParam);
6967
6968   case LVM_EDITLABELW:
6969   case LVM_EDITLABELA:
6970     return LISTVIEW_EditLabelA(hwnd, (INT)wParam);
6971
6972   case LVM_ENSUREVISIBLE:
6973     return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
6974
6975   case LVM_FINDITEMA:
6976     return LISTVIEW_FindItem(hwnd, (INT)wParam, (LPLVFINDINFO)lParam);
6977
6978   case LVM_GETBKCOLOR:
6979     return LISTVIEW_GetBkColor(hwnd);
6980
6981 /*      case LVM_GETBKIMAGE: */
6982
6983   case LVM_GETCALLBACKMASK:
6984     return LISTVIEW_GetCallbackMask(hwnd);
6985
6986   case LVM_GETCOLUMNA:
6987     return LISTVIEW_GetColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
6988
6989 /*      case LVM_GETCOLUMNW: */
6990
6991   case LVM_GETCOLUMNORDERARRAY:
6992     FIXME("Unimplemented msg LVM_GETCOLUMNORDERARRAY\n");
6993     return 0;
6994
6995   case LVM_GETCOLUMNWIDTH:
6996     return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam);
6997
6998   case LVM_GETCOUNTPERPAGE:
6999     return LISTVIEW_GetCountPerPage(hwnd);
7000
7001   case LVM_GETEDITCONTROL:
7002     return LISTVIEW_GetEditControl(hwnd);
7003
7004   case LVM_GETEXTENDEDLISTVIEWSTYLE:
7005     return LISTVIEW_GetExtendedListViewStyle(hwnd);
7006
7007   case LVM_GETHEADER:
7008     return LISTVIEW_GetHeader(hwnd);
7009
7010 /*      case LVM_GETHOTCURSOR: */
7011
7012   case LVM_GETHOTITEM:
7013     return LISTVIEW_GetHotItem(hwnd);
7014
7015 /*      case LVM_GETHOVERTIME: */
7016
7017   case LVM_GETIMAGELIST:
7018     return LISTVIEW_GetImageList(hwnd, (INT)wParam);
7019
7020 /*      case LVM_GETISEARCHSTRING: */
7021
7022   case LVM_GETITEMA:
7023     return LISTVIEW_GetItemA(hwnd, (LPLVITEMA)lParam);
7024
7025 /*      case LVM_GETITEMW: */
7026
7027   case LVM_GETITEMCOUNT:
7028     return LISTVIEW_GetItemCount(hwnd);
7029
7030   case LVM_GETITEMPOSITION:
7031     return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam);
7032
7033   case LVM_GETITEMRECT: 
7034     return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam);
7035
7036   case LVM_GETITEMSPACING: 
7037     return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam);
7038
7039   case LVM_GETITEMSTATE: 
7040     return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam);
7041     
7042   case LVM_GETITEMTEXTA:
7043     LISTVIEW_GetItemTextA(hwnd, (INT)wParam, (LPLVITEMA)lParam);
7044     break;
7045
7046 /*      case LVM_GETITEMTEXTW: */
7047
7048   case LVM_GETNEXTITEM:
7049     return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
7050
7051 /*      case LVM_GETNUMBEROFWORKAREAS: */
7052
7053   case LVM_GETORIGIN:
7054     return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam);
7055
7056   case LVM_GETSELECTEDCOUNT:
7057     return LISTVIEW_GetSelectedCount(hwnd);
7058
7059   case LVM_GETSELECTIONMARK: 
7060     return LISTVIEW_GetSelectionMark(hwnd);
7061
7062   case LVM_GETSTRINGWIDTHA:
7063     return LISTVIEW_GetStringWidthA (hwnd, (LPCSTR)lParam);
7064
7065 /*      case LVM_GETSTRINGWIDTHW: */
7066 /*      case LVM_GETSUBITEMRECT: */
7067
7068   case LVM_GETTEXTBKCOLOR:
7069     return LISTVIEW_GetTextBkColor(hwnd);
7070
7071   case LVM_GETTEXTCOLOR:
7072     return LISTVIEW_GetTextColor(hwnd);
7073
7074 /*      case LVM_GETTOOLTIPS: */
7075
7076   case LVM_GETTOPINDEX:
7077     return LISTVIEW_GetTopIndex(hwnd);
7078
7079 /*      case LVM_GETUNICODEFORMAT: */
7080
7081   case LVM_GETVIEWRECT:
7082     return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
7083
7084 /*      case LVM_GETWORKAREAS: */
7085
7086   case LVM_HITTEST:
7087     return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
7088
7089   case LVM_INSERTCOLUMNA:
7090     return LISTVIEW_InsertColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
7091
7092   case LVM_INSERTCOLUMNW:
7093     return LISTVIEW_InsertColumnW(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam);
7094
7095   case LVM_INSERTITEMA:
7096     return LISTVIEW_InsertItemA(hwnd, (LPLVITEMA)lParam);
7097
7098   case LVM_INSERTITEMW:
7099     return LISTVIEW_InsertItemW(hwnd, (LPLVITEMW)lParam);
7100
7101   case LVM_REDRAWITEMS:
7102     return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam);
7103
7104 /*   case LVM_SCROLL:  */
7105 /*     return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam); */
7106
7107   case LVM_SETBKCOLOR:
7108     return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam);
7109
7110 /*      case LVM_SETBKIMAGE: */
7111
7112   case LVM_SETCALLBACKMASK:
7113     return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam);
7114
7115   case LVM_SETCOLUMNA:
7116     return LISTVIEW_SetColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
7117
7118   case LVM_SETCOLUMNW:
7119     FIXME("Unimplemented msg LVM_SETCOLUMNW\n");
7120     return 0;
7121
7122   case LVM_SETCOLUMNORDERARRAY:
7123     FIXME("Unimplemented msg LVM_SETCOLUMNORDERARRAY\n");
7124     return 0;
7125
7126   case LVM_SETCOLUMNWIDTH:
7127     return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, (INT)lParam);
7128
7129   case LVM_SETEXTENDEDLISTVIEWSTYLE:
7130     return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam);
7131
7132 /*      case LVM_SETHOTCURSOR: */
7133
7134   case LVM_SETHOTITEM:
7135     return LISTVIEW_SetHotItem(hwnd, (INT)wParam);
7136
7137 /*      case LVM_SETHOVERTIME: */
7138 /*      case LVM_SETICONSPACING: */
7139         
7140   case LVM_SETIMAGELIST:
7141     return LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
7142
7143   case LVM_SETITEMA:
7144     return LISTVIEW_SetItemA(hwnd, (LPLVITEMA)lParam);
7145
7146 /*      case LVM_SETITEMW: */
7147
7148   case LVM_SETITEMCOUNT: 
7149     return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam);
7150     
7151   case LVM_SETITEMPOSITION:
7152     return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam),
7153                                     (INT)HIWORD(lParam));
7154
7155 /*      case LVM_SETITEMPOSITION32: */
7156
7157   case LVM_SETITEMSTATE: 
7158     return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMA)lParam);
7159
7160   case LVM_SETITEMTEXTA:
7161     return LISTVIEW_SetItemTextA(hwnd, (INT)wParam, (LPLVITEMA)lParam);
7162
7163 /*      case LVM_SETITEMTEXTW: */
7164
7165   case LVM_SETSELECTIONMARK:
7166     return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam);
7167
7168   case LVM_SETTEXTBKCOLOR:
7169     return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam);
7170
7171   case LVM_SETTEXTCOLOR:
7172     return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam);
7173
7174 /*      case LVM_SETTOOLTIPS: */
7175 /*      case LVM_SETUNICODEFORMAT: */
7176 /*      case LVM_SETWORKAREAS: */
7177
7178   case LVM_SORTITEMS:
7179     return LISTVIEW_SortItems(hwnd, wParam, lParam);
7180
7181 /*      case LVM_SUBITEMHITTEST: */
7182
7183   case LVM_UPDATE: 
7184     return LISTVIEW_Update(hwnd, (INT)wParam);
7185
7186 /*      case WM_CHAR: */
7187   case WM_COMMAND:
7188     return LISTVIEW_Command(hwnd, wParam, lParam);
7189
7190   case WM_CREATE:
7191     return LISTVIEW_Create(hwnd, wParam, lParam);
7192     
7193   case WM_ERASEBKGND:
7194     return LISTVIEW_EraseBackground(hwnd, wParam, lParam);
7195
7196   case WM_GETDLGCODE:
7197     return DLGC_WANTCHARS | DLGC_WANTARROWS;
7198
7199   case WM_GETFONT:
7200     return LISTVIEW_GetFont(hwnd);
7201
7202   case WM_HSCROLL:
7203     return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam), 
7204                             (INT)HIWORD(wParam), (HWND)lParam);
7205
7206   case WM_KEYDOWN:
7207     return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam);
7208
7209   case WM_KILLFOCUS:
7210     return LISTVIEW_KillFocus(hwnd);
7211
7212   case WM_LBUTTONDBLCLK:
7213     return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam), 
7214                                 HIWORD(lParam));
7215     
7216   case WM_LBUTTONDOWN:
7217     return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam), 
7218                                 HIWORD(lParam));
7219   case WM_LBUTTONUP:
7220     return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam), 
7221                               HIWORD(lParam));
7222     
7223 /*      case WM_MOUSEMOVE: */
7224 /*          return LISTVIEW_MouseMove (hwnd, wParam, lParam); */
7225
7226   case WM_NCCREATE:
7227     return LISTVIEW_NCCreate(hwnd, wParam, lParam);
7228
7229   case WM_NCDESTROY:
7230     return LISTVIEW_NCDestroy(hwnd);
7231
7232   case WM_NOTIFY:
7233     return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam);
7234
7235   case WM_NOTIFYFORMAT:
7236     return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam);
7237
7238   case WM_PAINT: 
7239     return LISTVIEW_Paint(hwnd, (HDC)wParam); 
7240
7241   case WM_RBUTTONDBLCLK:
7242     return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam), 
7243                                   HIWORD(lParam));
7244
7245   case WM_RBUTTONDOWN:
7246     return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam), 
7247                                 HIWORD(lParam));
7248
7249   case WM_RBUTTONUP:
7250     return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam), 
7251                               HIWORD(lParam));
7252
7253   case WM_SETFOCUS:
7254     return LISTVIEW_SetFocus(hwnd, (HWND)wParam);
7255
7256   case WM_SETFONT:
7257     return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam);
7258
7259 /*      case WM_SETREDRAW: */
7260
7261   case WM_SIZE:
7262     return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
7263
7264   case WM_STYLECHANGED:
7265     return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam);
7266
7267 /*      case WM_TIMER: */
7268
7269   case WM_VSCROLL:
7270     return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam), 
7271                             (INT)HIWORD(wParam), (HWND)lParam);
7272
7273 /*      case WM_WINDOWPOSCHANGED: */
7274 /*      case WM_WININICHANGE: */
7275
7276   default:
7277     if (uMsg >= WM_USER)
7278     {
7279       ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, 
7280           lParam);
7281     }
7282
7283     /* call default window procedure */
7284     return DefWindowProcA(hwnd, uMsg, wParam, lParam);
7285   }
7286
7287   return 0;
7288 }
7289
7290 /***
7291  * DESCRIPTION:
7292  * Registers the window class.
7293  * 
7294  * PARAMETER(S):
7295  * None
7296  *
7297  * RETURN:
7298  * None
7299  */
7300 VOID LISTVIEW_Register(void)
7301 {
7302   WNDCLASSA wndClass;
7303
7304   if (!GlobalFindAtomA(WC_LISTVIEWA)) 
7305   {
7306     ZeroMemory(&wndClass, sizeof(WNDCLASSA));
7307     wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
7308     wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
7309     wndClass.cbClsExtra = 0;
7310     wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
7311     wndClass.hCursor = LoadCursorA(0, IDC_ARROWA);
7312     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
7313     wndClass.lpszClassName = WC_LISTVIEWA;
7314     RegisterClassA(&wndClass);
7315   }
7316 }
7317
7318 /***
7319  * DESCRIPTION:
7320  * Unregisters the window class.
7321  * 
7322  * PARAMETER(S):
7323  * None
7324  *
7325  * RETURN:
7326  * None
7327  */
7328 VOID LISTVIEW_Unregister(void)
7329 {
7330   if (GlobalFindAtomA(WC_LISTVIEWA))
7331   {
7332     UnregisterClassA(WC_LISTVIEWA, (HINSTANCE)NULL);
7333   }
7334 }
7335
7336 /***
7337  * DESCRIPTION:
7338  * Handle any WM_COMMAND messages
7339  * 
7340  * PARAMETER(S):
7341  *
7342  * RETURN:
7343  */
7344 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam)
7345 {
7346     switch (HIWORD(wParam))
7347     {
7348         case EN_UPDATE:
7349         {
7350             /* 
7351              * Adjust the edit window size 
7352              */
7353             char buffer[1024];
7354             LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7355             HDC           hdc      = GetDC(infoPtr->hwndEdit);
7356             RECT          rect;
7357             SIZE          sz;
7358
7359             GetWindowTextA(infoPtr->hwndEdit, buffer, 1024);
7360             GetWindowRect(infoPtr->hwndEdit, &rect);
7361             if (GetTextExtentPoint32A(hdc, buffer, strlen(buffer), &sz))
7362             {
7363                     SetWindowPos ( 
7364                 infoPtr->hwndEdit,
7365                 HWND_TOP, 
7366                 0, 
7367                 0,
7368                 sz.cx + 15,
7369                 rect.bottom - rect.top,
7370                 SWP_DRAWFRAME|SWP_NOMOVE);
7371             }
7372             ReleaseDC(hwnd, hdc);
7373
7374             break;
7375         }
7376
7377         default:
7378           return SendMessageA (GetParent (hwnd), WM_COMMAND, wParam, lParam);
7379     }
7380
7381     return 0;
7382 }
7383
7384
7385 /***
7386  * DESCRIPTION:
7387  * Subclassed edit control windproc function
7388  *
7389  * PARAMETER(S):
7390  *
7391  * RETURN:
7392  */
7393 LRESULT CALLBACK EditLblWndProc(HWND hwnd, UINT uMsg, 
7394         WPARAM wParam, LPARAM lParam)
7395 {
7396     BOOL cancel = TRUE;
7397     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(GetParent(hwnd), 0);
7398     EDITLABEL_ITEM *einfo = infoPtr->pedititem;
7399
7400     switch (uMsg)
7401     {
7402         case WM_GETDLGCODE:
7403           return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
7404                         
7405         case WM_KILLFOCUS:
7406             break;
7407
7408         case WM_DESTROY:
7409             {
7410                 WNDPROC editProc = einfo->EditWndProc;
7411                 SetWindowLongA(hwnd, GWL_WNDPROC, (LONG)editProc);
7412                 COMCTL32_Free(einfo);
7413                 infoPtr->pedititem = NULL;
7414                 return CallWindowProcA(editProc, hwnd, uMsg, wParam, lParam);
7415             }
7416
7417         case WM_CHAR:
7418             if (VK_RETURN == (INT)wParam)
7419             {
7420                 cancel = FALSE;
7421                 break;
7422             }
7423             else if (VK_ESCAPE == (INT)wParam)
7424                 break;
7425
7426         default:
7427             return CallWindowProcA(einfo->EditWndProc, hwnd, 
7428                         uMsg, wParam, lParam);
7429     }
7430
7431     if (einfo->EditLblCb)
7432     {
7433         char *buffer  = NULL;
7434
7435         if (!cancel)
7436         {
7437             int len = 1 + GetWindowTextLengthA(hwnd);
7438
7439             if (len > 1)
7440             {
7441                 if (NULL != (buffer = (char *)COMCTL32_Alloc(len*sizeof(char))))
7442                 {
7443                     GetWindowTextA(hwnd, buffer, len);
7444                 }
7445             }
7446         }
7447             
7448         einfo->EditLblCb(GetParent(hwnd), buffer, einfo->param);
7449
7450         if (buffer)
7451             COMCTL32_Free(buffer);
7452
7453         einfo->EditLblCb = NULL;
7454     }
7455
7456     SendMessageA(hwnd, WM_CLOSE, 0, 0);
7457     return TRUE;
7458 }
7459
7460
7461 /***
7462  * DESCRIPTION:
7463  * Creates a subclassed edit cotrol
7464  *
7465  * PARAMETER(S):
7466  *
7467  * RETURN:
7468  */
7469 HWND CreateEditLabel(LPCSTR text, DWORD style, INT x, INT y, 
7470         INT width, INT height, HWND parent, HINSTANCE hinst, 
7471         EditlblCallback EditLblCb, DWORD param)
7472 {
7473     HWND hedit;
7474     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(parent, 0);
7475     if (NULL == (infoPtr->pedititem = COMCTL32_Alloc(sizeof(EDITLABEL_ITEM))))
7476         return 0;
7477
7478     style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
7479     if (!(hedit = CreateWindowA("Edit", text, style, x, y, width, height, 
7480                     parent, 0, hinst, 0)))
7481     {
7482         COMCTL32_Free(infoPtr->pedititem);
7483         return 0;
7484     }
7485
7486     infoPtr->pedititem->param = param;
7487     infoPtr->pedititem->EditLblCb = EditLblCb;
7488     infoPtr->pedititem->EditWndProc = (WNDPROC)SetWindowLongA(hedit, 
7489           GWL_WNDPROC, (LONG) EditLblWndProc);
7490
7491     return hedit;
7492 }