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