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