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