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