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