Added missing prototypes for StrRetToBuf(A|W).
[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   
2088   /* get number of fully visible columns */
2089   nColumnCount = LISTVIEW_GetColumnCount(hwnd);
2090   nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
2091   nItem = ListView_GetTopIndex(hwnd);
2092
2093   for (i = 0; i < nColumnCount; i++)
2094   {
2095     for (j = 0; j < nCountPerColumn; j++, nItem++)
2096     {
2097       if (nItem >= GETITEMCOUNT(infoPtr))
2098         return;
2099
2100       rcItem.top = j * infoPtr->nItemHeight;
2101       rcItem.left = i * infoPtr->nItemWidth;
2102       rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
2103       rcItem.right = rcItem.left + infoPtr->nItemWidth;
2104       LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem);
2105     }
2106   }
2107 }
2108
2109 /***
2110  * DESCRIPTION:
2111  * Draws listview items when in icon or small icon display mode.
2112  * 
2113  * PARAMETER(S):
2114  * [I] HWND : window handle
2115  * [I] HDC : device context handle 
2116  *
2117  * RETURN:
2118  * None
2119  */
2120 static VOID LISTVIEW_RefreshIcon(HWND hwnd, HDC hdc, BOOL bSmall)
2121 {
2122   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2123   POINT ptPosition;
2124   POINT ptOrigin;
2125   RECT rcItem;
2126   INT i;
2127
2128   LISTVIEW_GetOrigin(hwnd, &ptOrigin);
2129   for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
2130   {
2131     LISTVIEW_GetItemPosition(hwnd, i, &ptPosition);
2132     ptPosition.x += ptOrigin.x;
2133     ptPosition.y += ptOrigin.y;
2134       
2135     if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
2136     {
2137       if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
2138       {
2139         if (ptPosition.y < infoPtr->rcList.bottom)
2140         {
2141           if (ptPosition.x < infoPtr->rcList.right)
2142           {
2143             rcItem.top = ptPosition.y;
2144             rcItem.left = ptPosition.x;
2145             rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
2146             rcItem.right = rcItem.left + infoPtr->nItemWidth;
2147             if (bSmall == FALSE)
2148             {
2149               LISTVIEW_DrawLargeItem(hwnd, hdc, i, rcItem);
2150             }
2151             else
2152             {
2153               LISTVIEW_DrawItem(hwnd, hdc, i, rcItem);
2154             }
2155           }
2156         }
2157       }
2158     }
2159   }
2160 }
2161
2162 /***
2163  * DESCRIPTION:
2164  * Draws listview items.
2165  * 
2166  * PARAMETER(S):
2167  * [I] HWND : window handle
2168  * [I] HDC : device context handle 
2169  *
2170  * RETURN:
2171  * NoneX
2172  */
2173 static VOID LISTVIEW_Refresh(HWND hwnd, HDC hdc)
2174 {
2175   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2176   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
2177   HFONT hOldFont;
2178   HPEN hPen, hOldPen;
2179
2180   /* select font */
2181   hOldFont = SelectObject(hdc, infoPtr->hFont);
2182
2183   /* select the doted pen (for drawing the focus box) */
2184   hPen = CreatePen(PS_DOT, 1, 0);
2185   hOldPen = SelectObject(hdc, hPen);
2186
2187   /* select transparent brush (for drawing the focus box) */
2188   SelectObject(hdc, GetStockObject(NULL_BRUSH)); 
2189
2190   if (uView == LVS_LIST)
2191   {
2192     LISTVIEW_RefreshList(hwnd, hdc); 
2193   }
2194   else if (uView == LVS_REPORT)
2195   {
2196     LISTVIEW_RefreshReport(hwnd, hdc);
2197   }
2198   else if (uView == LVS_SMALLICON)
2199   {
2200     LISTVIEW_RefreshIcon(hwnd, hdc, TRUE);
2201   }
2202   else if (uView == LVS_ICON)
2203   {
2204     LISTVIEW_RefreshIcon(hwnd, hdc, FALSE);
2205   }
2206
2207   /* unselect objects */
2208   SelectObject(hdc, hOldFont);
2209   SelectObject(hdc, hOldPen);
2210   
2211   /* delete pen */
2212   DeleteObject(hPen);
2213 }
2214
2215
2216 /***
2217  * DESCRIPTION:
2218  * Calculates the approximate width and height of a given number of items.
2219  * 
2220  * PARAMETER(S):
2221  * [I] HWND : window handle
2222  * [I] INT : number of items
2223  * [I] INT : width
2224  * [I] INT : height
2225  *
2226  * RETURN:
2227  * Returns a DWORD. The width in the low word and the height in high word.
2228  */
2229 static LRESULT LISTVIEW_ApproximateViewRect(HWND hwnd, INT nItemCount, 
2230                                             WORD wWidth, WORD wHeight)
2231 {
2232   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2233   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
2234   INT nItemCountPerColumn = 1;
2235   INT nColumnCount = 0;
2236   DWORD dwViewRect = 0;
2237
2238   if (nItemCount == -1)
2239   {
2240     nItemCount = GETITEMCOUNT(infoPtr);
2241   }
2242
2243   if (uView == LVS_LIST)
2244   {
2245     if (wHeight == 0xFFFF)
2246     {
2247       /* use current height */
2248       wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2249     }
2250
2251     if (wHeight < infoPtr->nItemHeight)
2252     {
2253       wHeight = infoPtr->nItemHeight;
2254     }
2255
2256     if (nItemCount > 0)
2257     {
2258       if (infoPtr->nItemHeight > 0)
2259       {
2260         nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
2261         if (nItemCountPerColumn == 0)
2262         {
2263           nItemCountPerColumn = 1;
2264         }
2265         
2266         if (nItemCount % nItemCountPerColumn != 0)
2267         {
2268           nColumnCount = nItemCount / nItemCountPerColumn;
2269         }
2270         else
2271         {
2272           nColumnCount = nItemCount / nItemCountPerColumn + 1;
2273         }
2274       }
2275     }
2276
2277     /* Microsoft padding magic */
2278     wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
2279     wWidth = nColumnCount * infoPtr->nItemWidth + 2;
2280
2281     dwViewRect = MAKELONG(wWidth, wHeight);
2282   }
2283   else if (uView == LVS_REPORT)
2284   {
2285     /* TO DO */
2286   }
2287   else if (uView == LVS_SMALLICON)
2288   {
2289     /* TO DO */
2290   }
2291   else if (uView == LVS_ICON)
2292   {
2293     /* TO DO */
2294   }
2295  
2296   return dwViewRect;
2297 }
2298
2299 /***
2300  * DESCRIPTION:
2301  * Arranges listview items in icon display mode.
2302  * 
2303  * PARAMETER(S):
2304  * [I] HWND : window handle
2305  * [I] INT : alignment code
2306  *
2307  * RETURN:
2308  *   SUCCESS : TRUE
2309  *   FAILURE : FALSE
2310  */
2311 static LRESULT LISTVIEW_Arrange(HWND hwnd, INT nAlignCode)
2312 {
2313   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK; 
2314   BOOL bResult = FALSE;
2315
2316   if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
2317   {
2318     switch (nAlignCode)
2319     {
2320     case LVA_ALIGNLEFT:
2321       /* TO DO */
2322       break;
2323     case LVA_ALIGNTOP:
2324       /* TO DO */
2325       break;
2326     case LVA_DEFAULT:
2327       /* TO DO */
2328       break;
2329     case LVA_SNAPTOGRID:
2330       /* TO DO */
2331       break;
2332     }
2333   }
2334
2335   return bResult;
2336 }
2337
2338 /* << LISTVIEW_CreateDragImage >> */
2339
2340 /***
2341  * DESCRIPTION:
2342  * Removes all listview items and subitems.
2343  * 
2344  * PARAMETER(S):
2345  * [I] HWND : window handle
2346  *
2347  * RETURN:
2348  *   SUCCESS : TRUE
2349  *   FAILURE : FALSE
2350  */
2351 static LRESULT LISTVIEW_DeleteAllItems(HWND hwnd)
2352 {
2353   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2354   LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
2355   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2356   UINT uView = lStyle & LVS_TYPEMASK;
2357   LISTVIEW_ITEM *lpItem;
2358   LISTVIEW_SUBITEM *lpSubItem;
2359   NMLISTVIEW nmlv;
2360   BOOL bSuppress;
2361   BOOL bResult = FALSE;
2362   INT i;
2363   INT j;
2364   HDPA hdpaSubItems;
2365
2366   TRACE("(hwnd=%x,)\n", hwnd);
2367
2368   if (GETITEMCOUNT(infoPtr) > 0)
2369   {
2370     /* initialize memory */
2371     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2372     
2373     /* send LVN_DELETEALLITEMS notification */
2374     nmlv.hdr.hwndFrom = hwnd;
2375     nmlv.hdr.idFrom = lCtrlId;
2376     nmlv.hdr.code = LVN_DELETEALLITEMS;
2377     nmlv.iItem = -1;
2378
2379     /* verify if subsequent LVN_DELETEITEM notifications should be 
2380        suppressed */
2381     bSuppress = ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
2382
2383     for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
2384     {
2385       hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
2386       if (hdpaSubItems != NULL)
2387       {
2388         for (j = 1; j < hdpaSubItems->nItemCount; j++)
2389         {
2390           lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
2391           if (lpSubItem != NULL)
2392           {
2393             /* free subitem string */
2394             if ((lpSubItem->pszText != NULL) && 
2395                 (lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
2396             {
2397               COMCTL32_Free(lpSubItem->pszText);
2398             }
2399             
2400             /* free subitem */
2401             COMCTL32_Free(lpSubItem);
2402           }    
2403         }
2404     
2405         lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
2406         if (lpItem != NULL)
2407         {
2408           if (bSuppress == FALSE)
2409           {
2410             /* send LVN_DELETEITEM notification */
2411             nmlv.hdr.code = LVN_DELETEITEM;
2412             nmlv.iItem = i;
2413             nmlv.lParam = lpItem->lParam;
2414             ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
2415           }
2416
2417           /* free item string */
2418           if ((lpItem->pszText != NULL) && 
2419               (lpItem->pszText != LPSTR_TEXTCALLBACKA))
2420           {
2421             COMCTL32_Free(lpItem->pszText);
2422           }
2423           
2424           /* free item */
2425           COMCTL32_Free(lpItem);
2426         }
2427         
2428         DPA_Destroy(hdpaSubItems);
2429       }
2430     }
2431
2432     /* reinitialize listview memory */
2433     bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
2434     
2435     /* align items (set position of each item) */
2436     if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
2437     {
2438       if (lStyle & LVS_ALIGNLEFT)
2439       {
2440         LISTVIEW_AlignLeft(hwnd);
2441       }
2442       else
2443       {
2444         LISTVIEW_AlignTop(hwnd);
2445       }
2446     }
2447     
2448     LISTVIEW_UpdateScroll(hwnd);
2449
2450     /* invalidate client area (optimization needed) */
2451     InvalidateRect(hwnd, NULL, TRUE);
2452   }
2453   
2454   return bResult;
2455 }
2456
2457 /***
2458  * DESCRIPTION:
2459  * Removes a column from the listview control.
2460  * 
2461  * PARAMETER(S):
2462  * [I] HWND : window handle
2463  * [I] INT : column index
2464  *
2465  * RETURN:
2466  *   SUCCESS : TRUE
2467  *   FAILURE : FALSE
2468  */
2469 static LRESULT LISTVIEW_DeleteColumn(HWND hwnd, INT nColumn)
2470 {
2471   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2472   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
2473   BOOL bResult = FALSE;
2474   
2475   if (Header_DeleteItem(infoPtr->hwndHeader, nColumn) != FALSE)
2476   {
2477     bResult = LISTVIEW_RemoveColumn(infoPtr->hdpaItems, nColumn);
2478
2479     /* reset scroll parameters */
2480     if (uView == LVS_REPORT)
2481     {
2482       /* update scrollbar(s) */
2483       LISTVIEW_UpdateScroll(hwnd);
2484
2485       /* refresh client area */
2486       InvalidateRect(hwnd, NULL, FALSE);
2487     }
2488   }
2489
2490   return bResult;
2491 }
2492
2493 /***
2494  * DESCRIPTION:
2495  * Removes an item from the listview control.
2496  * 
2497  * PARAMETER(S):
2498  * [I] HWND : window handle
2499  * [I] INT : item index  
2500  *
2501  * RETURN:
2502  *   SUCCESS : TRUE
2503  *   FAILURE : FALSE
2504  */
2505 static LRESULT LISTVIEW_DeleteItem(HWND hwnd, INT nItem)
2506 {
2507   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2508   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
2509   UINT uView = lStyle & LVS_TYPEMASK;
2510   LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
2511   NMLISTVIEW nmlv;
2512   BOOL bResult = FALSE;
2513   HDPA hdpaSubItems;
2514   LISTVIEW_ITEM *lpItem;
2515   LISTVIEW_SUBITEM *lpSubItem;
2516   INT i;
2517
2518   TRACE("(hwnd=%x,nItem=%d)\n", hwnd, nItem);
2519
2520   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
2521   {
2522     /* initialize memory */
2523     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2524
2525     hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
2526     if (hdpaSubItems != NULL)
2527     {
2528       for (i = 1; i < hdpaSubItems->nItemCount; i++)
2529       {
2530         lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2531         if (lpSubItem != NULL)
2532         {
2533           /* free item string */
2534           if ((lpSubItem->pszText != NULL) && 
2535               (lpSubItem->pszText != LPSTR_TEXTCALLBACKA))
2536           {
2537             COMCTL32_Free(lpSubItem->pszText);
2538           }
2539           
2540           /* free item */
2541           COMCTL32_Free(lpSubItem);
2542         }    
2543       }
2544     
2545       lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
2546       if (lpItem != NULL)
2547       {
2548         /* send LVN_DELETEITEM notification */
2549         nmlv.hdr.hwndFrom = hwnd;
2550         nmlv.hdr.idFrom = lCtrlId;
2551         nmlv.hdr.code = LVN_DELETEITEM;
2552         nmlv.iItem = nItem;
2553         nmlv.lParam = lpItem->lParam;
2554         SendMessageA(GetParent(hwnd), WM_NOTIFY, (WPARAM)lCtrlId, 
2555                      (LPARAM)&nmlv);
2556
2557         /* free item string */
2558         if ((lpItem->pszText != NULL) && 
2559             (lpItem->pszText != LPSTR_TEXTCALLBACKA))
2560         {
2561           COMCTL32_Free(lpItem->pszText);
2562         }
2563         
2564         /* free item */
2565         COMCTL32_Free(lpItem);
2566       }
2567       
2568       bResult = DPA_Destroy(hdpaSubItems);
2569     }
2570
2571     /* align items (set position of each item) */
2572     if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
2573     {
2574       if (lStyle & LVS_ALIGNLEFT)
2575       {
2576         LISTVIEW_AlignLeft(hwnd);
2577       }
2578       else
2579       {
2580         LISTVIEW_AlignTop(hwnd);
2581       }
2582     }
2583
2584     LISTVIEW_UpdateScroll(hwnd);
2585
2586     /* refresh client area */
2587     InvalidateRect(hwnd, NULL, TRUE);
2588   }
2589   
2590   return bResult;
2591 }
2592
2593 /* LISTVIEW_EditLabel */
2594
2595 /***
2596  * DESCRIPTION:
2597  * Ensures the specified item is visible, scrolling into view if necessary.
2598  * 
2599  * PARAMETER(S):
2600  * [I] HWND : window handle
2601  * [I] INT : item index
2602  * [I] BOOL : partially or entirely visible
2603  *
2604  * RETURN:
2605  *   SUCCESS : TRUE
2606  *   FAILURE : FALSE
2607  */
2608 static BOOL LISTVIEW_EnsureVisible(HWND hwnd, INT nItem, BOOL bPartial)
2609 {
2610   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2611   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
2612   INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2613   INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2614   INT nScrollPosHeight = 0;
2615   INT nScrollPosWidth = 0;
2616   SCROLLINFO scrollInfo;
2617   RECT rcItem;
2618
2619   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
2620   scrollInfo.cbSize = sizeof(SCROLLINFO);
2621   scrollInfo.fMask = SIF_POS;
2622
2623   /* ALWAYS bPartial == FALSE, FOR NOW! */
2624
2625   rcItem.left = LVIR_BOUNDS;
2626   if (LISTVIEW_GetItemRect(hwnd, nItem, &rcItem) != FALSE)
2627   {
2628     if (rcItem.left < infoPtr->rcList.left)
2629     {
2630       if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
2631       {
2632         /* scroll left */
2633         if (uView == LVS_LIST)
2634         {
2635           nScrollPosWidth = infoPtr->nItemWidth;
2636           rcItem.left += infoPtr->rcList.left;
2637         }
2638         else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
2639         {
2640           nScrollPosWidth = max(1, nListWidth / 10);
2641           rcItem.left += infoPtr->rcList.left;
2642         }
2643         
2644         if (rcItem.left % nScrollPosWidth == 0)
2645         {
2646           scrollInfo.nPos += rcItem.left / nScrollPosWidth;
2647         }
2648         else
2649         {
2650           scrollInfo.nPos += rcItem.left / nScrollPosWidth - 1;
2651         }
2652         
2653         SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
2654       }
2655     }
2656     else if (rcItem.right > infoPtr->rcList.right)
2657     {
2658       if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
2659       {
2660         /* scroll right */
2661         if (uView == LVS_LIST)
2662         {
2663           rcItem.right -= infoPtr->rcList.right;
2664           nScrollPosWidth = infoPtr->nItemWidth;
2665         }
2666         else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
2667         {
2668           rcItem.right -= infoPtr->rcList.right;
2669           nScrollPosWidth = max(1, nListWidth / 10);
2670         }
2671
2672         if (rcItem.right % nScrollPosWidth == 0)
2673         {
2674           scrollInfo.nPos += rcItem.right / nScrollPosWidth;
2675         }
2676         else
2677         {
2678           scrollInfo.nPos += rcItem.right / nScrollPosWidth + 1;
2679         }
2680
2681         SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
2682       }
2683     }
2684     
2685     if (rcItem.top < infoPtr->rcList.top)
2686     {
2687       /* scroll up */
2688       if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
2689       {
2690         if (uView == LVS_REPORT)
2691         {
2692           rcItem.top -= infoPtr->rcList.top; 
2693           nScrollPosHeight = infoPtr->nItemHeight;
2694         }
2695         else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
2696         {
2697           nScrollPosHeight = max(1, nListHeight / 10);
2698           rcItem.top += infoPtr->rcList.top;
2699         }
2700
2701         if (rcItem.top % nScrollPosHeight == 0)
2702         {
2703           scrollInfo.nPos += rcItem.top / nScrollPosHeight;
2704         }
2705         else
2706         {
2707           scrollInfo.nPos += rcItem.top / nScrollPosHeight - 1;
2708         }
2709         
2710         SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
2711       }
2712     }
2713     else if (rcItem.bottom > infoPtr->rcList.bottom)
2714     {
2715       /* scroll down */
2716       if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
2717       {
2718         if (uView == LVS_REPORT)
2719         {
2720           rcItem.bottom -= infoPtr->rcList.bottom;
2721           nScrollPosHeight = infoPtr->nItemHeight;
2722         }
2723         else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
2724         {
2725           nScrollPosHeight = max(1, nListHeight / 10);
2726           rcItem.bottom -= infoPtr->rcList.bottom;
2727         }
2728
2729         if (rcItem.bottom % nScrollPosHeight == 0)
2730         {
2731           scrollInfo.nPos += rcItem.bottom / nScrollPosHeight;
2732         }
2733         else
2734         {
2735           scrollInfo.nPos += rcItem.bottom / nScrollPosHeight + 1;
2736         }
2737         
2738         SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
2739       }
2740     }
2741   }
2742   
2743   return TRUE;
2744 }
2745
2746 /***
2747  * DESCRIPTION:
2748  * Retrieves the nearest item, given a position and a direction.
2749  * 
2750  * PARAMETER(S):
2751  * [I] HWND : window handle
2752  * [I] POINT : start position
2753  * [I] UINT : direction
2754  *
2755  * RETURN:
2756  * Item index if successdful, -1 otherwise.
2757  */
2758 static INT LISTVIEW_GetNearestItem(HWND hwnd, POINT pt, UINT vkDirection)
2759 {
2760   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2761   LVHITTESTINFO lvHitTestInfo;
2762   INT nItem = -1;
2763   RECT rcView;
2764
2765   if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
2766   {
2767     ZeroMemory(&lvHitTestInfo, sizeof(LVHITTESTINFO));
2768     LISTVIEW_GetOrigin(hwnd, &lvHitTestInfo.pt);
2769     lvHitTestInfo.pt.x += pt.x;
2770     lvHitTestInfo.pt.y += pt.y;
2771     
2772     do
2773     { 
2774       if (vkDirection == VK_DOWN)
2775       {
2776         lvHitTestInfo.pt.y += infoPtr->nItemHeight;
2777       }      
2778       else if (vkDirection == VK_UP)
2779       {
2780         lvHitTestInfo.pt.y -= infoPtr->nItemHeight;
2781       }
2782       else if (vkDirection == VK_LEFT)
2783       {
2784         lvHitTestInfo.pt.x -= infoPtr->nItemWidth;
2785       }
2786       else if (vkDirection == VK_RIGHT)
2787       {
2788         lvHitTestInfo.pt.x += infoPtr->nItemWidth;
2789       }
2790
2791       if (PtInRect(&rcView, lvHitTestInfo.pt) == FALSE)
2792       {
2793         return -1;
2794       }
2795       else
2796       {
2797         nItem = LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo);
2798       }
2799
2800     }
2801     while (nItem == -1);
2802   }
2803
2804   return nItem;
2805 }  
2806
2807 /***
2808  * DESCRIPTION:
2809  * Searches for an item with specific characteristics.
2810  * 
2811  * PARAMETER(S):
2812  * [I] HWND : window handle
2813  * [I] INT : base item index
2814  * [I] LPLVFINDINFO : item information to look for
2815  * 
2816  * RETURN:
2817  *   SUCCESS : index of item
2818  *   FAILURE : -1
2819  */
2820 static LRESULT LISTVIEW_FindItem(HWND hwnd, INT nStart, 
2821                                  LPLVFINDINFO lpFindInfo)
2822 {
2823   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2824   POINT ptItem;
2825   CHAR szDispText[DISP_TEXT_SIZE];
2826   LVITEMA lvItem;
2827   BOOL bWrap = FALSE;
2828   INT nItem = nStart;
2829   INT nLast = GETITEMCOUNT(infoPtr);
2830
2831   if ((nItem >= -1) && (lpFindInfo != NULL))
2832   {
2833     ZeroMemory(&lvItem, sizeof(LVITEMA));
2834
2835     if (lpFindInfo->flags & LVFI_PARAM)
2836     {
2837       lvItem.mask |= LVIF_PARAM;
2838     }
2839     
2840     if (lpFindInfo->flags & LVFI_STRING)
2841     {
2842       lvItem.mask |= LVIF_TEXT;
2843       lvItem.pszText = szDispText;
2844       lvItem.cchTextMax = DISP_TEXT_SIZE;
2845     }
2846
2847     if (lpFindInfo->flags & LVFI_PARTIAL)
2848     {
2849       lvItem.mask |= LVIF_TEXT;
2850       lvItem.pszText = szDispText;
2851       lvItem.cchTextMax = DISP_TEXT_SIZE;
2852     }
2853
2854     if (lpFindInfo->flags & LVFI_WRAP)
2855     {
2856       bWrap = TRUE;
2857     }
2858
2859     if (lpFindInfo->flags & LVFI_NEARESTXY)
2860     {
2861       ptItem.x = lpFindInfo->pt.x;
2862       ptItem.y = lpFindInfo->pt.y;
2863     }
2864
2865     while (1)
2866     {
2867       while (nItem < nLast)
2868       {
2869         if (lpFindInfo->flags & LVFI_NEARESTXY)
2870         {
2871           nItem = LISTVIEW_GetNearestItem(hwnd, ptItem, 
2872                                           lpFindInfo->vkDirection);
2873           if (nItem != -1)
2874           {
2875             /* get position of the new item index */
2876             if (ListView_GetItemPosition(hwnd, nItem, &ptItem) == FALSE)
2877               return -1;
2878           }
2879           else
2880             return -1;
2881         }
2882         else
2883         {
2884           nItem++;
2885         }
2886         
2887         lvItem.iItem = nItem;
2888         lvItem.iSubItem = 0;
2889         if (ListView_GetItemA(hwnd, &lvItem) != FALSE)
2890         {
2891           if (lvItem.mask & LVIF_TEXT)
2892           {
2893             if (lpFindInfo->flags & LVFI_PARTIAL)
2894             {
2895               if (strstr(lvItem.pszText, lpFindInfo->psz) == NULL)
2896                 continue;
2897             }
2898             else
2899             {
2900               if (strcmp(lvItem.pszText, lpFindInfo->psz) != 0)
2901                 continue;
2902             }
2903           }
2904           
2905           if (lvItem.mask & LVIF_PARAM)
2906           {
2907             if (lpFindInfo->lParam != lvItem.lParam)
2908               continue;
2909           }
2910           
2911           return nItem;
2912         }
2913       }
2914
2915       if (bWrap != FALSE)
2916       {
2917         nItem = -1;
2918         nLast = nStart + 1;
2919         bWrap = FALSE;
2920       }
2921       else
2922       {
2923         return -1;
2924       }       
2925     }
2926   }
2927
2928  return -1; 
2929 }
2930
2931 /***
2932  * DESCRIPTION:
2933  * Retrieves the background color of the listview control.
2934  * 
2935  * PARAMETER(S):
2936  * [I] HWND : window handle
2937  *
2938  * RETURN:
2939  * COLORREF associated with the background.
2940  */
2941 static LRESULT LISTVIEW_GetBkColor(HWND hwnd)
2942 {
2943   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2944
2945   return infoPtr->clrBk;
2946 }
2947
2948 /***
2949  * DESCRIPTION:
2950  * Retrieves the background image of the listview control.
2951  * 
2952  * PARAMETER(S):
2953  * [I] HWND : window handle
2954  * [O] LPLVMKBIMAGE : background image attributes
2955  *
2956  * RETURN:
2957  *   SUCCESS : TRUE
2958  *   FAILURE : FALSE`
2959  */
2960 /* static LRESULT LISTVIEW_GetBkImage(HWND hwnd, LPLVBKIMAGE lpBkImage)   */
2961 /* {   */
2962 /*   FIXME (listview, "empty stub!\n"); */
2963 /*   return FALSE;   */
2964 /* }   */
2965
2966 /***
2967  * DESCRIPTION:
2968  * Retrieves the callback mask.
2969  * 
2970  * PARAMETER(S):
2971  * [I] HWND : window handle
2972  *
2973  * RETURN:
2974  *  Value of mask
2975  */
2976 static UINT LISTVIEW_GetCallbackMask(HWND hwnd)
2977 {
2978   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2979
2980   return infoPtr->uCallbackMask;
2981 }
2982
2983 /***
2984  * DESCRIPTION:
2985  * Retrieves column attributes.
2986  * 
2987  * PARAMETER(S):
2988  * [I] HWND : window handle
2989  * [I] INT :  column index
2990  * [IO] LPLVCOLUMNA : column information
2991  *
2992  * RETURN:
2993  *   SUCCESS : TRUE
2994  *   FAILURE : FALSE
2995  */
2996 static LRESULT LISTVIEW_GetColumnA(HWND hwnd, INT nItem, LPLVCOLUMNA lpColumn)
2997 {
2998   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
2999   HDITEMA hdi;
3000   BOOL bResult = FALSE;
3001   
3002   if (lpColumn != NULL)
3003   {
3004     /* initialize memory */
3005     ZeroMemory(&hdi, sizeof(HDITEMA));
3006     
3007     if (lpColumn->mask & LVCF_FMT)
3008     {
3009       hdi.mask |= HDI_FORMAT;
3010     }
3011
3012     if (lpColumn->mask & LVCF_WIDTH)
3013     {
3014       hdi.mask |= HDI_WIDTH;
3015     }
3016
3017     if (lpColumn->mask & LVCF_TEXT)
3018     {
3019       hdi.mask |= (HDI_TEXT | HDI_FORMAT);
3020     }
3021
3022     if (lpColumn->mask & LVCF_IMAGE)
3023     {
3024       hdi.mask |= HDI_IMAGE;
3025     }
3026
3027     if (lpColumn->mask & LVCF_ORDER)
3028     {
3029       hdi.mask |= HDI_ORDER;
3030     }
3031
3032     bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
3033     if (bResult != FALSE)
3034     {
3035       if (lpColumn->mask & LVCF_FMT) 
3036       {
3037         lpColumn->fmt = 0;
3038
3039         if (hdi.fmt & HDF_LEFT)
3040         {
3041           lpColumn->fmt |= LVCFMT_LEFT;
3042         }
3043         else if (hdi.fmt & HDF_RIGHT)
3044         {
3045           lpColumn->fmt |= LVCFMT_RIGHT;
3046         }
3047         else if (hdi.fmt & HDF_CENTER)
3048         {
3049           lpColumn->fmt |= LVCFMT_CENTER;
3050         }
3051
3052         if (hdi.fmt & HDF_IMAGE)
3053         {
3054           lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
3055         }
3056
3057         if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
3058         {
3059           lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
3060         }
3061       }
3062
3063       if (lpColumn->mask & LVCF_WIDTH)
3064       {
3065         lpColumn->cx = hdi.cxy;
3066       }
3067       
3068       if ((lpColumn->mask & LVCF_TEXT) && (lpColumn->pszText) && (hdi.pszText))
3069       {
3070         lstrcpynA (lpColumn->pszText, hdi.pszText, lpColumn->cchTextMax);
3071       }
3072
3073       if (lpColumn->mask & LVCF_IMAGE)
3074       {
3075         lpColumn->iImage = hdi.iImage;
3076       }
3077
3078       if (lpColumn->mask & LVCF_ORDER)
3079       {
3080         lpColumn->iOrder = hdi.iOrder;
3081       }
3082     }
3083   }
3084
3085   return bResult;
3086 }
3087
3088 /* LISTVIEW_GetColumnW */
3089 /* LISTVIEW_GetColumnOrderArray */
3090
3091 /***
3092  * DESCRIPTION:
3093  * Retrieves the column width.
3094  * 
3095  * PARAMETER(S):
3096  * [I] HWND : window handle
3097  * [I] int : column index
3098  *
3099  * RETURN:
3100  *   SUCCESS : column width
3101  *   FAILURE : zero 
3102  */
3103 static LRESULT LISTVIEW_GetColumnWidth(HWND hwnd, INT nColumn)
3104 {
3105   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3106   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3107   INT nColumnWidth = 0;
3108   HDITEMA hdi;
3109
3110   if (uView == LVS_LIST)
3111   {
3112     nColumnWidth = infoPtr->nItemWidth;
3113   }
3114   else if (uView == LVS_REPORT)
3115   {
3116     /* get column width from header */
3117     ZeroMemory(&hdi, sizeof(HDITEMA));
3118     hdi.mask = HDI_WIDTH;
3119     if (Header_GetItemA(infoPtr->hwndHeader, nColumn, &hdi) != FALSE)
3120     {
3121       nColumnWidth = hdi.cxy;
3122     }
3123   }
3124
3125   return nColumnWidth;
3126 }
3127
3128 /***
3129  * DESCRIPTION:
3130  * In list or report display mode, retrieves the number of items that can fit 
3131  * vertically in the visible area. In icon or small icon display mode, 
3132  * retrieves the total number of visible items.
3133  * 
3134  * PARAMETER(S):
3135  * [I] HWND : window handle
3136  *
3137  * RETURN:
3138  * Number of fully visible items.
3139  */
3140 static LRESULT LISTVIEW_GetCountPerPage(HWND hwnd)
3141 {
3142   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3143   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3144   INT nItemCount = 0;
3145
3146   if (uView == LVS_LIST)
3147   {
3148     if (infoPtr->rcList.right > infoPtr->nItemWidth)
3149     {
3150       nItemCount = LISTVIEW_GetCountPerRow(hwnd) * 
3151                    LISTVIEW_GetCountPerColumn(hwnd);
3152     }
3153   }
3154   else if (uView == LVS_REPORT)
3155   {
3156     nItemCount = LISTVIEW_GetCountPerColumn(hwnd);
3157   }
3158   else
3159   {
3160     nItemCount = GETITEMCOUNT(infoPtr);
3161   }
3162
3163   return nItemCount;
3164 }
3165
3166 /* LISTVIEW_GetEditControl */
3167
3168 /***
3169  * DESCRIPTION:
3170  * Retrieves the extended listview style.
3171  *
3172  * PARAMETERS:
3173  * [I] HWND  : window handle
3174  *
3175  * RETURN:
3176  *   SUCCESS : previous style
3177  *   FAILURE : 0
3178  */
3179 static LRESULT LISTVIEW_GetExtendedListViewStyle(HWND hwnd)
3180 {
3181     LISTVIEW_INFO *infoPtr;
3182
3183     /* make sure we can get the listview info */
3184     if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
3185         return (0);
3186
3187     return (infoPtr->dwExStyle);
3188 }
3189
3190 /***
3191  * DESCRIPTION:
3192  * Retrieves the handle to the header control.
3193  * 
3194  * PARAMETER(S):
3195  * [I] HWND : window handle
3196  *
3197  * RETURN:
3198  * Header handle.
3199  */
3200 static LRESULT LISTVIEW_GetHeader(HWND hwnd)
3201 {
3202   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3203
3204   return infoPtr->hwndHeader;
3205 }
3206
3207 /* LISTVIEW_GetHotCursor */
3208 /* LISTVIEW_GetHotItem */
3209 /* LISTVIEW_GetHoverTime */
3210
3211 /***
3212  * DESCRIPTION:
3213  * Retrieves an image list handle.
3214  * 
3215  * PARAMETER(S):
3216  * [I] HWND : window handle
3217  * [I] INT : image list identifier 
3218  * 
3219  * RETURN:
3220  *   SUCCESS : image list handle
3221  *   FAILURE : NULL
3222  */
3223 static LRESULT LISTVIEW_GetImageList(HWND hwnd, INT nImageList)
3224 {
3225   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3226   HIMAGELIST himl = NULL;
3227
3228   switch (nImageList) 
3229   {
3230   case LVSIL_NORMAL:
3231     himl = infoPtr->himlNormal;
3232     break;
3233   case LVSIL_SMALL:
3234     himl = infoPtr->himlSmall;
3235     break;
3236   case LVSIL_STATE:
3237     himl = infoPtr->himlState;
3238     break;
3239   }
3240
3241   return (LRESULT)himl;
3242 }
3243
3244 /* LISTVIEW_GetISearchString */
3245
3246 /***
3247  * DESCRIPTION:
3248  * Retrieves item attributes.
3249  * 
3250  * PARAMETER(S):
3251  * [I] HWND : window handle
3252  * [IO] LPLVITEMA : item info
3253  * 
3254  * RETURN:
3255  *   SUCCESS : TRUE 
3256  *   FAILURE : FALSE
3257  */
3258 static LRESULT LISTVIEW_GetItemA(HWND hwnd, LPLVITEMA lpLVItem)
3259 {
3260   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3261   LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
3262   BOOL bResult = FALSE;
3263   NMLVDISPINFOA dispInfo;
3264   LISTVIEW_SUBITEM *lpSubItem;
3265   LISTVIEW_ITEM *lpItem;
3266   HDPA hdpaSubItems;
3267
3268   TRACE("(hwnd=%x, lpLVItem=%p)\n", hwnd, lpLVItem);
3269
3270   if (lpLVItem != NULL)
3271   {
3272     if ((lpLVItem->iItem >= 0) && (lpLVItem->iItem < GETITEMCOUNT(infoPtr)))
3273     {
3274       ZeroMemory(&dispInfo, sizeof(NMLVDISPINFOA));
3275       hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3276       if (hdpaSubItems != NULL)
3277       {
3278         lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3279         if (lpItem != NULL)
3280         {
3281           bResult = TRUE;
3282           if (lpLVItem->iSubItem == 0)
3283           {
3284             if ((lpItem->iImage == I_IMAGECALLBACK) &&
3285                 (lpLVItem->mask & LVIF_IMAGE))
3286             {
3287               dispInfo.item.mask |= LVIF_IMAGE;
3288             }
3289             
3290             if ((lpItem->pszText == LPSTR_TEXTCALLBACKA) && 
3291                 (lpLVItem->mask & LVIF_TEXT))
3292             {
3293               dispInfo.item.mask |= LVIF_TEXT;
3294               ZeroMemory(lpLVItem->pszText, sizeof(CHAR)*lpLVItem->cchTextMax);
3295               dispInfo.item.pszText = lpLVItem->pszText;
3296               dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
3297             }
3298             
3299             if ((infoPtr->uCallbackMask != 0) && (lpLVItem->mask & LVIF_STATE))
3300             {        
3301               dispInfo.item.mask |= LVIF_STATE;
3302               dispInfo.item.stateMask = infoPtr->uCallbackMask; 
3303             }
3304             
3305             if (dispInfo.item.mask != 0)
3306             {
3307               dispInfo.hdr.hwndFrom = hwnd;
3308               dispInfo.hdr.idFrom = lCtrlId;
3309               dispInfo.hdr.code = LVN_GETDISPINFOA;
3310               dispInfo.item.iItem = lpLVItem->iItem;
3311               dispInfo.item.iSubItem = 0;
3312               dispInfo.item.lParam = lpItem->lParam;
3313               ListView_Notify(GetParent(hwnd), lCtrlId, &dispInfo);
3314             }
3315             
3316             if (dispInfo.item.mask & LVIF_IMAGE)
3317             {
3318               lpLVItem->iImage = dispInfo.item.iImage;
3319             }
3320             else if (lpLVItem->mask & LVIF_IMAGE)
3321             {
3322               lpLVItem->iImage = lpItem->iImage;
3323             }
3324             
3325             if (dispInfo.item.mask & LVIF_TEXT)
3326             {
3327               if (dispInfo.item.mask & LVIF_DI_SETITEM)
3328               {
3329                 Str_SetPtrA(&lpItem->pszText, dispInfo.item.pszText);
3330               }
3331               lpLVItem->pszText = dispInfo.item.pszText;
3332             }
3333             else if (lpLVItem->mask & LVIF_TEXT)
3334             {
3335               lpLVItem->pszText = lpItem->pszText;
3336             }
3337             
3338             if (dispInfo.item.mask & LVIF_STATE)
3339             {
3340               lpLVItem->state = lpItem->state;
3341               lpLVItem->state &= ~dispInfo.item.stateMask;
3342               lpLVItem->state |= (dispInfo.item.state & 
3343                                   dispInfo.item.stateMask);
3344             }
3345             else if (lpLVItem->mask & LVIF_STATE)
3346             {
3347               lpLVItem->state = lpItem->state & lpLVItem->stateMask;
3348             }
3349
3350             if (lpLVItem->mask & LVIF_PARAM)
3351             {
3352               lpLVItem->lParam = lpItem->lParam;
3353             }
3354             
3355             if (lpLVItem->mask & LVIF_INDENT)
3356             {
3357               lpLVItem->iIndent = lpItem->iIndent;
3358             }
3359           }
3360           else
3361           {
3362             lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, 
3363                                                lpLVItem->iSubItem);
3364             if (lpSubItem != NULL)
3365             {
3366               if ((lpSubItem->iImage == I_IMAGECALLBACK) &&
3367                   (lpLVItem->mask & LVIF_IMAGE))
3368               {
3369                 dispInfo.item.mask |= LVIF_IMAGE;
3370               }
3371               
3372               if ((lpSubItem->pszText == LPSTR_TEXTCALLBACKA) && 
3373                   (lpLVItem->mask & LVIF_TEXT))
3374               {
3375                 dispInfo.item.mask |= LVIF_TEXT;
3376                 ZeroMemory(lpLVItem->pszText, 
3377                            sizeof(CHAR)*lpLVItem->cchTextMax);
3378                 dispInfo.item.pszText = lpLVItem->pszText;
3379                 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
3380               }
3381             }
3382             else
3383             {
3384               if (lpLVItem->mask & LVIF_IMAGE)
3385               {
3386                 dispInfo.item.mask |= LVIF_IMAGE;
3387               }
3388           
3389               if (lpLVItem->mask & LVIF_TEXT)
3390               {
3391                 dispInfo.item.mask |= LVIF_TEXT;
3392                 ZeroMemory(lpLVItem->pszText, 
3393                            sizeof(CHAR)*lpLVItem->cchTextMax);
3394                 dispInfo.item.pszText = lpLVItem->pszText;
3395                 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
3396               }
3397             }
3398
3399             if (dispInfo.item.mask != 0)
3400             {
3401               dispInfo.hdr.hwndFrom = hwnd;
3402               dispInfo.hdr.idFrom = lCtrlId;
3403               dispInfo.hdr.code = LVN_GETDISPINFOA;
3404               dispInfo.item.iItem = lpLVItem->iItem;
3405               dispInfo.item.iSubItem = lpLVItem->iSubItem;
3406               dispInfo.item.lParam = lpItem->lParam;
3407               ListView_Notify(GetParent(hwnd), lCtrlId, &dispInfo);
3408             }
3409
3410             if (dispInfo.item.mask & LVIF_IMAGE)
3411             {
3412               lpLVItem->iImage = dispInfo.item.iImage;
3413             }
3414             else if (lpLVItem->mask & LVIF_IMAGE)
3415             {
3416               lpLVItem->iImage = lpItem->iImage;
3417             }
3418
3419             if (dispInfo.item.mask & LVIF_TEXT)
3420             {
3421               if (dispInfo.item.mask & LVIF_DI_SETITEM)
3422               {
3423                 if (lpSubItem)
3424                   Str_SetPtrA(&lpSubItem->pszText, dispInfo.item.pszText);
3425               }
3426               lpLVItem->pszText = dispInfo.item.pszText;
3427             }
3428             else if (lpLVItem->mask & LVIF_TEXT)
3429             {
3430               lpLVItem->pszText = lpSubItem->pszText;
3431             }
3432           }
3433         }
3434       }
3435     }
3436   }
3437
3438   return bResult;
3439 }
3440
3441 /* LISTVIEW_GetItemW */
3442 /* LISTVIEW_GetHotCursor */
3443
3444 /***
3445  * DESCRIPTION:
3446  * Retrieves the index of the hot item.
3447  *
3448  * PARAMETERS:
3449  * [I] HWND  : window handle
3450  *
3451  * RETURN:
3452  *   SUCCESS : hot item index
3453  *   FAILURE : -1 (no hot item)
3454  */
3455 static LRESULT LISTVIEW_GetHotItem(HWND hwnd)
3456 {
3457     LISTVIEW_INFO *infoPtr;
3458
3459     /* make sure we can get the listview info */
3460     if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
3461         return (-1);
3462
3463     return (infoPtr->nHotItem);
3464 }
3465
3466 /* LISTVIEW_GetHoverTime */
3467
3468 /***
3469  * DESCRIPTION:
3470  * Retrieves the number of items in the listview control.
3471  * 
3472  * PARAMETER(S):
3473  * [I] HWND : window handle
3474  * 
3475  * RETURN:
3476  * Number of items.
3477  */
3478 static LRESULT LISTVIEW_GetItemCount(HWND hwnd)
3479 {
3480   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3481
3482   return GETITEMCOUNT(infoPtr);
3483 }
3484
3485 /***
3486  * DESCRIPTION:
3487  * Retrieves the position (upper-left) of the listview control item.
3488  * 
3489  * PARAMETER(S):
3490  * [I] HWND : window handle
3491  * [I] INT : item index
3492  * [O] LPPOINT : coordinate information
3493  *
3494  * RETURN:
3495  *   SUCCESS : TRUE
3496  *   FAILURE : FALSE
3497  */
3498 static BOOL LISTVIEW_GetItemPosition(HWND hwnd, INT nItem, 
3499                                      LPPOINT lpptPosition)
3500 {
3501   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3502   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3503   BOOL bResult = FALSE;
3504   HDPA hdpaSubItems;
3505   LISTVIEW_ITEM *lpItem;
3506   INT nCountPerColumn;
3507   INT nRow;
3508
3509   TRACE("(hwnd=%x,nItem=%d,lpptPosition=%p)\n", hwnd, nItem, 
3510         lpptPosition);
3511
3512   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && 
3513       (lpptPosition != NULL))
3514   {
3515     if (uView == LVS_LIST)
3516     {
3517       bResult = TRUE;
3518       nItem = nItem - ListView_GetTopIndex(hwnd);
3519       nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
3520       if (nItem < 0)
3521       {
3522         nRow = nItem % nCountPerColumn;
3523         if (nRow == 0)
3524         {
3525           lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
3526           lpptPosition->y = 0;
3527         }
3528         else
3529         {
3530           lpptPosition->x = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
3531           lpptPosition->y = (nRow + nCountPerColumn) * infoPtr->nItemHeight;  
3532         }
3533       }
3534       else
3535       {
3536         lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
3537         lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
3538       }
3539     }
3540     else if (uView == LVS_REPORT)
3541     {
3542       bResult = TRUE;
3543       lpptPosition->x = REPORT_MARGINX;
3544       lpptPosition->y = ((nItem - ListView_GetTopIndex(hwnd)) * 
3545                          infoPtr->nItemHeight) + infoPtr->rcList.top;
3546     }
3547     else
3548     {
3549       hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
3550       if (hdpaSubItems != NULL)
3551       {
3552         lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
3553         if (lpItem != NULL)
3554         {
3555           bResult = TRUE;
3556           lpptPosition->x = lpItem->ptPosition.x;
3557           lpptPosition->y = lpItem->ptPosition.y;
3558         }
3559       }
3560     }
3561   }
3562     
3563   return bResult;
3564 }
3565
3566 /***
3567  * DESCRIPTION:
3568  * Retrieves the bounding rectangle for a listview control item.
3569  * 
3570  * PARAMETER(S):
3571  * [I] HWND : window handle
3572  * [I] INT : item index
3573  * [IO] LPRECT : bounding rectangle coordinates
3574  * 
3575  * RETURN:
3576  *   SUCCESS : TRUE
3577  *   FAILURE : FALSE
3578  */
3579 static LRESULT LISTVIEW_GetItemRect(HWND hwnd, INT nItem, LPRECT lprc)
3580 {
3581   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3582   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3583   BOOL bResult = FALSE;
3584   POINT ptOrigin;
3585   POINT ptItem;
3586   HDC hdc;
3587   HFONT hOldFont;
3588   INT nLeftPos;
3589   INT nLabelWidth;
3590   TEXTMETRICA tm;
3591
3592   TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd, nItem, lprc);
3593   
3594   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
3595   {
3596     if (ListView_GetItemPosition(hwnd, nItem, &ptItem) != FALSE)
3597     {
3598       switch(lprc->left)  
3599       {  
3600       case LVIR_ICON: 
3601         if (uView == LVS_ICON)
3602         {
3603           if (infoPtr->himlNormal != NULL)
3604           {
3605             if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
3606             {
3607               bResult = TRUE;
3608               lprc->left = ptItem.x + ptOrigin.x;
3609               lprc->top = ptItem.y + ptOrigin.y;
3610               lprc->right = lprc->left + infoPtr->iconSize.cx;
3611               lprc->bottom = (lprc->top + infoPtr->iconSize.cy + 
3612                               ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
3613             }
3614           }
3615         }
3616         else if (uView == LVS_SMALLICON)
3617         {
3618           if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
3619           {
3620             bResult = TRUE;
3621             lprc->left = ptItem.x + ptOrigin.x;
3622             lprc->top = ptItem.y + ptOrigin.y;
3623             lprc->bottom = lprc->top + infoPtr->nItemHeight;
3624
3625             if (infoPtr->himlState != NULL)
3626               lprc->left += infoPtr->iconSize.cx;
3627               
3628             if (infoPtr->himlSmall != NULL)
3629               lprc->right = lprc->left + infoPtr->iconSize.cx;
3630             else
3631               lprc->right = lprc->left;
3632           }
3633         }
3634         else 
3635         {
3636           bResult = TRUE;
3637           lprc->left = ptItem.x;
3638           lprc->top = ptItem.y;
3639           lprc->bottom = lprc->top + infoPtr->nItemHeight;
3640
3641           if (infoPtr->himlState != NULL)
3642           {
3643             lprc->left += infoPtr->iconSize.cx;
3644           }
3645             
3646           if (infoPtr->himlSmall != NULL)
3647           {
3648             lprc->right = lprc->left + infoPtr->iconSize.cx;
3649           }
3650           else
3651           {
3652             lprc->right = lprc->left;
3653           }
3654         }
3655         break;
3656
3657       case LVIR_LABEL: 
3658         if (uView == LVS_ICON)
3659         {
3660           if (infoPtr->himlNormal != NULL)
3661           {
3662             if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
3663             {
3664               bResult = TRUE;
3665               lprc->left = ptItem.x + ptOrigin.x;
3666               lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
3667                            ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
3668               nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
3669               if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
3670               {
3671                 lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
3672                 lprc->right = lprc->left + nLabelWidth;
3673               }
3674               else
3675               {
3676                 lprc->left += 1;
3677                 lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
3678               }
3679             
3680               hdc = GetDC(hwnd);
3681               hOldFont = SelectObject(hdc, infoPtr->hFont);
3682               GetTextMetricsA(hdc, &tm);
3683               lprc->bottom = lprc->top + tm.tmHeight + HEIGHT_PADDING;
3684               SelectObject(hdc, hOldFont);
3685               ReleaseDC(hwnd, hdc);
3686             }              
3687           }
3688         }
3689         else if (uView == LVS_SMALLICON)
3690         {
3691           if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
3692           {
3693             bResult = TRUE;
3694             nLeftPos = lprc->left = ptItem.x + ptOrigin.x; 
3695             lprc->top = ptItem.y + ptOrigin.y;
3696             lprc->bottom = lprc->top + infoPtr->nItemHeight;
3697             
3698             if (infoPtr->himlState != NULL)
3699             {
3700               lprc->left += infoPtr->iconSize.cx;
3701             }
3702             
3703             if (infoPtr->himlSmall != NULL)
3704             {
3705               lprc->left += infoPtr->iconSize.cx;
3706             }
3707             
3708             nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
3709             if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
3710             {
3711               lprc->right = lprc->left + nLabelWidth;
3712             }
3713             else
3714             {
3715               lprc->right = nLeftPos + infoPtr->nItemWidth;
3716             }
3717           }
3718         }
3719         else
3720         {
3721           bResult = TRUE;
3722           nLeftPos = lprc->left = ptItem.x; 
3723           lprc->top = ptItem.y;
3724           lprc->bottom = lprc->top + infoPtr->nItemHeight;
3725
3726           if (infoPtr->himlState != NULL)
3727           {
3728             lprc->left += infoPtr->iconSize.cx;
3729           }
3730
3731           if (infoPtr->himlSmall != NULL)
3732           {
3733             lprc->left += infoPtr->iconSize.cx;
3734           }
3735
3736           nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
3737           if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
3738           {
3739             lprc->right = lprc->left + nLabelWidth;
3740           }
3741           else
3742           {
3743             lprc->right = nLeftPos + infoPtr->nItemWidth;
3744           }
3745         }
3746         break;
3747
3748       case LVIR_BOUNDS:
3749         if (uView == LVS_ICON)
3750         {
3751           if (infoPtr->himlNormal != NULL)
3752           {
3753             if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
3754             {
3755               bResult = TRUE;
3756               lprc->left = ptItem.x + ptOrigin.x;
3757               lprc->top = ptItem.y + ptOrigin.y; 
3758               lprc->right = lprc->left + infoPtr->iconSpacing.cx;
3759               lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
3760             }
3761           } 
3762         }
3763         else if (uView == LVS_SMALLICON)
3764         {
3765           if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
3766           {
3767             bResult = TRUE;
3768             lprc->left = ptItem.x + ptOrigin.x; 
3769             lprc->right = lprc->left;
3770             lprc->top = ptItem.y + ptOrigin.y;
3771             lprc->bottom = lprc->top + infoPtr->nItemHeight;
3772             if (infoPtr->himlState != NULL)
3773               lprc->right += infoPtr->iconSize.cx;
3774             if (infoPtr->himlSmall != NULL)
3775               lprc->right += infoPtr->iconSize.cx;
3776
3777             nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
3778             if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
3779             {
3780               lprc->right += nLabelWidth;
3781             }
3782             else
3783             {
3784               lprc->right = lprc->left + infoPtr->nItemWidth;
3785             }
3786           }
3787         }
3788         else
3789         {
3790           bResult = TRUE;
3791           lprc->left = ptItem.x; 
3792           lprc->right = lprc->left;
3793           lprc->top = ptItem.y;
3794           lprc->bottom = lprc->top + infoPtr->nItemHeight;
3795
3796           if (infoPtr->himlState != NULL)
3797           {
3798             lprc->right += infoPtr->iconSize.cx;
3799           }
3800
3801           if (infoPtr->himlSmall != NULL)
3802           {
3803             lprc->right += infoPtr->iconSize.cx;
3804           }
3805
3806           nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
3807           if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
3808           {
3809             lprc->right += nLabelWidth;
3810           }
3811           else
3812           {
3813             lprc->right = lprc->left + infoPtr->nItemWidth;
3814           }
3815         }
3816         break;
3817         
3818       case LVIR_SELECTBOUNDS:
3819         if (uView == LVS_ICON)
3820         {
3821           if (infoPtr->himlNormal != NULL)
3822           {
3823             if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
3824             {
3825               bResult = TRUE;
3826               lprc->left = ptItem.x + ptOrigin.x;
3827               lprc->top = ptItem.y + ptOrigin.y;
3828               lprc->right = lprc->left + infoPtr->iconSpacing.cx;
3829               lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
3830             }
3831           } 
3832         }
3833         else if (uView == LVS_SMALLICON)
3834         {
3835           if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
3836           {
3837             bResult = TRUE;
3838             nLeftPos= lprc->left = ptItem.x + ptOrigin.x;  
3839             lprc->top = ptItem.y + ptOrigin.y;
3840             lprc->bottom = lprc->top + infoPtr->nItemHeight;
3841             
3842             if (infoPtr->himlState != NULL)
3843             {
3844               lprc->left += infoPtr->iconSize.cx;
3845             }
3846             
3847             lprc->right = lprc->left;
3848             
3849             if (infoPtr->himlSmall != NULL)
3850             {
3851               lprc->right += infoPtr->iconSize.cx;
3852             }
3853             
3854             nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
3855             if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
3856             {
3857               lprc->right += nLabelWidth;
3858             }
3859             else
3860             {
3861               lprc->right = nLeftPos + infoPtr->nItemWidth;
3862             }
3863           }
3864         }
3865         else
3866         {
3867           bResult = TRUE;
3868           nLeftPos = lprc->left = ptItem.x; 
3869           lprc->top = ptItem.y;
3870           lprc->bottom = lprc->top + infoPtr->nItemHeight;
3871
3872           if (infoPtr->himlState != NULL)
3873           {
3874             lprc->left += infoPtr->iconSize.cx;
3875           }
3876           
3877           lprc->right = lprc->left;
3878         
3879           if (infoPtr->himlSmall != NULL)
3880           {
3881             lprc->right += infoPtr->iconSize.cx;
3882           }
3883
3884           nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
3885           if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
3886           {
3887             lprc->right += nLabelWidth;
3888           }
3889           else
3890           {
3891             lprc->right = nLeftPos + infoPtr->nItemWidth;
3892           }
3893         }
3894         break;
3895       }
3896     }
3897   }
3898         
3899   return bResult;
3900 }
3901
3902 /***
3903  * DESCRIPTION:
3904  * Retrieves the width of a label.
3905  * 
3906  * PARAMETER(S):
3907  * [I] HWND : window handle
3908  * 
3909  * RETURN:
3910  *   SUCCESS : string width (in pixels)
3911  *   FAILURE : zero
3912  */
3913 static INT LISTVIEW_GetLabelWidth(HWND hwnd, INT nItem)
3914 {
3915   CHAR szDispText[DISP_TEXT_SIZE];
3916   INT nLabelWidth = 0;
3917   LVITEMA lvItem;
3918
3919   TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
3920
3921   ZeroMemory(&lvItem, sizeof(LVITEMA));
3922   lvItem.mask = LVIF_TEXT;
3923   lvItem.iItem = nItem;
3924   lvItem.cchTextMax = DISP_TEXT_SIZE;
3925   lvItem.pszText = szDispText;
3926   if (ListView_GetItemA(hwnd, &lvItem) != FALSE)
3927   {
3928     nLabelWidth = ListView_GetStringWidthA(hwnd, lvItem.pszText); 
3929   }
3930     
3931   return nLabelWidth;
3932 }
3933
3934 /***
3935  * DESCRIPTION:
3936  * Retrieves the spacing between listview control items.
3937  * 
3938  * PARAMETER(S):
3939  * [I] HWND : window handle
3940  * [I] BOOL : flag for small or large icon 
3941  *
3942  * RETURN:
3943  * Horizontal + vertical spacing
3944  */
3945 static LRESULT LISTVIEW_GetItemSpacing(HWND hwnd, BOOL bSmall)
3946 {
3947   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3948   LONG lResult;
3949
3950   if (bSmall == FALSE)
3951   {
3952     lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
3953   }
3954   else
3955   {
3956     /* TODO: need to store width of smallicon item */
3957     lResult = MAKELONG(0, infoPtr->nItemHeight);
3958   }  
3959   
3960   return lResult;
3961 }
3962
3963 /***
3964  * DESCRIPTION:
3965  * Retrieves the state of a listview control item.
3966  * 
3967  * PARAMETER(S):
3968  * [I] HWND : window handle
3969  * [I] INT : item index
3970  * [I] UINT : state mask
3971  * 
3972  * RETURN:
3973  * State specified by the mask.
3974  */
3975 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask)
3976 {
3977   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
3978   LVITEMA lvItem;
3979   UINT uState = 0;
3980
3981   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
3982   {
3983     ZeroMemory(&lvItem, sizeof(LVITEMA));
3984     lvItem.iItem = nItem;
3985     lvItem.stateMask = uMask;
3986     lvItem.mask = LVIF_STATE;
3987     if (ListView_GetItemA(hwnd, &lvItem) != FALSE)
3988     {
3989       uState = lvItem.state;
3990     }
3991   }
3992
3993   return uState;
3994 }
3995
3996 /***
3997  * DESCRIPTION:
3998  * Retrieves the text of a listview control item or subitem. 
3999  * 
4000  * PARAMETER(S):
4001  * [I] HWND : window handle
4002  * [I] INT : item index
4003  * [IO] LPLVITEMA : item information
4004  * 
4005  * RETURN:
4006  *   SUCCESS : string length
4007  *   FAILURE : 0
4008  */
4009 static LRESULT LISTVIEW_GetItemTextA(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
4010 {
4011   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4012   INT nLength = 0;
4013   
4014   if (lpLVItem != NULL)
4015   {
4016     if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
4017     {
4018       lpLVItem->mask = LVIF_TEXT;
4019       lpLVItem->iItem = nItem;
4020       if (ListView_GetItemA(hwnd, lpLVItem) != FALSE)
4021       {
4022         nLength = lstrlenA(lpLVItem->pszText);
4023       }
4024     }
4025   }
4026
4027   return nLength;
4028 }
4029
4030 /***
4031  * DESCRIPTION:
4032  * Searches for an item based on properties + relationships.
4033  * 
4034  * PARAMETER(S):
4035  * [I] HWND : window handle
4036  * [I] INT : item index
4037  * [I] INT : relationship flag
4038  * 
4039  * RETURN:
4040  *   SUCCESS : item index
4041  *   FAILURE : -1
4042  */
4043 static LRESULT LISTVIEW_GetNextItem(HWND hwnd, INT nItem, UINT uFlags)
4044 {
4045   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4046   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4047   UINT uMask = 0;
4048   LVFINDINFO lvFindInfo;
4049   INT nCountPerColumn;
4050   INT i;
4051   
4052   if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
4053   { 
4054     ZeroMemory(&lvFindInfo, sizeof(LVFINDINFO));
4055
4056     if (uFlags & LVNI_CUT)
4057       uMask |= LVIS_CUT;
4058     
4059     if (uFlags & LVNI_DROPHILITED)
4060       uMask |= LVIS_DROPHILITED;
4061           
4062     if (uFlags & LVNI_FOCUSED)
4063       uMask |= LVIS_FOCUSED;
4064
4065     if (uFlags & LVNI_SELECTED)
4066       uMask |= LVIS_SELECTED;
4067
4068     if (uFlags & LVNI_ABOVE)
4069     {
4070       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
4071       {
4072         while (nItem >= 0)
4073         {
4074           nItem--;
4075           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
4076             return nItem;
4077         }
4078       }
4079       else
4080       {
4081         lvFindInfo.flags = LVFI_NEARESTXY;
4082         lvFindInfo.vkDirection = VK_UP;
4083         ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
4084         while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
4085         {
4086           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
4087             return nItem;
4088         }
4089       }
4090     }
4091     else if (uFlags & LVNI_BELOW)
4092     {
4093       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
4094       {
4095         while (nItem < GETITEMCOUNT(infoPtr))
4096         {
4097           nItem++;
4098           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
4099             return nItem;
4100         }
4101       }
4102       else
4103       {
4104         lvFindInfo.flags = LVFI_NEARESTXY;
4105         lvFindInfo.vkDirection = VK_DOWN;
4106         ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
4107         while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
4108         {
4109           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
4110             return nItem;
4111         }
4112       }
4113     }
4114     else if (uFlags & LVNI_TOLEFT)
4115     {
4116       if (uView == LVS_LIST)
4117       {
4118         nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
4119         while (nItem - nCountPerColumn >= 0)
4120         {
4121           nItem -= nCountPerColumn;
4122           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
4123             return nItem;
4124         }
4125       }
4126       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4127       {
4128         lvFindInfo.flags = LVFI_NEARESTXY;
4129         lvFindInfo.vkDirection = VK_LEFT;
4130         ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
4131         while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
4132         {
4133           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
4134             return nItem;
4135         }
4136       }
4137     }
4138     else if (uFlags & LVNI_TORIGHT)
4139     {
4140       if (uView == LVS_LIST)
4141       {
4142         nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
4143         while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
4144         {
4145           nItem += nCountPerColumn;
4146           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
4147             return nItem;
4148         }
4149       }
4150       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4151       {
4152         lvFindInfo.flags = LVFI_NEARESTXY;
4153         lvFindInfo.vkDirection = VK_RIGHT;
4154         ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
4155         while ((nItem = ListView_FindItem(hwnd, nItem, &lvFindInfo)) != -1)
4156         {
4157           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
4158             return nItem;
4159         }
4160       }
4161     }
4162     else
4163     {
4164       nItem++;
4165
4166       /* search by index */
4167       for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
4168       {
4169         if ((ListView_GetItemState(hwnd, i, uMask) & uMask) == uMask)
4170           return i;
4171       }
4172     }
4173   }
4174
4175   return -1;
4176 }
4177
4178 /* LISTVIEW_GetNumberOfWorkAreas */
4179
4180 /***
4181  * DESCRIPTION:
4182  * Retrieves the origin coordinates when in icon or small icon display mode.
4183  * 
4184  * PARAMETER(S):
4185  * [I] HWND : window handle
4186  * [O] LPPOINT : coordinate information
4187  * 
4188  * RETURN:
4189  *   SUCCESS : TRUE
4190  *   FAILURE : FALSE
4191  */
4192 static LRESULT LISTVIEW_GetOrigin(HWND hwnd, LPPOINT lpptOrigin)
4193 {
4194   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4195   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
4196   UINT uView = lStyle & LVS_TYPEMASK;
4197   INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4198   INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4199   BOOL bResult = FALSE;
4200   
4201   TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd, lpptOrigin);
4202
4203   if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4204   {
4205     SCROLLINFO scrollInfo;
4206     ZeroMemory(lpptOrigin, sizeof(POINT));
4207     ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
4208     scrollInfo.cbSize = sizeof(SCROLLINFO);
4209
4210     if (lStyle & WS_HSCROLL)
4211     {
4212       scrollInfo.fMask = SIF_POS;
4213       if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE) 
4214       {
4215         lpptOrigin->x = -scrollInfo.nPos * max(nListWidth / 10, 1);
4216       }
4217     }
4218
4219     if (lStyle & WS_VSCROLL)
4220     {
4221       scrollInfo.fMask = SIF_POS;
4222       if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4223       {
4224         lpptOrigin->y = -scrollInfo.nPos * max(nListHeight / 10, 1);
4225       }
4226     }
4227       
4228     bResult = TRUE;
4229   }
4230   
4231   return bResult;
4232 }
4233
4234 /***
4235  * DESCRIPTION:
4236  * Retrieves the number of items that are marked as selected.
4237  * 
4238  * PARAMETER(S):
4239  * [I] HWND : window handle
4240  * 
4241  * RETURN:
4242  * Number of items selected.
4243  */
4244 static LRESULT LISTVIEW_GetSelectedCount(HWND hwnd)
4245 {
4246   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4247   INT nSelectedCount = 0;
4248   INT i;
4249
4250   for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
4251   {
4252     if (ListView_GetItemState(hwnd, i, LVIS_SELECTED) & LVIS_SELECTED)
4253     {
4254       nSelectedCount++;
4255     }
4256   }
4257   
4258   return nSelectedCount;
4259 }
4260
4261 /***
4262  * DESCRIPTION:
4263  * Retrieves item index that marks the start of a multiple selection.
4264  * 
4265  * PARAMETER(S):
4266  * [I] HWND : window handle
4267  * 
4268  * RETURN:
4269  * Index number or -1 if there is no selection mark.
4270  */
4271 static LRESULT LISTVIEW_GetSelectionMark(HWND hwnd)
4272 {
4273   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4274
4275   return infoPtr->nSelectionMark;
4276 }
4277
4278 /***
4279  * DESCRIPTION:
4280  * Retrieves the width of a string.
4281  * 
4282  * PARAMETER(S):
4283  * [I] HWND : window handle
4284  * 
4285  * RETURN:
4286  *   SUCCESS : string width (in pixels)
4287  *   FAILURE : zero
4288  */
4289 static LRESULT LISTVIEW_GetStringWidthA(HWND hwnd, LPCSTR lpszText)
4290 {
4291   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4292   HFONT hFont, hOldFont;
4293   SIZE stringSize;
4294   HDC hdc;
4295
4296   ZeroMemory(&stringSize, sizeof(SIZE));
4297   if (lpszText != NULL)
4298   {
4299     hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
4300     hdc = GetDC(hwnd);
4301     hOldFont = SelectObject(hdc, hFont);
4302     GetTextExtentPointA(hdc, lpszText, lstrlenA(lpszText), &stringSize);
4303     SelectObject(hdc, hOldFont);
4304     ReleaseDC(hwnd, hdc);
4305   }
4306
4307   return stringSize.cx;
4308 }
4309
4310 /***
4311  * DESCRIPTION:
4312  * Retrieves the text backgound color.
4313  * 
4314  * PARAMETER(S):
4315  * [I] HWND : window handle
4316  * 
4317  * RETURN:
4318  * COLORREF associated with the the background.
4319  */
4320 static LRESULT LISTVIEW_GetTextBkColor(HWND hwnd)
4321 {
4322   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
4323
4324   return infoPtr->clrTextBk;
4325 }
4326
4327 /***
4328  * DESCRIPTION:
4329  * Retrieves the text color.
4330  * 
4331  * PARAMETER(S):
4332  * [I] HWND : window handle
4333  * 
4334  * RETURN:
4335  * COLORREF associated with the text.
4336  */
4337 static LRESULT LISTVIEW_GetTextColor(HWND hwnd)
4338 {
4339   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
4340
4341   return infoPtr->clrText;
4342 }
4343
4344 /***
4345  * DESCRIPTION:
4346  * Determines which section of the item was selected (if any).
4347  * 
4348  * PARAMETER(S):
4349  * [I] HWND : window handle
4350  * [IO] LPLVHITTESTINFO : hit test information
4351  *
4352  * RETURN:
4353  *   SUCCESS : item index
4354  *   FAILURE : -1
4355  */
4356 static INT LISTVIEW_HitTestItem(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
4357 {
4358   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4359   RECT rcItem;
4360   INT i;
4361   
4362   TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
4363         lpHitTestInfo->pt.y);
4364
4365   for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
4366   {
4367     rcItem.left = LVIR_BOUNDS;
4368     if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
4369     {
4370       if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
4371       {
4372         rcItem.left = LVIR_ICON;
4373         if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
4374         {
4375           if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
4376           {
4377             lpHitTestInfo->flags = LVHT_ONITEMICON;
4378             lpHitTestInfo->iItem = i;
4379             lpHitTestInfo->iSubItem = 0;
4380             return i;
4381           }
4382         }
4383       
4384         rcItem.left = LVIR_LABEL;
4385         if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) != FALSE)
4386         {
4387           if (PtInRect(&rcItem, lpHitTestInfo->pt) != FALSE)
4388           {
4389             lpHitTestInfo->flags = LVHT_ONITEMLABEL;
4390             lpHitTestInfo->iItem = i;
4391             lpHitTestInfo->iSubItem = 0;
4392             return i;
4393           }
4394         }
4395         
4396         lpHitTestInfo->flags = LVHT_ONITEMSTATEICON;
4397         lpHitTestInfo->iItem = i;
4398         lpHitTestInfo->iSubItem = 0;
4399         return i;
4400       }
4401     }
4402   }
4403      
4404   lpHitTestInfo->flags = LVHT_NOWHERE;
4405
4406   return -1;
4407 }
4408
4409 /***
4410  * DESCRIPTION:
4411  * Determines which listview item is located at the specified position.
4412  * 
4413  * PARAMETER(S):
4414  * [I] HWND : window handle
4415  * [IO} LPLVHITTESTINFO : hit test information
4416  *
4417  * RETURN:
4418  *   SUCCESS : item index
4419  *   FAILURE : -1
4420  */
4421 static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
4422 {
4423   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4424   INT nItem = -1;
4425
4426   lpHitTestInfo->flags = 0;
4427
4428   if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
4429   {
4430     lpHitTestInfo->flags = LVHT_TOLEFT;
4431   }
4432   else if (infoPtr->rcList.right < lpHitTestInfo->pt.x) 
4433   {
4434     lpHitTestInfo->flags = LVHT_TORIGHT;
4435   }
4436   if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
4437   {
4438     lpHitTestInfo->flags |= LVHT_ABOVE;
4439   }
4440   else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y) 
4441   {
4442     lpHitTestInfo->flags |= LVHT_BELOW;
4443   }
4444
4445   if (lpHitTestInfo->flags == 0)
4446   {
4447     nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo);
4448   }
4449   
4450   return nItem;
4451 }
4452
4453 /***
4454  * DESCRIPTION:
4455  * Inserts a new column.
4456  * 
4457  * PARAMETER(S):
4458  * [I] HWND : window handle
4459  * [I] INT : column index
4460  * [I] LPLVCOLUMNA : column information
4461  *
4462  * RETURN:
4463  *   SUCCESS : new column index
4464  *   FAILURE : -1
4465  */
4466 static LRESULT LISTVIEW_InsertColumnA(HWND hwnd, INT nColumn, 
4467                                       LPLVCOLUMNA lpColumn)
4468 {
4469   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4470   HDITEMA hdi;
4471   INT nNewColumn = -1;
4472
4473   TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn, 
4474         lpColumn);
4475
4476   if (lpColumn != NULL) 
4477   {
4478     /* initialize memory */
4479     ZeroMemory(&hdi, sizeof(HDITEMA));
4480
4481     if (lpColumn->mask & LVCF_FMT) 
4482     {
4483       /* format member is valid */
4484       hdi.mask |= HDI_FORMAT;
4485
4486       /* set text alignment (leftmost column must be left-aligned) */
4487       if (nColumn == 0)
4488       {
4489         hdi.fmt |= HDF_LEFT;
4490       }
4491       else
4492       {
4493         if (lpColumn->fmt & LVCFMT_LEFT)
4494         {
4495           hdi.fmt |= HDF_LEFT;
4496         }
4497         else if (lpColumn->fmt & LVCFMT_RIGHT)
4498         {
4499           hdi.fmt |= HDF_RIGHT;
4500         }
4501         else if (lpColumn->fmt & LVCFMT_CENTER)
4502         {
4503           hdi.fmt |= HDF_CENTER;
4504         }
4505       }
4506  
4507       if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
4508       {
4509         hdi.fmt |= HDF_BITMAP_ON_RIGHT;
4510         /* ??? */
4511       }
4512
4513       if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
4514       {
4515         /* ??? */
4516       }
4517       
4518       if (lpColumn->fmt & LVCFMT_IMAGE)
4519       {
4520         hdi.fmt |= HDF_IMAGE;
4521         hdi.iImage = I_IMAGECALLBACK;
4522       }
4523     }
4524
4525     if (lpColumn->mask & LVCF_WIDTH) 
4526     {
4527       hdi.mask |= HDI_WIDTH;
4528       hdi.cxy = lpColumn->cx;
4529     }
4530   
4531     if (lpColumn->mask & LVCF_TEXT) 
4532     {
4533       hdi.mask |= HDI_TEXT | HDI_FORMAT;
4534       hdi.pszText = lpColumn->pszText;
4535       hdi.cchTextMax = lstrlenA(lpColumn->pszText);
4536       hdi.fmt |= HDF_STRING;
4537     }
4538   
4539     if (lpColumn->mask & LVCF_IMAGE) 
4540     {
4541       hdi.mask |= HDI_IMAGE;
4542       hdi.iImage = lpColumn->iImage;
4543     }
4544
4545     if (lpColumn->mask & LVCF_ORDER) 
4546     {
4547       hdi.mask |= HDI_ORDER;
4548       hdi.iOrder = lpColumn->iOrder;
4549     }
4550
4551     /* insert item in header control */
4552     nNewColumn = SendMessageA(infoPtr->hwndHeader, HDM_INSERTITEMA,
4553                              (WPARAM)nColumn, (LPARAM)&hdi);
4554
4555     LISTVIEW_UpdateScroll(hwnd);
4556     InvalidateRect(hwnd, NULL, FALSE);
4557   }
4558
4559   return nNewColumn;
4560 }
4561
4562 static LRESULT LISTVIEW_InsertColumnW(HWND hwnd, INT nColumn, 
4563                                       LPLVCOLUMNW lpColumn)
4564 {
4565   LVCOLUMNA     lvca;
4566   LRESULT               lres;
4567       
4568   memcpy(&lvca,lpColumn,sizeof(lvca));
4569   if (lpColumn->mask & LVCF_TEXT)
4570     lvca.pszText = HEAP_strdupWtoA(GetProcessHeap(),0,lpColumn->pszText);
4571   lres = LISTVIEW_InsertColumnA(hwnd,nColumn,&lvca);
4572   if (lpColumn->mask & LVCF_TEXT)
4573     HeapFree(GetProcessHeap(),0,lvca.pszText);
4574   return lres;
4575 }
4576
4577
4578 /***
4579  * DESCRIPTION:
4580  * Inserts a new item in the listview control.
4581  * 
4582  * PARAMETER(S):
4583  * [I] HWND : window handle
4584  * [I] LPLVITEMA : item information
4585  *
4586  * RETURN:
4587  *   SUCCESS : new item index
4588  *   FAILURE : -1
4589  */
4590 static LRESULT LISTVIEW_InsertItemA(HWND hwnd, LPLVITEMA lpLVItem)
4591 {
4592   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4593   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
4594   UINT uView = lStyle & LVS_TYPEMASK;
4595   LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
4596   NMLISTVIEW nmlv;
4597   INT nItem = -1;
4598   HDPA hdpaSubItems;
4599   LISTVIEW_ITEM *lpItem = NULL;
4600
4601   TRACE("(hwnd=%x,lpLVItem=%p)\n", hwnd, lpLVItem);
4602
4603   if (lpLVItem != NULL)
4604   {
4605     /* make sure it's not a subitem; cannot insert a subitem */
4606     if (lpLVItem->iSubItem == 0)
4607     {
4608       lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM));
4609       if (lpItem != NULL)
4610       {
4611         ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
4612         if (LISTVIEW_InitItem(hwnd, lpItem, lpLVItem) != FALSE)
4613         {
4614           /* insert item in listview control data structure */
4615           hdpaSubItems = DPA_Create(8);
4616           if (hdpaSubItems != NULL)
4617           {
4618             nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem);
4619             if (nItem != -1)
4620             {
4621               nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem, 
4622                                     hdpaSubItems);
4623               if (nItem != -1)
4624               {
4625                 /* manage item focus */
4626                 if (lpLVItem->mask & LVIF_STATE)
4627                 {
4628                   if (lpLVItem->stateMask & LVIS_FOCUSED)
4629                   {
4630                     LISTVIEW_SetItemFocus(hwnd, nItem);
4631                   }           
4632                 }
4633                 
4634                 /* send LVN_INSERTITEM notification */
4635                 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4636                 nmlv.hdr.hwndFrom = hwnd;
4637                 nmlv.hdr.idFrom = lCtrlId;
4638                 nmlv.hdr.code = LVN_INSERTITEM;
4639                 nmlv.iItem = nItem;
4640                 nmlv.lParam = lpItem->lParam;;
4641                 ListView_LVNotify(GetParent(hwnd), lCtrlId, &nmlv);
4642                 
4643                 /* align items (set position of each item) */
4644                 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4645                 {
4646                   if (lStyle & LVS_ALIGNLEFT)
4647                   {
4648                     LISTVIEW_AlignLeft(hwnd);
4649                   }
4650                   else
4651                   {
4652                     LISTVIEW_AlignTop(hwnd);
4653                   }
4654                 }
4655                 
4656                 LISTVIEW_UpdateScroll(hwnd);
4657                 /* refresh client area */
4658                 InvalidateRect(hwnd, NULL, FALSE);
4659               }
4660             }
4661           }
4662         }
4663       }
4664     }
4665   }
4666
4667   /* free memory if unsuccessful */
4668   if ((nItem == -1) && (lpItem != NULL))
4669   {
4670     COMCTL32_Free(lpItem);
4671   }
4672   
4673   return nItem;
4674 }
4675
4676 static LRESULT LISTVIEW_InsertItemW(HWND hwnd, LPLVITEMW lpLVItem) {
4677   LVITEMA lvia;
4678   LRESULT lres;
4679
4680   memcpy(&lvia,lpLVItem,sizeof(LVITEMA));
4681   if (lvia.mask & LVIF_TEXT) {
4682     if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
4683       lvia.pszText = LPSTR_TEXTCALLBACKA;
4684     else
4685       lvia.pszText = HEAP_strdupWtoA(GetProcessHeap(),0,lpLVItem->pszText);
4686   }
4687   lres = LISTVIEW_InsertItemA(hwnd, &lvia);
4688   if (lvia.mask & LVIF_TEXT) {
4689     if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW)
4690       HeapFree(GetProcessHeap(),0,lvia.pszText);
4691   }
4692   return lres;
4693 }
4694
4695 /* LISTVIEW_InsertItemW */
4696
4697 /***
4698  * DESCRIPTION:
4699  * Redraws a range of items.
4700  * 
4701  * PARAMETER(S):
4702  * [I] HWND : window handle
4703  * [I] INT : first item
4704  * [I] INT : last item
4705  *
4706  * RETURN:
4707  *   SUCCESS : TRUE
4708  *   FAILURE : FALSE
4709  */
4710 static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast)
4711 {
4712   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0); 
4713   BOOL bResult = FALSE;
4714   RECT rc;
4715
4716   if (nFirst <= nLast)
4717   {
4718     if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
4719     {
4720       if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
4721       {
4722         /* bResult = */
4723         InvalidateRect(hwnd, &rc, FALSE);
4724       }
4725     }
4726   }
4727
4728   return bResult;
4729 }
4730
4731 /* LISTVIEW_Scroll */
4732
4733 /***
4734  * DESCRIPTION:
4735  * Sets the background color.
4736  * 
4737  * PARAMETER(S):
4738  * [I] HWND : window handle
4739  * [I] COLORREF : background color
4740  *
4741  * RETURN:
4742  *   SUCCESS : TRUE
4743  *   FAILURE : FALSE
4744  */
4745 static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk)
4746 {
4747   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4748
4749   infoPtr->clrBk = clrBk;
4750   InvalidateRect(hwnd, NULL, TRUE);
4751   
4752   return TRUE;
4753 }
4754
4755 /* LISTVIEW_SetBkImage */
4756
4757 /***
4758  * DESCRIPTION:
4759  * Sets the callback mask. This mask will be used when the parent
4760  * window stores state information (some or all).
4761  * 
4762  * PARAMETER(S):
4763  * [I] HWND : window handle
4764  * [I] UINT : state mask
4765  *
4766  * RETURN:
4767  *   SUCCESS : TRUE
4768  *   FAILURE : FALSE
4769  */
4770 static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask)
4771 {
4772   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4773
4774   infoPtr->uCallbackMask = uMask;
4775
4776   return TRUE;
4777 }
4778
4779 /***
4780  * DESCRIPTION:
4781  * Sets the attributes of a header item.
4782  * 
4783  * PARAMETER(S):
4784  * [I] HWND : window handle
4785  * [I] INT : column index
4786  * [I] LPLVCOLUMNA : column attributes
4787  *
4788  * RETURN:
4789  *   SUCCESS : TRUE
4790  *   FAILURE : FALSE
4791  */
4792 static LRESULT LISTVIEW_SetColumnA(HWND hwnd, INT nColumn, 
4793                                    LPLVCOLUMNA lpColumn)
4794 {
4795   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
4796   BOOL bResult = FALSE;
4797   HDITEMA hdi, hdiget;
4798
4799   if ((lpColumn != NULL) && (nColumn >= 0) && 
4800       (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
4801   {
4802     /* initialize memory */
4803     ZeroMemory(&hdi, sizeof(HDITEMA));
4804
4805     if (lpColumn->mask & LVCF_FMT) 
4806     {
4807       /* format member is valid */
4808       hdi.mask |= HDI_FORMAT;
4809
4810       /* get current format first */
4811       hdiget.mask = HDI_FORMAT;
4812       if (Header_GetItemA(infoPtr->hwndHeader, nColumn, &hdiget))
4813               /* preserve HDF_STRING if present */
4814               hdi.fmt = hdiget.fmt & HDF_STRING;
4815
4816       /* set text alignment (leftmost column must be left-aligned) */
4817       if (nColumn == 0)
4818       {
4819         hdi.fmt |= HDF_LEFT;
4820       }
4821       else
4822       {
4823         if (lpColumn->fmt & LVCFMT_LEFT)
4824         {
4825           hdi.fmt |= HDF_LEFT;
4826         }
4827         else if (lpColumn->fmt & LVCFMT_RIGHT)
4828         {
4829           hdi.fmt |= HDF_RIGHT;
4830         }
4831         else if (lpColumn->fmt & LVCFMT_CENTER)
4832         {
4833           hdi.fmt |= HDF_CENTER;
4834         }
4835       }
4836       
4837       if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
4838       {
4839         hdi.fmt |= HDF_BITMAP_ON_RIGHT;
4840       }
4841
4842       if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
4843       {
4844         hdi.fmt |= HDF_IMAGE;
4845       }
4846       
4847       if (lpColumn->fmt & LVCFMT_IMAGE)
4848       {
4849         hdi.fmt |= HDF_IMAGE;
4850         hdi.iImage = I_IMAGECALLBACK;
4851       }
4852     }
4853
4854     if (lpColumn->mask & LVCF_WIDTH) 
4855     {
4856       hdi.mask |= HDI_WIDTH;
4857       hdi.cxy = lpColumn->cx;
4858     }
4859     
4860     if (lpColumn->mask & LVCF_TEXT) 
4861     {
4862       hdi.mask |= HDI_TEXT | HDI_FORMAT;
4863       hdi.pszText = lpColumn->pszText;
4864       hdi.cchTextMax = lstrlenA(lpColumn->pszText);
4865       hdi.fmt |= HDF_STRING;
4866     }
4867   
4868     if (lpColumn->mask & LVCF_IMAGE) 
4869     {
4870       hdi.mask |= HDI_IMAGE;
4871       hdi.iImage = lpColumn->iImage;
4872     }
4873
4874     if (lpColumn->mask & LVCF_ORDER) 
4875     {
4876       hdi.mask |= HDI_ORDER;
4877       hdi.iOrder = lpColumn->iOrder;
4878     }
4879
4880     /* set header item attributes */
4881     bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
4882   }
4883   
4884   return bResult;
4885 }
4886
4887 /* LISTVIEW_SetColumnW */
4888 /* LISTVIEW_SetColumnOrderArray */
4889
4890 /***
4891  * DESCRIPTION:
4892  * Sets the width of a column
4893  *
4894  * PARAMETERS:
4895  * [I] HWND : window handle
4896  * [I] INT : column index
4897  * [I] INT : column width
4898  *
4899  * RETURN:
4900  *   SUCCESS : TRUE
4901  *   FAILURE : FALSE
4902  */
4903 static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx)
4904 {
4905     LISTVIEW_INFO *infoPtr;
4906     HDITEMA hdi;
4907     LRESULT lret;
4908     LONG lStyle;
4909
4910     /* set column width only if in report mode */
4911     lStyle = GetWindowLongA(hwnd, GWL_STYLE);
4912     if ((lStyle & LVS_TYPEMASK) != LVS_REPORT)
4913         return (FALSE);
4914
4915     /* make sure we can get the listview info */
4916     if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
4917         return (FALSE);  
4918     if (!infoPtr->hwndHeader) /* make sure we have a header */
4919         return (FALSE); 
4920         
4921     /* FIXME: currently ignoring LVSCW_AUTOSIZE (-1) and
4922           * LVSCV_AUTOSIZE_USEHEADER (-2)
4923           */
4924     if (cx < 0) 
4925         return (FALSE);
4926         
4927     hdi.mask = HDI_WIDTH;
4928     hdi.cxy = cx;
4929
4930     /* call header to update the column change */
4931     lret = Header_SetItemA(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
4932
4933     infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
4934
4935     InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
4936
4937     return lret;
4938 }
4939
4940 /***
4941  * DESCRIPTION:
4942  * Sets the extended listview style.
4943  *
4944  * PARAMETERS:
4945  * [I] HWND  : window handle
4946  * [I] DWORD : mask
4947  * [I] DWORD : style
4948  *
4949  * RETURN:
4950  *   SUCCESS : previous style
4951  *   FAILURE : 0
4952  */
4953 static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle)
4954 {
4955     LISTVIEW_INFO *infoPtr;
4956     DWORD dwOldStyle;
4957
4958     /* make sure we can get the listview info */
4959     if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
4960         return (0);
4961
4962     /* store previous style */
4963     dwOldStyle = infoPtr->dwExStyle;
4964
4965     /* set new style */
4966     infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
4967
4968     return (dwOldStyle);
4969 }
4970
4971 /* LISTVIEW_SetHotCursor */
4972
4973 /***
4974  * DESCRIPTION:
4975  * Sets the hot item index.
4976  *
4977  * PARAMETERS:
4978  * [I] HWND  : window handle
4979  * [I] INT   : index
4980  *
4981  * RETURN:
4982  *   SUCCESS : previous hot item index
4983  *   FAILURE : -1 (no hot item)
4984  */
4985 static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex)
4986 {
4987     LISTVIEW_INFO *infoPtr;
4988     INT iOldIndex;
4989
4990     /* make sure we can get the listview info */
4991     if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0)))
4992         return (-1);
4993
4994     /* store previous index */
4995     iOldIndex = infoPtr->nHotItem;
4996
4997     /* set new style */
4998     infoPtr->nHotItem = iIndex;
4999
5000     return (iOldIndex);
5001 }
5002
5003 /* LISTVIEW_SetIconSpacing */
5004
5005 /***
5006  * DESCRIPTION:
5007  * Sets image lists.
5008  * 
5009  * PARAMETER(S):
5010  * [I] HWND : window handle
5011  * [I] INT : image list type  
5012  * [I] HIMAGELIST : image list handle
5013  *
5014  * RETURN:
5015  *   SUCCESS : old image list
5016  *   FAILURE : NULL
5017  */
5018 static LRESULT LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
5019 {
5020   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5021   HIMAGELIST himlTemp = 0;
5022
5023   switch (nType) 
5024   {
5025   case LVSIL_NORMAL:
5026     himlTemp = infoPtr->himlNormal;
5027     infoPtr->himlNormal = himl;
5028     return (LRESULT)himlTemp;
5029
5030   case LVSIL_SMALL:
5031     himlTemp = infoPtr->himlSmall;
5032     infoPtr->himlSmall = himl;
5033     return (LRESULT)himlTemp;
5034
5035   case LVSIL_STATE:
5036     himlTemp = infoPtr->himlState;
5037     infoPtr->himlState = himl;
5038     ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
5039     return (LRESULT)himlTemp;
5040   }
5041
5042   return (LRESULT)NULL;
5043 }
5044
5045
5046 /***
5047  * DESCRIPTION:
5048  * Sets the attributes of an item.
5049  * 
5050  * PARAMETER(S):
5051  * [I] HWND : window handle
5052  * [I] LPLVITEM : item information 
5053  *
5054  * RETURN:
5055  *   SUCCESS : TRUE
5056  *   FAILURE : FALSE
5057  */
5058 static LRESULT LISTVIEW_SetItemA(HWND hwnd, LPLVITEMA lpLVItem)
5059 {
5060   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5061   BOOL bResult = FALSE;
5062
5063   if (lpLVItem != NULL)
5064   {
5065     if ((lpLVItem->iItem >= 0) && (lpLVItem->iItem < GETITEMCOUNT(infoPtr)))
5066     {
5067       if (lpLVItem->iSubItem == 0)
5068       {
5069         bResult = LISTVIEW_SetItem(hwnd, lpLVItem);
5070       }
5071       else
5072       {
5073         bResult = LISTVIEW_SetSubItem(hwnd, lpLVItem);
5074       }
5075     }
5076   }
5077
5078
5079   return bResult;
5080 }
5081
5082 /* LISTVIEW_SetItemW  */
5083
5084 /***
5085  * DESCRIPTION:
5086  * Preallocates memory.
5087  * 
5088  * PARAMETER(S):
5089  * [I] HWND : window handle
5090  * [I] INT : item count (prjected number of items)
5091  * [I] DWORD : update flags
5092  *
5093  * RETURN:
5094  *   SUCCESS : TRUE
5095  *   FAILURE : FALSE
5096  */
5097 static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags)
5098 {
5099   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
5100
5101   FIXME("(%d %08lx)empty stub!\n", nItems, dwFlags);
5102
5103   if (nItems == 0)
5104     return LISTVIEW_DeleteAllItems (hwnd);
5105
5106   if (nItems > GETITEMCOUNT(infoPtr))
5107 {
5108     /* append items */
5109     FIXME("append items\n");
5110
5111   }
5112   else if (nItems < GETITEMCOUNT(infoPtr))
5113   {
5114     /* remove items */
5115     FIXME("remove items\n");
5116
5117   }
5118
5119   return TRUE;
5120 }
5121
5122 /***
5123  * DESCRIPTION:
5124  * Sets the position of an item.
5125  * 
5126  * PARAMETER(S):
5127  * [I] HWND : window handle
5128  * [I] INT : item index
5129  * [I] INT : x coordinate
5130  * [I] INT : y coordinate
5131  *
5132  * RETURN:
5133  *   SUCCESS : TRUE
5134  *   FAILURE : FALSE
5135  */
5136 static BOOL LISTVIEW_SetItemPosition(HWND hwnd, INT nItem, 
5137                                      INT nPosX, INT nPosY)
5138 {
5139   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
5140   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5141   LISTVIEW_ITEM *lpItem;
5142   HDPA hdpaSubItems;
5143   BOOL bResult = FALSE;
5144
5145   TRACE("(hwnd=%x,nItem=%d,X=%d,Y=%d)\n", hwnd, nItem, nPosX, nPosY);
5146
5147   if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
5148   {
5149     if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
5150     {
5151       hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
5152       if (hdpaSubItems != NULL)
5153       {
5154         lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
5155         if (lpItem != NULL)
5156         {
5157           bResult = TRUE;
5158           lpItem->ptPosition.x = nPosX;
5159           lpItem->ptPosition.y = nPosY;
5160         }
5161       }
5162     }
5163   }
5164
5165   return bResult;
5166 }
5167
5168 /* LISTVIEW_SetItemPosition32 */
5169
5170 /***
5171  * DESCRIPTION:
5172  * Sets the state of one or many items.
5173  * 
5174  * PARAMETER(S):
5175  * [I] HWND : window handle
5176  * [I]INT : item index
5177  * [I] LPLVITEM : item or subitem info
5178  *
5179  * RETURN:
5180  *   SUCCESS : TRUE
5181  *   FAILURE : FALSE
5182  */
5183 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
5184 {
5185   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5186   BOOL bResult = FALSE;
5187   LVITEMA lvItem;
5188   INT i;
5189
5190   if (nItem == -1)
5191   {
5192     bResult = TRUE;
5193     ZeroMemory(&lvItem, sizeof(LVITEMA));
5194     lvItem.mask = LVIF_STATE;
5195     lvItem.state = lpLVItem->state;
5196     lvItem.stateMask = lpLVItem->stateMask ;
5197     
5198     /* apply to all items */
5199     for (i = 0; i< GETITEMCOUNT(infoPtr); i++)
5200     {
5201       lvItem.iItem = i;
5202       if (ListView_SetItemA(hwnd, &lvItem) == FALSE)
5203       {
5204         bResult = FALSE;
5205       }
5206     }
5207   }
5208   else
5209   {
5210     ZeroMemory(&lvItem, sizeof(LVITEMA));
5211     lvItem.mask = LVIF_STATE;
5212     lvItem.state = lpLVItem->state;
5213     lvItem.stateMask = lpLVItem->stateMask;
5214     lvItem.iItem = nItem;
5215     bResult = ListView_SetItemA(hwnd, &lvItem);
5216   }
5217
5218   return bResult;
5219 }
5220
5221 /***
5222  * DESCRIPTION:
5223  * Sets the text of an item or subitem.
5224  * 
5225  * PARAMETER(S):
5226  * [I] HWND : window handle
5227  * [I] INT : item index
5228  * [I] LPLVITEMA : item or subitem info
5229  *
5230  * RETURN:
5231  *   SUCCESS : TRUE
5232  *   FAILURE : FALSE
5233  */
5234 static BOOL LISTVIEW_SetItemTextA(HWND hwnd, INT nItem, LPLVITEMA lpLVItem)
5235 {
5236   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5237   BOOL bResult = FALSE;
5238   LVITEMA lvItem;
5239
5240   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
5241   {
5242     ZeroMemory(&lvItem, sizeof(LVITEMA));
5243     lvItem.mask = LVIF_TEXT;
5244     lvItem.pszText = lpLVItem->pszText;
5245     lvItem.iItem = nItem;
5246     lvItem.iSubItem = lpLVItem->iSubItem;
5247     bResult = ListView_SetItemA(hwnd, &lvItem);
5248   }
5249   
5250   return bResult;
5251 }
5252
5253 /* LISTVIEW_SetItemTextW */
5254
5255 /***
5256  * DESCRIPTION:
5257  * Set item index that marks the start of a multiple selection.
5258  *
5259  * PARAMETER(S):
5260  * [I] HWND : window handle
5261  * [I] INT  : index
5262  *
5263  * RETURN:
5264  * Index number or -1 if there is no selection mark.
5265  */
5266 static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex)
5267 {
5268   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5269   INT nOldIndex = infoPtr->nSelectionMark;
5270
5271   infoPtr->nSelectionMark = nIndex;
5272
5273   return nOldIndex;
5274 }
5275
5276 /***
5277  * DESCRIPTION:
5278  * Sets the text background color.
5279  * 
5280  * PARAMETER(S):
5281  * [I] HWND : window handle
5282  * [I] COLORREF : text background color
5283  *
5284  * RETURN:
5285  *   SUCCESS : TRUE
5286  *   FAILURE : FALSE
5287  */
5288 static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk)
5289 {
5290   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5291
5292   infoPtr->clrTextBk = clrTextBk;
5293   InvalidateRect(hwnd, NULL, TRUE);
5294
5295   return TRUE;
5296 }
5297
5298 /***
5299  * DESCRIPTION:
5300  * Sets the text foreground color.
5301  * 
5302  * PARAMETER(S):
5303  * [I] HWND : window handle
5304  * [I] COLORREF : text color 
5305  *
5306  * RETURN:
5307  *   SUCCESS : TRUE
5308  *   FAILURE : FALSE
5309  */
5310 static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText)
5311 {
5312   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5313
5314   infoPtr->clrText = clrText;
5315   InvalidateRect(hwnd, NULL, TRUE);
5316
5317   return TRUE;
5318 }
5319
5320 /* LISTVIEW_SetToolTips */
5321 /* LISTVIEW_SetUnicodeFormat */
5322 /* LISTVIEW_SetWorkAreas */
5323
5324 /***
5325  * DESCRIPTION:
5326  * Callback internally used by LISTVIEW_SortItems()
5327  * 
5328  * PARAMETER(S):
5329  * [I] LPVOID : first LISTVIEW_ITEM to compare
5330  * [I] LPVOID : second LISTVIEW_ITEM to compare
5331  * [I] LPARAM : HWND of control
5332  *
5333  * RETURN:
5334  *   if first comes before second : negative
5335  *   if first comes after second : positive
5336  *   if first and second are equivalent : zero
5337  */
5338 static INT WINAPI LISTVIEW_CallBackCompare( 
5339   LPVOID first, 
5340   LPVOID second, 
5341   LPARAM lParam)
5342 {
5343   /* Forward the call to the client defined callback */
5344   HWND hwnd = (HWND)lParam;
5345   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5346
5347   return (infoPtr->pfnCompare)(
5348     ((LISTVIEW_ITEM *)first)->lParam,
5349     ((LISTVIEW_ITEM *)second)->lParam,
5350     infoPtr->lParamSort);
5351 }
5352
5353 /***
5354  * DESCRIPTION:
5355  * Sorts the listview items.
5356  * 
5357  * PARAMETER(S):
5358  * [I] HWND : window handle
5359  * [I] WPARAM : application-defined value
5360  * [I] LPARAM : pointer to comparision callback
5361  *
5362  * RETURN:
5363  *   SUCCESS : TRUE
5364  *   FAILURE : FALSE
5365  */
5366 static LRESULT LISTVIEW_SortItems(HWND hwnd, WPARAM wParam, LPARAM lParam)
5367 {
5368     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5369     HDPA hdpaSubItems;
5370     LISTVIEW_ITEM *lpItem;
5371     int nCount, i;
5372     HDPA sortList;
5373     
5374     if (!infoPtr || !infoPtr->hdpaItems)
5375         return FALSE;
5376     
5377     nCount = GETITEMCOUNT(infoPtr);
5378     /* if there are 0 or 1 items, there is no need to sort */
5379     if (nCount > 1)
5380     {
5381         sortList = DPA_Create(nCount);
5382
5383         infoPtr->pfnCompare = (PFNLVCOMPARE)lParam;
5384         infoPtr->lParamSort = (LPARAM)wParam;
5385         
5386         /* append pointers one by one to sortList */
5387         for (i = 0; i < nCount; i++)
5388         {
5389             if ((hdpaSubItems = (HDPA) DPA_GetPtr(infoPtr->hdpaItems, i)))
5390                 if ((lpItem = (LISTVIEW_ITEM *) DPA_GetPtr(hdpaSubItems, 0)))
5391                     DPA_InsertPtr(sortList, nCount + 1, lpItem);
5392         }
5393
5394         /* sort the sortList */
5395         DPA_Sort(sortList, LISTVIEW_CallBackCompare, hwnd);
5396
5397         /* copy the pointers back */
5398         for (i = 0; i < nCount; i++)
5399         {
5400             if ((hdpaSubItems = (HDPA) DPA_GetPtr(infoPtr->hdpaItems, i)) &&
5401                 (lpItem = (LISTVIEW_ITEM *) DPA_GetPtr(sortList, i)))
5402                 DPA_SetPtr(hdpaSubItems, 0, lpItem);
5403         }
5404
5405         DPA_Destroy(sortList);
5406     }
5407
5408     return TRUE;
5409 }
5410
5411 /* LISTVIEW_SubItemHitTest */
5412
5413 /***
5414  * DESCRIPTION:
5415  * Updates an items or rearranges the listview control.
5416  * 
5417  * PARAMETER(S):
5418  * [I] HWND : window handle
5419  * [I] INT : item index
5420  *
5421  * RETURN:
5422  *   SUCCESS : TRUE
5423  *   FAILURE : FALSE
5424  */
5425 static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
5426 {
5427   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5428   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
5429   BOOL bResult = FALSE;
5430   RECT rc;
5431
5432   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
5433   {
5434     bResult = TRUE;
5435
5436     /* rearrange with default alignment style */
5437     if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
5438         ((lStyle & LVS_TYPEMASK)  == LVS_SMALLICON)))
5439     {
5440       ListView_Arrange(hwnd, 0);
5441     }
5442     else
5443     {
5444       /* get item bounding rectangle */
5445       rc.left = LVIR_BOUNDS;
5446       ListView_GetItemRect(hwnd, nItem, &rc);
5447       InvalidateRect(hwnd, &rc, TRUE);
5448     }
5449   }
5450
5451   return bResult;
5452 }
5453
5454 /***
5455  * DESCRIPTION:
5456  * Creates the listview control.
5457  * 
5458  * PARAMETER(S):
5459  * [I] HWND : window handle
5460  *
5461  * RETURN:
5462  * Zero
5463  */
5464 static LRESULT LISTVIEW_Create(HWND hwnd, WPARAM wParam, LPARAM lParam)
5465 {
5466   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5467   LPCREATESTRUCTA lpcs = (LPCREATESTRUCTA)lParam;
5468   UINT uView = lpcs->style & LVS_TYPEMASK;
5469   LOGFONTA logFont;
5470
5471   /* initialize info pointer */
5472   ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
5473
5474   /* determine the type of structures to use */
5475   infoPtr->notifyFormat = SendMessageA(GetParent(hwnd), WM_NOTIFYFORMAT, 
5476                                        (WPARAM)hwnd, (LPARAM)NF_QUERY);
5477   if (infoPtr->notifyFormat != NFR_ANSI)
5478   {
5479     FIXME("ANSI notify format is NOT used\n");
5480   }
5481   
5482   /* initialize color information  */
5483   infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
5484   infoPtr->clrText = GetSysColor(COLOR_WINDOWTEXT);
5485   infoPtr->clrTextBk = GetSysColor(COLOR_WINDOW); 
5486
5487   /* set default values */
5488   infoPtr->uCallbackMask = 0;
5489   infoPtr->nFocusedItem = -1;
5490   infoPtr->nSelectionMark = -1;
5491   infoPtr->nHotItem = -1;
5492   infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
5493   infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
5494   ZeroMemory(&infoPtr->rcList, sizeof(RECT));
5495
5496   /* get default font (icon title) */
5497   SystemParametersInfoA(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
5498   infoPtr->hDefaultFont = CreateFontIndirectA(&logFont);
5499   infoPtr->hFont = infoPtr->hDefaultFont;
5500   
5501   /* create header */
5502   infoPtr->hwndHeader = CreateWindowA(WC_HEADERA, (LPCSTR)NULL, 
5503                                       WS_CHILD | HDS_HORZ | HDS_BUTTONS, 
5504                                       0, 0, 0, 0, hwnd, (HMENU)0, 
5505                                       lpcs->hInstance, NULL);
5506
5507   /* set header font */
5508   SendMessageA(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, 
5509                (LPARAM)TRUE);
5510   
5511   if (uView == LVS_ICON)
5512   {
5513     infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
5514     infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
5515   }
5516   else if (uView == LVS_REPORT)
5517   {
5518     if (!(LVS_NOCOLUMNHEADER & lpcs->style))
5519     {
5520       ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
5521     }
5522
5523     infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
5524     infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
5525   }
5526   else
5527   {
5528     infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
5529     infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
5530   }
5531
5532   /* display unsupported listview window styles */
5533   LISTVIEW_UnsupportedStyles(lpcs->style);
5534
5535   /* allocate memory for the data structure */
5536   infoPtr->hdpaItems = DPA_Create(10);
5537
5538   /* initialize size of items */
5539   infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
5540   infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
5541
5542   return 0;
5543 }
5544
5545 /***
5546  * DESCRIPTION:
5547  * Erases the background of the listview control.
5548  * 
5549  * PARAMETER(S):
5550  * [I] HWND : window handle
5551  * [I] WPARAM : device context handle
5552  * [I] LPARAM : not used
5553  * 
5554  * RETURN:
5555  *   SUCCESS : TRUE
5556  *   FAILURE : FALSE
5557  */
5558 static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam, 
5559                                         LPARAM lParam)
5560 {
5561   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5562   BOOL bResult;
5563
5564   if (infoPtr->clrBk == CLR_NONE) 
5565   {
5566     bResult = SendMessageA(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam);
5567   }
5568   else 
5569   {
5570     RECT rc;
5571     HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
5572     GetClientRect(hwnd, &rc);
5573     FillRect((HDC)wParam, &rc, hBrush);
5574     DeleteObject(hBrush);
5575     bResult = TRUE;
5576   }
5577
5578   return bResult;
5579 }
5580
5581 /***
5582  * DESCRIPTION:
5583  * Retrieves the listview control font.
5584  * 
5585  * PARAMETER(S):
5586  * [I] HWND : window handle
5587  *
5588  * RETURN:
5589  * Font handle.
5590  */
5591 static LRESULT LISTVIEW_GetFont(HWND hwnd)
5592 {
5593   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5594
5595   return infoPtr->hFont;
5596 }
5597
5598 /***
5599  * DESCRIPTION:
5600  * Performs vertical scrolling.
5601  * 
5602  * PARAMETER(S):
5603  * [I] HWND : window handle
5604  * [I] INT : scroll code
5605  * [I] SHORT : current scroll position if scroll code is SB_THIMBPOSITION 
5606  *             or SB_THUMBTRACK.
5607  * [I] HWND : scrollbar control window handle
5608  *
5609  * RETURN:
5610  * Zero
5611  */
5612 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
5613                                 HWND hScrollWnd)
5614 {
5615   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5616   SCROLLINFO scrollInfo;
5617
5618   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5619   scrollInfo.cbSize = sizeof(SCROLLINFO);
5620   scrollInfo.fMask = /*SIF_PAGE |*/ SIF_POS | SIF_RANGE;
5621  
5622   if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
5623   {
5624     INT nOldScrollPos = scrollInfo.nPos;
5625     switch (nScrollCode)
5626     {
5627     case SB_LINEUP:
5628       if (scrollInfo.nPos > scrollInfo.nMin)
5629       {
5630         scrollInfo.nPos--;
5631       }
5632     break;
5633     
5634     case SB_LINEDOWN:
5635       if (scrollInfo.nPos < scrollInfo.nMax)
5636       {
5637         scrollInfo.nPos++;
5638       }
5639       break;
5640       
5641     case SB_PAGEUP:
5642       if (scrollInfo.nPos > scrollInfo.nMin)
5643       {
5644         INT nPage = 0;
5645
5646         if (uView == LVS_REPORT)
5647         {
5648           nPage = LISTVIEW_GetCountPerColumn(hwnd);
5649         }
5650         else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
5651         {
5652           nPage = 10;
5653         }
5654
5655         if (scrollInfo.nPos >= nPage)
5656         {
5657           scrollInfo.nPos -= nPage;
5658         }
5659         else
5660         {
5661           scrollInfo.nPos = scrollInfo.nMin;
5662         }
5663       }
5664       break;
5665       
5666     case SB_PAGEDOWN:
5667       if (scrollInfo.nPos < scrollInfo.nMax)
5668       {
5669         INT nPage = 0;
5670
5671         if (uView == LVS_REPORT)
5672         {
5673           nPage = LISTVIEW_GetCountPerColumn(hwnd);
5674         }
5675         else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
5676         {
5677           nPage = 10;
5678         }
5679         
5680         if (scrollInfo.nPos <= scrollInfo.nMax - nPage)
5681         {
5682           scrollInfo.nPos += nPage;
5683         }
5684         else
5685         {
5686           scrollInfo.nPos = scrollInfo.nMax;
5687         }
5688       }
5689       break;
5690
5691     case SB_THUMBPOSITION:
5692       break;
5693     }
5694
5695     if (nOldScrollPos != scrollInfo.nPos)
5696     {
5697       scrollInfo.fMask = SIF_POS;
5698       SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
5699       InvalidateRect(hwnd, NULL, TRUE);
5700     }
5701   }
5702     
5703   return 0;
5704 }
5705
5706 /***
5707  * DESCRIPTION:
5708  * Performs horizontal scrolling.
5709  * 
5710  * PARAMETER(S):
5711  * [I] HWND : window handle
5712  * [I] INT : scroll code
5713  * [I] SHORT : current scroll position if scroll code is SB_THIMBPOSITION 
5714  *             or SB_THUMBTRACK.
5715  * [I] HWND : scrollbar control window handle
5716  *
5717  * RETURN:
5718  * Zero
5719  */
5720 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
5721                                 HWND hScrollWnd)
5722 {
5723   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5724   SCROLLINFO scrollInfo;
5725
5726   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5727   scrollInfo.cbSize = sizeof(SCROLLINFO);
5728   scrollInfo.fMask = /*SIF_PAGE |*/ SIF_POS | SIF_RANGE;
5729  
5730   if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
5731   {
5732     INT nOldScrollPos = scrollInfo.nPos;
5733
5734     switch (nScrollCode)
5735     {
5736     case SB_LINELEFT:
5737       if (scrollInfo.nPos > scrollInfo.nMin)
5738       {
5739         scrollInfo.nPos--;
5740       }
5741       break;
5742     
5743     case SB_LINERIGHT:
5744       if (scrollInfo.nPos < scrollInfo.nMax)
5745       {
5746         scrollInfo.nPos++;
5747       }
5748       break;
5749       
5750     case SB_PAGELEFT:
5751       if (scrollInfo.nPos > scrollInfo.nMin)
5752       {
5753         INT nPage = 0;
5754
5755         if (uView == LVS_LIST)
5756         {
5757           nPage = LISTVIEW_GetCountPerRow(hwnd);
5758         }
5759         else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
5760         {
5761           nPage = 10;
5762         }
5763         
5764         if (scrollInfo.nPos >= nPage)
5765         {
5766           scrollInfo.nPos -= nPage;
5767         }
5768         else
5769         {
5770           scrollInfo.nPos = scrollInfo.nMin;
5771         }
5772       }
5773       break;
5774       
5775     case SB_PAGERIGHT:
5776       if (scrollInfo.nPos < scrollInfo.nMax)
5777       {
5778         INT nPage = 0;
5779
5780         if (uView == LVS_LIST)
5781         {
5782           nPage = LISTVIEW_GetCountPerRow(hwnd);
5783         }
5784         else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
5785         {
5786           nPage = 10;
5787         }
5788         
5789         if (scrollInfo.nPos <= scrollInfo.nMax - nPage)
5790         {
5791           scrollInfo.nPos += nPage;
5792         }
5793         else
5794         {
5795           scrollInfo.nPos = scrollInfo.nMax;
5796         }
5797       }
5798       break;
5799
5800     case SB_THUMBPOSITION:
5801       break;
5802     }
5803
5804     if (nOldScrollPos != scrollInfo.nPos)
5805     {
5806       scrollInfo.fMask = SIF_POS;
5807       SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
5808       InvalidateRect(hwnd, NULL, TRUE);
5809     }
5810   }
5811     
5812   return 0;
5813 }
5814
5815 /***
5816  * DESCRIPTION:
5817  * ??? 
5818  * 
5819  * PARAMETER(S):
5820  * [I] HWND : window handle
5821  * [I] INT : virtual key 
5822  * [I] LONG : key data
5823  *
5824  * RETURN:
5825  * Zero
5826  */
5827 static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData)
5828 {
5829   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5830   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
5831   HWND hwndParent = GetParent(hwnd);
5832   NMLVKEYDOWN nmKeyDown; 
5833   NMHDR nmh;
5834   INT nItem = -1;
5835   BOOL bRedraw = FALSE;
5836
5837   /* send LVN_KEYDOWN notification */
5838   ZeroMemory(&nmKeyDown, sizeof(NMLVKEYDOWN));
5839   nmKeyDown.hdr.hwndFrom = hwnd;  
5840   nmKeyDown.hdr.idFrom = nCtrlId;  
5841   nmKeyDown.hdr.code = LVN_KEYDOWN;  
5842   nmKeyDown.wVKey = nVirtualKey; 
5843   nmKeyDown.flags = 0; 
5844   SendMessageA(hwndParent, WM_NOTIFY, (WPARAM)nCtrlId, (LPARAM)&nmKeyDown); 
5845   
5846   /* initialize */
5847   nmh.hwndFrom = hwnd;
5848   nmh.idFrom = nCtrlId;
5849
5850   switch (nVirtualKey)
5851   {
5852   case VK_RETURN:
5853     if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
5854     {
5855       /* send NM_RETURN notification */
5856       nmh.code = NM_RETURN;
5857       ListView_Notify(hwndParent, nCtrlId, &nmh);
5858       
5859       /* send LVN_ITEMACTIVATE notification */
5860       nmh.code = LVN_ITEMACTIVATE;
5861       ListView_Notify(hwndParent, nCtrlId, &nmh);
5862     }
5863     break;
5864
5865   case VK_HOME:
5866     if (GETITEMCOUNT(infoPtr) > 0)
5867     {
5868       nItem = 0;
5869     }
5870     break;
5871
5872   case VK_END:
5873     if (GETITEMCOUNT(infoPtr) > 0)
5874     {
5875       nItem = GETITEMCOUNT(infoPtr) - 1;
5876     }
5877     break;
5878
5879   case VK_LEFT:
5880     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT);
5881     break;
5882
5883   case VK_UP:
5884     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE);
5885     break;
5886     
5887   case VK_RIGHT:
5888     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT);
5889     break;
5890
5891   case VK_DOWN:
5892     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW);
5893     break;
5894
5895   case VK_PRIOR:
5896     /* TO DO */
5897     break;
5898
5899   case VK_NEXT:
5900     /* TO DO */
5901     break;
5902   }
5903
5904   if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
5905   {
5906     bRedraw = LISTVIEW_KeySelection(hwnd, nItem);
5907     if (bRedraw != FALSE)
5908     {
5909       /* refresh client area */
5910       InvalidateRect(hwnd, NULL, TRUE);
5911       UpdateWindow(hwnd);
5912     }
5913   }
5914
5915   return 0;
5916 }
5917
5918 /***
5919  * DESCRIPTION:
5920  * Kills the focus.
5921  * 
5922  * PARAMETER(S):
5923  * [I] HWND : window handle
5924  *
5925  * RETURN:
5926  * Zero
5927  */
5928 static LRESULT LISTVIEW_KillFocus(HWND hwnd)
5929 {
5930   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongA(hwnd, 0);
5931   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
5932   NMHDR nmh;
5933
5934   TRACE("(hwnd=%x)\n", hwnd);
5935
5936   /* send NM_KILLFOCUS notification */
5937   nmh.hwndFrom = hwnd;
5938   nmh.idFrom = nCtrlId;
5939   nmh.code = NM_KILLFOCUS;
5940   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
5941
5942   /* set window focus flag */
5943   infoPtr->bFocus = FALSE;
5944
5945   /* NEED drawing optimization ; redraw the selected items */
5946   InvalidateRect(hwnd, NULL, FALSE);
5947
5948   return 0;
5949 }
5950
5951 /***
5952  * DESCRIPTION:
5953  * Processes double click messages (left mouse button).
5954  * 
5955  * PARAMETER(S):
5956  * [I] HWND : window handle
5957  * [I] WORD : key flag
5958  * [I] WORD : x coordinate
5959  * [I] WORD : y coordinate
5960  *
5961  * RETURN:
5962  * Zero
5963  */
5964 static LRESULT LISTVIEW_LButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX, 
5965                                       WORD wPosY)
5966 {
5967   LONG nCtrlId = GetWindowLongA(hwnd, GWL_ID);
5968   NMHDR nmh;
5969
5970   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
5971
5972   /* send NM_DBLCLK notification */
5973   nmh.hwndFrom = hwnd;
5974   nmh.idFrom = nCtrlId;
5975   nmh.code = NM_DBLCLK;
5976   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
5977
5978   /* send LVN_ITEMACTIVATE notification */
5979   nmh.code = LVN_ITEMACTIVATE;
5980   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
5981
5982   return 0;
5983 }
5984
5985 /***
5986  * DESCRIPTION:
5987  * Processes mouse down messages (left mouse button).
5988  * 
5989  * PARAMETER(S):
5990  * [I] HWND : window handle
5991  * [I] WORD : key flag
5992  * [I] WORD : x coordinate
5993  * [I] WORD : y coordinate
5994  *
5995  * RETURN:
5996  * Zero
5997  */
5998 static LRESULT LISTVIEW_LButtonDown(HWND hwnd, WORD wKey, WORD wPosX, 
5999                                     WORD wPosY)
6000 {
6001   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6002   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6003   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
6004   static BOOL bGroupSelect = TRUE;
6005   POINT ptPosition;
6006   NMHDR nmh;
6007   INT nItem;
6008
6009   TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, 
6010         wPosY);
6011
6012   /* send NM_RELEASEDCAPTURE notification */ 
6013   nmh.hwndFrom = hwnd;
6014   nmh.idFrom = nCtrlId;
6015   nmh.code = NM_RELEASEDCAPTURE;
6016   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
6017  
6018   if (infoPtr->bFocus == FALSE)
6019   {
6020     SetFocus(hwnd);
6021   }
6022
6023   /* set left button down flag */
6024   infoPtr->bLButtonDown = TRUE;
6025   
6026   ptPosition.x = wPosX;
6027   ptPosition.y = wPosY;
6028   nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
6029   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6030   {
6031     if (lStyle & LVS_SINGLESEL)
6032     {
6033       LISTVIEW_SetSelection(hwnd, nItem);
6034     }
6035     else
6036     {
6037       if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
6038       {
6039         if (bGroupSelect != FALSE)
6040         {
6041           LISTVIEW_AddGroupSelection(hwnd, nItem);
6042         }
6043         else
6044         {
6045           LISTVIEW_AddSelection(hwnd, nItem);
6046         }
6047       }
6048       else if (wKey & MK_CONTROL)
6049       {
6050         bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem);
6051       }
6052       else  if (wKey & MK_SHIFT)
6053       {
6054         LISTVIEW_SetGroupSelection(hwnd, nItem);
6055       }
6056       else
6057       {
6058         LISTVIEW_SetSelection(hwnd, nItem);
6059       }
6060     }
6061   }
6062   else
6063   {
6064     /* remove all selections */
6065     LISTVIEW_RemoveSelections(hwnd, 0, GETITEMCOUNT(infoPtr));
6066   }
6067
6068   InvalidateRect(hwnd, NULL, TRUE);
6069
6070   return 0;
6071 }
6072
6073 /***
6074  * DESCRIPTION:
6075  * Processes mouse up messages (left mouse button).
6076  * 
6077  * PARAMETER(S):
6078  * [I] HWND : window handle
6079  * [I] WORD : key flag
6080  * [I] WORD : x coordinate
6081  * [I] WORD : y coordinate
6082  *
6083  * RETURN:
6084  * Zero
6085  */
6086 static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX, 
6087                                   WORD wPosY)
6088 {
6089   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6090
6091   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
6092
6093   if (infoPtr->bLButtonDown != FALSE) 
6094   {
6095     INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
6096     NMHDR nmh;
6097
6098     /* send NM_CLICK notification */
6099     nmh.hwndFrom = hwnd;
6100     nmh.idFrom = nCtrlId;
6101     nmh.code = NM_CLICK;
6102     ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
6103
6104     /* set left button flag */
6105     infoPtr->bLButtonDown = FALSE;
6106   }
6107
6108   return 0;
6109 }
6110
6111 /***
6112  * DESCRIPTION:
6113  * Creates the listview control (called before WM_CREATE).
6114  * 
6115  * PARAMETER(S):
6116  * [I] HWND : window handle
6117  * [I] WPARAM : unhandled 
6118  * [I] LPARAM : widow creation info
6119  * 
6120  * RETURN:
6121  * Zero
6122  */
6123 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
6124 {
6125   LISTVIEW_INFO *infoPtr;
6126
6127   TRACE("(hwnd=%x,wParam=%x,lParam=%lx)\n", hwnd, wParam, lParam);
6128
6129   /* allocate memory for info structure */
6130   infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
6131   SetWindowLongA(hwnd, 0, (LONG)infoPtr);
6132   if (infoPtr == NULL) 
6133   {
6134     ERR("could not allocate info memory!\n");
6135     return 0;
6136   }
6137
6138   if ((LISTVIEW_INFO *)GetWindowLongA(hwnd, 0) != infoPtr) 
6139   {
6140     ERR("pointer assignment error!\n");
6141     return 0;
6142   }
6143
6144   return DefWindowProcA(hwnd, WM_NCCREATE, wParam, lParam);
6145 }
6146
6147 /***
6148  * DESCRIPTION:
6149  * Destroys the listview control (called after WM_DESTROY).
6150  * 
6151  * PARAMETER(S):
6152  * [I] HWND : window handle
6153  * 
6154  * RETURN:
6155  * Zero
6156  */
6157 static LRESULT LISTVIEW_NCDestroy(HWND hwnd)
6158 {
6159   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6160
6161   TRACE("(hwnd=%x)\n", hwnd);
6162
6163   /* delete all items */
6164   LISTVIEW_DeleteAllItems(hwnd);
6165
6166   /* destroy data structure */
6167   DPA_Destroy(infoPtr->hdpaItems);
6168
6169   /* destroy font */
6170   infoPtr->hFont = (HFONT)0;
6171   if (infoPtr->hDefaultFont)
6172   {
6173     DeleteObject(infoPtr->hDefaultFont);
6174   }
6175
6176   /* free listview info pointer*/
6177   COMCTL32_Free(infoPtr);
6178
6179   return 0;
6180 }
6181
6182 /***
6183  * DESCRIPTION:
6184  * Handles notifications from children.
6185  * 
6186  * PARAMETER(S):
6187  * [I] HWND : window handle
6188  * [I] INT : control identifier
6189  * [I] LPNMHDR : notification information
6190  * 
6191  * RETURN:
6192  * Zero
6193  */
6194 static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh)
6195 {
6196   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6197   
6198   if (lpnmh->hwndFrom == infoPtr->hwndHeader) 
6199   {
6200     /* handle notification from header control */
6201     if (lpnmh->code == HDN_ENDTRACKA)
6202     {
6203       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6204       InvalidateRect(hwnd, NULL, TRUE);
6205     }
6206     if(lpnmh->code ==  HDN_ITEMCLICKA)
6207     {
6208         /* Handle sorting by Header Column */
6209         NMLISTVIEW nmlv;
6210         LPNMHEADERA pnmHeader = (LPNMHEADERA) lpnmh;
6211         LONG lCtrlId = GetWindowLongA(hwnd, GWL_ID);
6212
6213         ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6214         nmlv.hdr.hwndFrom = hwnd;
6215         nmlv.hdr.idFrom = lCtrlId;
6216         nmlv.hdr.code = LVN_COLUMNCLICK;
6217         nmlv.iItem = -1;
6218         nmlv.iSubItem = pnmHeader->iItem;
6219         
6220         ListView_LVNotify(GetParent(hwnd),lCtrlId, &nmlv);
6221         InvalidateRect(hwnd, NULL, TRUE);
6222
6223     }
6224
6225   }
6226
6227   return 0;
6228 }
6229
6230 /***
6231  * DESCRIPTION:
6232  * Determines the type of structure to use.
6233  * 
6234  * PARAMETER(S):
6235  * [I] HWND : window handle of the sender
6236  * [I] HWND : listview window handle 
6237  * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
6238  *
6239  * RETURN:
6240  * Zero
6241  */
6242 static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand)
6243 {
6244   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6245
6246   if (nCommand == NF_REQUERY)
6247   {
6248     /* determine the type of structure to use */
6249     infoPtr->notifyFormat = SendMessageA(hwndFrom, WM_NOTIFYFORMAT, 
6250                                          (WPARAM)hwnd, (LPARAM)NF_QUERY);
6251     if (infoPtr->notifyFormat == NFR_UNICODE)
6252     {
6253       FIXME("NO support for unicode structures");
6254     }
6255   }
6256
6257   return 0;
6258 }
6259
6260 /***
6261  * DESCRIPTION:
6262  * Paints/Repaints the listview control.
6263  * 
6264  * PARAMETER(S):
6265  * [I] HWND : window handle
6266  * [I] HDC : device context handle
6267  *
6268  * RETURN:
6269  * Zero
6270  */
6271 static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc)
6272 {
6273   PAINTSTRUCT ps;
6274
6275    TRACE("(hwnd=%x,hdc=%x)\n", hwnd, hdc);
6276
6277   if (hdc == 0)
6278   {
6279     hdc = BeginPaint(hwnd, &ps);
6280     LISTVIEW_Refresh(hwnd, hdc);
6281     EndPaint(hwnd, &ps);
6282   }
6283   else
6284   {
6285     LISTVIEW_Refresh(hwnd, hdc);
6286   }
6287
6288   return 0;
6289 }
6290
6291 /***
6292  * DESCRIPTION:
6293  * Processes double click messages (right mouse button).
6294  * 
6295  * PARAMETER(S):
6296  * [I] HWND : window handle
6297  * [I] WORD : key flag
6298  * [I] WORD : x coordinate
6299  * [I] WORD : y coordinate
6300  *
6301  * RETURN:
6302  * Zero
6303  */
6304 static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX, 
6305                                       WORD wPosY)
6306 {
6307   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
6308   NMHDR nmh;
6309
6310   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
6311
6312   /* send NM_RELEASEDCAPTURE notification */ 
6313   nmh.hwndFrom = hwnd;
6314   nmh.idFrom = nCtrlId;
6315   nmh.code = NM_RELEASEDCAPTURE;
6316   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
6317
6318   /* send NM_RDBLCLK notification */
6319   nmh.code = NM_RDBLCLK;
6320   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
6321
6322   return 0;
6323 }
6324
6325 /***
6326  * DESCRIPTION:
6327  * Processes mouse down messages (right mouse button).
6328  * 
6329  * PARAMETER(S):
6330  * [I] HWND : window handle
6331  * [I] WORD : key flag
6332  * [I] WORD : x coordinate
6333  * [I] WORD : y coordinate
6334  *
6335  * RETURN:
6336  * Zero
6337  */
6338 static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX, 
6339                                     WORD wPosY)
6340 {
6341   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6342   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
6343   POINT ptPosition;
6344   NMHDR nmh;
6345   INT nItem;
6346
6347   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
6348
6349   /* send NM_RELEASEDCAPTURE notification */
6350   nmh.hwndFrom = hwnd;
6351   nmh.idFrom = nCtrlId;
6352   nmh.code = NM_RELEASEDCAPTURE;
6353   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
6354  
6355   /* make sure the listview control window has the focus */
6356   if (infoPtr->bFocus == FALSE)
6357   {
6358     SetFocus(hwnd);
6359   }
6360
6361   /* set right button down flag */
6362   infoPtr->bRButtonDown = TRUE;
6363
6364   /* determine the index of the selected item */
6365   ptPosition.x = wPosX;
6366   ptPosition.y = wPosY;
6367   nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
6368   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6369   {
6370     if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)))
6371     {
6372       LISTVIEW_SetSelection(hwnd, nItem);
6373     }
6374   }
6375   else
6376   {
6377     LISTVIEW_RemoveSelections(hwnd, 0, GETITEMCOUNT(infoPtr));
6378   }
6379   
6380   return 0;
6381 }
6382
6383 /***
6384  * DESCRIPTION:
6385  * Processes mouse up messages (right mouse button).
6386  * 
6387  * PARAMETER(S):
6388  * [I] HWND : window handle
6389  * [I] WORD : key flag
6390  * [I] WORD : x coordinate
6391  * [I] WORD : y coordinate
6392  *
6393  * RETURN:
6394  * Zero
6395  */
6396 static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX, 
6397                                   WORD wPosY)
6398 {
6399   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6400   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
6401   NMHDR nmh;
6402
6403   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
6404
6405   if (infoPtr->bRButtonDown != FALSE) 
6406   {
6407     /* send NM_RClICK notification */
6408     ZeroMemory(&nmh, sizeof(NMHDR));
6409     nmh.hwndFrom = hwnd;
6410     nmh.idFrom = nCtrlId;
6411     nmh.code = NM_RCLICK;
6412     ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
6413
6414     /* set button flag */
6415     infoPtr->bRButtonDown = FALSE;
6416   }
6417   
6418   return 0;
6419 }
6420
6421 /***
6422  * DESCRIPTION:
6423  * Sets the focus.  
6424  * 
6425  * PARAMETER(S):
6426  * [I] HWND : window handle
6427  * [I] HWND : window handle of previously focused window
6428  *
6429  * RETURN:
6430  * Zero
6431  */
6432 static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus)
6433 {
6434   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6435   INT nCtrlId = GetWindowLongA(hwnd, GWL_ID);
6436   NMHDR nmh;
6437
6438   TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
6439
6440   /* send NM_SETFOCUS notification */
6441   nmh.hwndFrom = hwnd;
6442   nmh.idFrom = nCtrlId;
6443   nmh.code = NM_SETFOCUS;
6444   ListView_Notify(GetParent(hwnd), nCtrlId, &nmh);
6445
6446   /* set window focus flag */
6447   infoPtr->bFocus = TRUE;
6448
6449   InvalidateRect(hwnd, NULL, TRUE);
6450   UpdateWindow(hwnd);
6451
6452   return 0;
6453 }
6454
6455 /***
6456  * DESCRIPTION:
6457  * Sets the font.  
6458  * 
6459  * PARAMETER(S):
6460  * [I] HWND : window handle
6461  * [I] HFONT : font handle
6462  * [I] WORD : redraw flag
6463  *
6464  * RETURN:
6465  * Zero
6466  */
6467 static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw)
6468 {
6469   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6470   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
6471
6472   TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
6473
6474   if (hFont == 0)
6475   {
6476     infoPtr->hFont = infoPtr->hDefaultFont;
6477   }
6478   else
6479   {
6480     infoPtr->hFont = hFont;
6481   }
6482
6483   if (uView == LVS_REPORT)
6484   {
6485     /* set header font */
6486     SendMessageA(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, 
6487                    MAKELPARAM(fRedraw, 0));
6488   }
6489
6490   /* invalidate listview control client area */
6491   InvalidateRect(hwnd, NULL, TRUE);
6492   
6493   if (fRedraw != FALSE)
6494   {
6495     UpdateWindow(hwnd);
6496   }
6497
6498   return 0;
6499 }
6500
6501 /***
6502  * DESCRIPTION:
6503  * Resizes the listview control. This function processes WM_SIZE
6504  * messages.  At this time, the width and height are not used.
6505  * 
6506  * PARAMETER(S):
6507  * [I] HWND : window handle
6508  * [I] WORD : new width
6509  * [I] WORD : new height
6510  *
6511  * RETURN:
6512  * Zero
6513  */
6514 static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height)
6515 {
6516   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE); 
6517   UINT uView = lStyle & LVS_TYPEMASK;
6518
6519   TRACE("(hwnd=%x, width=%d, height=%d)\n",hwnd, Width, Height);
6520
6521   LISTVIEW_UpdateSize(hwnd);
6522
6523   if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6524   {
6525     if (lStyle & LVS_ALIGNLEFT)
6526     {
6527       LISTVIEW_AlignLeft(hwnd);
6528     }
6529     else
6530     {
6531       LISTVIEW_AlignTop(hwnd);
6532     }
6533   }
6534
6535   LISTVIEW_UpdateScroll(hwnd);
6536   
6537   /* invalidate client area + erase background */
6538   InvalidateRect(hwnd, NULL, TRUE);
6539
6540   return 0;
6541 }
6542
6543 /***
6544  * DESCRIPTION:
6545  * Sets the size information.
6546  * 
6547  * PARAMETER(S):
6548  * [I] HWND : window handle
6549  *
6550  * RETURN:
6551  * Zero
6552  */
6553 static VOID LISTVIEW_UpdateSize(HWND hwnd)
6554 {
6555   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6556   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
6557   UINT uView = lStyle & LVS_TYPEMASK;
6558   RECT rcList;
6559   
6560   GetClientRect(hwnd, &rcList);
6561   infoPtr->rcList.left = 0;
6562   infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
6563   infoPtr->rcList.top = 0;
6564   infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
6565      
6566   if (uView == LVS_LIST)
6567   {
6568     if ((lStyle & WS_HSCROLL) == 0)
6569     {
6570       INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
6571       if (infoPtr->rcList.bottom > nHScrollHeight)
6572       { 
6573         infoPtr->rcList.bottom -= nHScrollHeight;
6574       }
6575     }
6576   }
6577   else if (uView == LVS_REPORT)
6578   {
6579     HDLAYOUT hl;
6580     WINDOWPOS wp;
6581
6582     hl.prc = &rcList;
6583     hl.pwpos = &wp;
6584     Header_Layout(infoPtr->hwndHeader, &hl);
6585     if (!(LVS_NOCOLUMNHEADER & lStyle))
6586     {
6587       infoPtr->rcList.top = max(wp.cy, 0);
6588     }
6589   }
6590 }
6591
6592 /***
6593  * DESCRIPTION:
6594  * Processes WM_STYLECHANGED messages. 
6595  * 
6596  * PARAMETER(S):
6597  * [I] HWND : window handle
6598  * [I] WPARAM : window style type (normal or extended)
6599  * [I] LPSTYLESTRUCT : window style information
6600  *
6601  * RETURN:
6602  * Zero
6603  */
6604 static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType, 
6605                                  LPSTYLESTRUCT lpss)
6606 {
6607   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
6608   UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
6609   UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
6610   RECT rcList = infoPtr->rcList;
6611
6612   TRACE("(hwnd=%x, styletype=%x, stylestruct=%p)\n", 
6613         hwnd, wStyleType, lpss);
6614
6615   if (wStyleType == GWL_STYLE)
6616   {
6617     if (uOldView == LVS_REPORT)
6618     {
6619       ShowWindow(infoPtr->hwndHeader, SW_HIDE);
6620     }
6621  
6622     if ((lpss->styleOld & WS_HSCROLL) != 0)
6623     {
6624        ShowScrollBar(hwnd, SB_HORZ, FALSE);
6625     }
6626  
6627     if ((lpss->styleOld & WS_VSCROLL) != 0)
6628     {
6629        ShowScrollBar(hwnd, SB_VERT, FALSE);
6630     }
6631  
6632     if (uNewView == LVS_ICON)
6633     {
6634       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
6635       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
6636       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6637       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
6638       if (lpss->styleNew & LVS_ALIGNLEFT)
6639       {
6640         LISTVIEW_AlignLeft(hwnd);
6641       }
6642       else
6643       {
6644         LISTVIEW_AlignTop(hwnd);
6645       }
6646     }
6647     else if (uNewView == LVS_REPORT)
6648     {
6649       HDLAYOUT hl;
6650       WINDOWPOS wp;
6651
6652       hl.prc = &rcList;
6653       hl.pwpos = &wp;
6654       Header_Layout(infoPtr->hwndHeader, &hl);
6655       SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy, 
6656                    wp.flags);
6657       if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
6658       {
6659         ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
6660       }
6661       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
6662       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
6663       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6664       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
6665     }
6666     else if (uNewView == LVS_LIST)
6667     {
6668       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
6669       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
6670       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6671       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
6672     }
6673     else
6674     {
6675       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
6676       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
6677       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6678       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
6679       if (lpss->styleNew & LVS_ALIGNLEFT)
6680       {
6681         LISTVIEW_AlignLeft(hwnd);
6682       }
6683       else
6684       {
6685         LISTVIEW_AlignTop(hwnd);
6686       }
6687     }
6688
6689     /* update the size of the client area */
6690     LISTVIEW_UpdateSize(hwnd);
6691
6692     /* add scrollbars if needed */
6693     LISTVIEW_UpdateScroll(hwnd);
6694     
6695     /* invalidate client area + erase background */
6696     InvalidateRect(hwnd, NULL, TRUE);
6697
6698     /* print the list of unsupported window styles */
6699     LISTVIEW_UnsupportedStyles(lpss->styleNew);
6700   }
6701
6702   return 0;
6703 }
6704
6705 /***
6706  * DESCRIPTION:
6707  * Window procedure of the listview control.
6708  *
6709  */
6710 static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
6711                                    LPARAM lParam)
6712 {
6713   switch (uMsg)
6714   {
6715   case LVM_APPROXIMATEVIEWRECT: 
6716     return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam, 
6717                                         LOWORD(lParam), HIWORD(lParam));
6718   case LVM_ARRANGE: 
6719     return LISTVIEW_Arrange(hwnd, (INT)wParam);
6720
6721 /* case LVM_CREATEDRAGIMAGE: */
6722
6723   case LVM_DELETEALLITEMS:
6724     return LISTVIEW_DeleteAllItems(hwnd);
6725
6726   case LVM_DELETECOLUMN:
6727     return LISTVIEW_DeleteColumn(hwnd, (INT)wParam);
6728
6729   case LVM_DELETEITEM:
6730     return LISTVIEW_DeleteItem(hwnd, (INT)wParam);
6731
6732 /*      case LVM_EDITLABEL: */
6733
6734   case LVM_ENSUREVISIBLE:
6735     return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
6736
6737   case LVM_FINDITEMA:
6738     return LISTVIEW_FindItem(hwnd, (INT)wParam, (LPLVFINDINFO)lParam);
6739
6740   case LVM_GETBKCOLOR:
6741     return LISTVIEW_GetBkColor(hwnd);
6742
6743 /*      case LVM_GETBKIMAGE: */
6744
6745   case LVM_GETCALLBACKMASK:
6746     return LISTVIEW_GetCallbackMask(hwnd);
6747
6748   case LVM_GETCOLUMNA:
6749     return LISTVIEW_GetColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
6750
6751 /*      case LVM_GETCOLUMNW: */
6752
6753   case LVM_GETCOLUMNORDERARRAY:
6754     FIXME("Unimplemented msg LVM_GETCOLUMNORDERARRAY\n");
6755     return 0;
6756
6757   case LVM_GETCOLUMNWIDTH:
6758     return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam);
6759
6760   case LVM_GETCOUNTPERPAGE:
6761     return LISTVIEW_GetCountPerPage(hwnd);
6762
6763 /*      case LVM_GETEDITCONTROL: */
6764
6765   case LVM_GETEXTENDEDLISTVIEWSTYLE:
6766     return LISTVIEW_GetExtendedListViewStyle(hwnd);
6767
6768   case LVM_GETHEADER:
6769     return LISTVIEW_GetHeader(hwnd);
6770
6771 /*      case LVM_GETHOTCURSOR: */
6772
6773   case LVM_GETHOTITEM:
6774     return LISTVIEW_GetHotItem(hwnd);
6775
6776 /*      case LVM_GETHOVERTIME: */
6777
6778   case LVM_GETIMAGELIST:
6779     return LISTVIEW_GetImageList(hwnd, (INT)wParam);
6780
6781 /*      case LVM_GETISEARCHSTRING: */
6782
6783   case LVM_GETITEMA:
6784     return LISTVIEW_GetItemA(hwnd, (LPLVITEMA)lParam);
6785
6786 /*      case LVM_GETITEMW: */
6787
6788   case LVM_GETITEMCOUNT:
6789     return LISTVIEW_GetItemCount(hwnd);
6790
6791   case LVM_GETITEMPOSITION:
6792     return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam);
6793
6794   case LVM_GETITEMRECT: 
6795     return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam);
6796
6797   case LVM_GETITEMSPACING: 
6798     return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam);
6799
6800   case LVM_GETITEMSTATE: 
6801     return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam);
6802     
6803   case LVM_GETITEMTEXTA:
6804     LISTVIEW_GetItemTextA(hwnd, (INT)wParam, (LPLVITEMA)lParam);
6805     break;
6806
6807 /*      case LVM_GETITEMTEXTW: */
6808
6809   case LVM_GETNEXTITEM:
6810     return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
6811
6812 /*      case LVM_GETNUMBEROFWORKAREAS: */
6813
6814   case LVM_GETORIGIN:
6815     return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam);
6816
6817   case LVM_GETSELECTEDCOUNT:
6818     return LISTVIEW_GetSelectedCount(hwnd);
6819
6820   case LVM_GETSELECTIONMARK: 
6821     return LISTVIEW_GetSelectionMark(hwnd);
6822
6823   case LVM_GETSTRINGWIDTHA:
6824     return LISTVIEW_GetStringWidthA (hwnd, (LPCSTR)lParam);
6825
6826 /*      case LVM_GETSTRINGWIDTHW: */
6827 /*      case LVM_GETSUBITEMRECT: */
6828
6829   case LVM_GETTEXTBKCOLOR:
6830     return LISTVIEW_GetTextBkColor(hwnd);
6831
6832   case LVM_GETTEXTCOLOR:
6833     return LISTVIEW_GetTextColor(hwnd);
6834
6835 /*      case LVM_GETTOOLTIPS: */
6836
6837   case LVM_GETTOPINDEX:
6838     return LISTVIEW_GetTopIndex(hwnd);
6839
6840 /*      case LVM_GETUNICODEFORMAT: */
6841
6842   case LVM_GETVIEWRECT:
6843     return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
6844
6845 /*      case LVM_GETWORKAREAS: */
6846
6847   case LVM_HITTEST:
6848     return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
6849
6850   case LVM_INSERTCOLUMNA:
6851     return LISTVIEW_InsertColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
6852
6853   case LVM_INSERTCOLUMNW:
6854     return LISTVIEW_InsertColumnW(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam);
6855
6856   case LVM_INSERTITEMA:
6857     return LISTVIEW_InsertItemA(hwnd, (LPLVITEMA)lParam);
6858
6859   case LVM_INSERTITEMW:
6860     return LISTVIEW_InsertItemW(hwnd, (LPLVITEMW)lParam);
6861
6862   case LVM_REDRAWITEMS:
6863     return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam);
6864
6865 /*   case LVM_SCROLL:  */
6866 /*     return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam); */
6867
6868   case LVM_SETBKCOLOR:
6869     return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam);
6870
6871 /*      case LVM_SETBKIMAGE: */
6872
6873   case LVM_SETCALLBACKMASK:
6874     return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam);
6875
6876   case LVM_SETCOLUMNA:
6877     return LISTVIEW_SetColumnA(hwnd, (INT)wParam, (LPLVCOLUMNA)lParam);
6878
6879   case LVM_SETCOLUMNW:
6880     FIXME("Unimplemented msg LVM_SETCOLUMNW\n");
6881     return 0;
6882
6883   case LVM_SETCOLUMNORDERARRAY:
6884     FIXME("Unimplemented msg LVM_SETCOLUMNORDERARRAY\n");
6885     return 0;
6886
6887   case LVM_SETCOLUMNWIDTH:
6888     return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, (INT)lParam);
6889
6890   case LVM_SETEXTENDEDLISTVIEWSTYLE:
6891     return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam);
6892
6893 /*      case LVM_SETHOTCURSOR: */
6894
6895   case LVM_SETHOTITEM:
6896     return LISTVIEW_SetHotItem(hwnd, (INT)wParam);
6897
6898 /*      case LVM_SETHOVERTIME: */
6899 /*      case LVM_SETICONSPACING: */
6900         
6901   case LVM_SETIMAGELIST:
6902     return LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
6903
6904   case LVM_SETITEMA:
6905     return LISTVIEW_SetItemA(hwnd, (LPLVITEMA)lParam);
6906
6907 /*      case LVM_SETITEMW: */
6908
6909   case LVM_SETITEMCOUNT: 
6910     return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam);
6911     
6912   case LVM_SETITEMPOSITION:
6913     return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam),
6914                                     (INT)HIWORD(lParam));
6915
6916 /*      case LVM_SETITEMPOSITION32: */
6917
6918   case LVM_SETITEMSTATE: 
6919     return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMA)lParam);
6920
6921   case LVM_SETITEMTEXTA:
6922     return LISTVIEW_SetItemTextA(hwnd, (INT)wParam, (LPLVITEMA)lParam);
6923
6924 /*      case LVM_SETITEMTEXTW: */
6925
6926   case LVM_SETSELECTIONMARK:
6927     return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam);
6928
6929   case LVM_SETTEXTBKCOLOR:
6930     return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam);
6931
6932   case LVM_SETTEXTCOLOR:
6933     return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam);
6934
6935 /*      case LVM_SETTOOLTIPS: */
6936 /*      case LVM_SETUNICODEFORMAT: */
6937 /*      case LVM_SETWORKAREAS: */
6938
6939   case LVM_SORTITEMS:
6940     return LISTVIEW_SortItems(hwnd, wParam, lParam);
6941
6942 /*      case LVM_SUBITEMHITTEST: */
6943
6944   case LVM_UPDATE: 
6945     return LISTVIEW_Update(hwnd, (INT)wParam);
6946
6947 /*      case WM_CHAR: */
6948 /*      case WM_COMMAND: */
6949
6950   case WM_CREATE:
6951     return LISTVIEW_Create(hwnd, wParam, lParam);
6952     
6953   case WM_ERASEBKGND:
6954     return LISTVIEW_EraseBackground(hwnd, wParam, lParam);
6955
6956   case WM_GETDLGCODE:
6957     return DLGC_WANTCHARS | DLGC_WANTARROWS;
6958
6959   case WM_GETFONT:
6960     return LISTVIEW_GetFont(hwnd);
6961
6962   case WM_HSCROLL:
6963     return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam), 
6964                             (INT)HIWORD(wParam), (HWND)lParam);
6965
6966   case WM_KEYDOWN:
6967     return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam);
6968
6969   case WM_KILLFOCUS:
6970     return LISTVIEW_KillFocus(hwnd);
6971
6972   case WM_LBUTTONDBLCLK:
6973     return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam), 
6974                                 HIWORD(lParam));
6975     
6976   case WM_LBUTTONDOWN:
6977     return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam), 
6978                                 HIWORD(lParam));
6979   case WM_LBUTTONUP:
6980     return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam), 
6981                               HIWORD(lParam));
6982     
6983 /*      case WM_MOUSEMOVE: */
6984 /*          return LISTVIEW_MouseMove (hwnd, wParam, lParam); */
6985
6986   case WM_NCCREATE:
6987     return LISTVIEW_NCCreate(hwnd, wParam, lParam);
6988
6989   case WM_NCDESTROY:
6990     return LISTVIEW_NCDestroy(hwnd);
6991
6992   case WM_NOTIFY:
6993     return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam);
6994
6995   case WM_NOTIFYFORMAT:
6996     return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam);
6997
6998   case WM_PAINT: 
6999     return LISTVIEW_Paint(hwnd, (HDC)wParam); 
7000
7001   case WM_RBUTTONDBLCLK:
7002     return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam), 
7003                                   HIWORD(lParam));
7004
7005   case WM_RBUTTONDOWN:
7006     return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam), 
7007                                 HIWORD(lParam));
7008
7009   case WM_RBUTTONUP:
7010     return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam), 
7011                               HIWORD(lParam));
7012
7013   case WM_SETFOCUS:
7014     return LISTVIEW_SetFocus(hwnd, (HWND)wParam);
7015
7016   case WM_SETFONT:
7017     return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam);
7018
7019 /*      case WM_SETREDRAW: */
7020
7021   case WM_SIZE:
7022     return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
7023
7024   case WM_STYLECHANGED:
7025     return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam);
7026
7027 /*      case WM_TIMER: */
7028
7029   case WM_VSCROLL:
7030     return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam), 
7031                             (INT)HIWORD(wParam), (HWND)lParam);
7032
7033 /*      case WM_WINDOWPOSCHANGED: */
7034 /*      case WM_WININICHANGE: */
7035
7036   default:
7037     if (uMsg >= WM_USER)
7038     {
7039       ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, 
7040           lParam);
7041     }
7042
7043     /* call default window procedure */
7044     return DefWindowProcA(hwnd, uMsg, wParam, lParam);
7045   }
7046
7047   return 0;
7048 }
7049
7050 /***
7051  * DESCRIPTION:
7052  * Registers the window class.
7053  * 
7054  * PARAMETER(S):
7055  * None
7056  *
7057  * RETURN:
7058  * None
7059  */
7060 VOID LISTVIEW_Register(void)
7061 {
7062   WNDCLASSA wndClass;
7063
7064   if (!GlobalFindAtomA(WC_LISTVIEWA)) 
7065   {
7066     ZeroMemory(&wndClass, sizeof(WNDCLASSA));
7067     wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
7068     wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
7069     wndClass.cbClsExtra = 0;
7070     wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
7071     wndClass.hCursor = LoadCursorA(0, IDC_ARROWA);
7072     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
7073     wndClass.lpszClassName = WC_LISTVIEWA;
7074     RegisterClassA(&wndClass);
7075   }
7076 }
7077
7078 /***
7079  * DESCRIPTION:
7080  * Unregisters the window class.
7081  * 
7082  * PARAMETER(S):
7083  * None
7084  *
7085  * RETURN:
7086  * None
7087  */
7088 VOID LISTVIEW_Unregister(void)
7089 {
7090   if (GlobalFindAtomA(WC_LISTVIEWA))
7091   {
7092     UnregisterClassA(WC_LISTVIEWA, (HINSTANCE)NULL);
7093   }
7094 }
7095