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