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