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