- Reduce the number of messages being passed to itself by using the
[wine] / dlls / comctl32 / listview.c
1 /*
2  * Listview control
3  *
4  * Copyright 1998, 1999 Eric Kohl
5  * Copyright 1999 Luc Tourangeau
6  * Copyright 2000 Jason Mawdsley
7  * Copyright 2001 Codeweavers Inc.
8  * Copyright 2002 Dimitrie O. Paun
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  *
24  * NOTES
25  * Listview control implementation.
26  *
27  * TODO:
28  *   1. No horizontal scrolling when header is larger than the client area.
29  *   2. Drawing optimizations.
30  *   3. Hot item handling.
31  *
32  * Notifications:
33  *   LISTVIEW_Notify : most notifications from children (editbox and header)
34  *
35  * Data structure:
36  *   LISTVIEW_SetItemCount : not completed for non OWNERDATA
37  *
38  * Advanced functionality:
39  *   LISTVIEW_GetNumberOfWorkAreas : not implemented
40  *   LISTVIEW_GetHotCursor : not implemented
41  *   LISTVIEW_GetISearchString : not implemented
42  *   LISTVIEW_GetBkImage : not implemented
43  *   LISTVIEW_SetBkImage : not implemented
44  *   LISTVIEW_GetColumnOrderArray : simple hack only
45  *   LISTVIEW_SetColumnOrderArray : simple hack only
46  *   LISTVIEW_Arrange : empty stub
47  *   LISTVIEW_ApproximateViewRect : incomplete
48  *   LISTVIEW_Update : not completed
49  *
50  * Known differences in message stream from native control (not known if
51  * these differences cause problems):
52  *   LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
53  *   LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
54  *   WM_PAINT does LVN_GETDISPINFO in item order 0->n, native does n->0.
55  *   WM_SETREDRAW(True) native does LVN_GETDISPINFO for all items and
56  *     does *not* invoke DefWindowProc
57  *   WM_CREATE does not issue WM_QUERYUISTATE and associated registry
58  *     processing for "USEDOUBLECLICKTIME".
59  */
60
61
62 #include "config.h"
63 #include "wine/port.h"
64
65 #include <ctype.h>
66 #include <string.h>
67 #include <stdlib.h>
68 #include <stdio.h>
69
70 #include "winbase.h"
71 #include "winnt.h"
72 #include "heap.h"
73 #include "commctrl.h"
74 #include "comctl32.h"
75 #include "wine/debug.h"
76
77 WINE_DEFAULT_DEBUG_CHANNEL(listview);
78
79 /* Some definitions for inline edit control */
80 typedef BOOL (*EditlblCallbackW)(HWND, LPWSTR, DWORD);
81 typedef BOOL (*EditlblCallbackA)(HWND, LPWSTR, DWORD);
82
83 typedef struct tagLV_INTHIT
84 {
85   LVHITTESTINFO  ht;
86   DWORD          distance;     /* distance to closest item    */
87   INT            iDistItem;    /* item number that is closest */
88 } LV_INTHIT, *LPLV_INTHIT;
89
90
91 typedef struct tagEDITLABEL_ITEM
92 {
93   WNDPROC EditWndProc;
94   DWORD param;
95   EditlblCallbackW EditLblCb;
96 } EDITLABEL_ITEM;
97
98 typedef struct tagLISTVIEW_SUBITEM
99 {
100   LPWSTR pszText;
101   INT iImage;
102   INT iSubItem;
103 } LISTVIEW_SUBITEM;
104
105 typedef struct tagLISTVIEW_ITEM
106 {
107   UINT state;
108   LPWSTR pszText;
109   INT iImage;
110   LPARAM lParam;
111   INT iIndent;
112   POINT ptPosition;
113
114 } LISTVIEW_ITEM;
115
116 typedef struct tagLISTVIEW_SELECTION
117 {
118   DWORD lower;
119   DWORD upper;
120 } LISTVIEW_SELECTION;
121
122 typedef struct tagLISTVIEW_INFO
123 {
124   HWND hwndSelf;
125   COLORREF clrBk;
126   COLORREF clrText;
127   COLORREF clrTextBk;
128   HIMAGELIST himlNormal;
129   HIMAGELIST himlSmall;
130   HIMAGELIST himlState;
131   BOOL bLButtonDown;
132   BOOL bRButtonDown;
133   INT nFocusedItem;
134   HDPA hdpaSelectionRanges;
135   INT nItemHeight;
136   INT nItemWidth;
137   INT nSelectionMark;
138   INT nHotItem;
139   SHORT notifyFormat;
140   RECT rcList;
141   RECT rcView;
142   SIZE iconSize;
143   SIZE iconSpacing;
144   UINT uCallbackMask;
145   HWND hwndHeader;
146   HFONT hDefaultFont;
147   HFONT hFont;
148   INT ntmHeight;               /*  from GetTextMetrics from above font */
149   INT ntmAveCharWidth;         /*  from GetTextMetrics from above font */
150   BOOL bFocus;
151   DWORD dwExStyle;             /* extended listview style */
152   HDPA hdpaItems;
153   PFNLVCOMPARE pfnCompare;
154   LPARAM lParamSort;
155   HWND hwndEdit;
156   BOOL Editing;
157   INT nEditLabelItem;
158   EDITLABEL_ITEM *pedititem;
159   DWORD dwHoverTime;
160   INT nColumnCount;            /* the number of columns in this control */
161
162   DWORD lastKeyPressTimestamp; /* Added */
163   WPARAM charCode;             /* Added */
164   INT nSearchParamLength;      /* Added */
165   WCHAR szSearchParam[ MAX_PATH ]; /* Added */
166   BOOL bIsDrawing;
167 } LISTVIEW_INFO;
168
169 /*
170  * constants
171  */
172
173 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
174 #define SB_INTERNAL_UP      -1
175 #define SB_INTERNAL_DOWN    -2
176 #define SB_INTERNAL_RIGHT   -3
177 #define SB_INTERNAL_LEFT    -4
178
179 /* maximum size of a label */
180 #define DISP_TEXT_SIZE 512
181
182 /* padding for items in list and small icon display modes */
183 #define WIDTH_PADDING 12
184
185 /* padding for items in list, report and small icon display modes */
186 #define HEIGHT_PADDING 1
187
188 /* offset of items in report display mode */
189 #define REPORT_MARGINX 2
190
191 /* padding for icon in large icon display mode
192  *   ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
193  *                                 that HITTEST will see.
194  *   ICON_TOP_PADDING_HITABLE - spacing between above and icon.
195  *   ICON_TOP_PADDING - sum of the two above.
196  *   ICON_BOTTOM_PADDING - between bottom of icon and top of text
197  *   LABEL_VERT_OFFSET - between bottom of text and end of box
198  */
199 #define ICON_TOP_PADDING_NOTHITABLE  2
200 #define ICON_TOP_PADDING_HITABLE     2
201 #define ICON_TOP_PADDING ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE
202 #define ICON_BOTTOM_PADDING 4
203 #define LABEL_VERT_OFFSET 10
204
205 /* default label width for items in list and small icon display modes */
206 #define DEFAULT_LABEL_WIDTH 40
207
208 /* default column width for items in list display mode */
209 #define DEFAULT_COLUMN_WIDTH 128
210
211 /* Size of "line" scroll for V & H scrolls */
212 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
213
214 /* Padding betwen image and label */
215 #define IMAGE_PADDING  2
216
217 /* Padding behind the label */
218 #define TRAILING_PADDING  5
219
220 /* Border for the icon caption */
221 #define CAPTION_BORDER  2
222 /*
223  * macros
224  */
225 /* retrieve the number of items in the listview */
226 #define GETITEMCOUNT(infoPtr) ((infoPtr)->hdpaItems->nItemCount)
227 #define HDM_INSERTITEMT(isW) ( (isW) ? HDM_INSERTITEMW : HDM_INSERTITEMA )
228
229 HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y,
230         INT width, INT height, HWND parent, HINSTANCE hinst,
231         EditlblCallbackW EditLblCb, DWORD param, BOOL isW);
232
233 /*
234  * forward declarations
235  */
236 static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW);
237 static INT LISTVIEW_SuperHitTestItem(HWND, LPLV_INTHIT, BOOL);
238 static INT LISTVIEW_HitTestItem(HWND, LPLVHITTESTINFO, BOOL);
239 static INT LISTVIEW_GetCountPerRow(HWND);
240 static INT LISTVIEW_GetCountPerColumn(HWND);
241 static VOID LISTVIEW_AlignLeft(HWND);
242 static VOID LISTVIEW_AlignTop(HWND);
243 static VOID LISTVIEW_AddGroupSelection(HWND, INT);
244 static VOID LISTVIEW_AddSelection(HWND, INT);
245 static BOOL LISTVIEW_AddSubItemT(HWND, LPLVITEMW, BOOL);
246 static INT LISTVIEW_FindInsertPosition(HDPA, INT);
247 static INT LISTVIEW_GetItemHeight(HWND);
248 static BOOL LISTVIEW_GetItemBoundBox(HWND, INT, LPRECT);
249 static BOOL LISTVIEW_GetItemPosition(HWND, INT, LPPOINT);
250 static LRESULT LISTVIEW_GetItemRect(HWND, INT, LPRECT);
251 static LRESULT LISTVIEW_GetSubItemRect(HWND, INT, INT, INT, LPRECT);
252 static INT LISTVIEW_GetItemWidth(HWND);
253 static INT LISTVIEW_GetLabelWidth(HWND, INT);
254 static LRESULT LISTVIEW_GetOrigin(HWND, LPPOINT);
255 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem);
256 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA, INT);
257 static LRESULT LISTVIEW_GetViewRect(HWND, LPRECT);
258 static BOOL LISTVIEW_InitItemT(HWND, LISTVIEW_ITEM *, LPLVITEMW, BOOL);
259 static BOOL LISTVIEW_InitSubItemT(HWND, LISTVIEW_SUBITEM *, LPLVITEMW, BOOL);
260 static LRESULT LISTVIEW_MouseSelection(HWND, POINT);
261 static BOOL LISTVIEW_RemoveColumn(HDPA, INT);
262 static BOOL LISTVIEW_RemoveSubItem(HDPA, INT);
263 static VOID LISTVIEW_SetGroupSelection(HWND, INT);
264 static BOOL LISTVIEW_SetItemT(HWND, LPLVITEMW, BOOL);
265 static BOOL LISTVIEW_SetItemFocus(HWND, INT);
266 static BOOL LISTVIEW_SetItemPosition(HWND, INT, LONG, LONG);
267 static VOID LISTVIEW_UpdateScroll(HWND);
268 static VOID LISTVIEW_SetSelection(HWND, INT);
269 static BOOL LISTVIEW_UpdateSize(HWND);
270 static BOOL LISTVIEW_SetSubItemT(HWND, LPLVITEMW, BOOL);
271 static LRESULT LISTVIEW_SetViewRect(HWND, LPRECT);
272 static BOOL LISTVIEW_ToggleSelection(HWND, INT);
273 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle);
274 static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW);
275 static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem);
276 static BOOL LISTVIEW_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem);
277 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam);
278 static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort);
279 static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW);
280 static INT LISTVIEW_ProcessLetterKeys( HWND hwnd, WPARAM charCode, LPARAM keyData );
281 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem);
282 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask);
283 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem);
284 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem);
285 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem);
286 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc);
287 static void ListView_UpdateLargeItemLabelRect (HWND hwnd, const LISTVIEW_INFO* infoPtr, int nItem, RECT *rect);
288 static LRESULT LISTVIEW_GetColumnT(HWND, INT, LPLVCOLUMNW, BOOL);
289 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos, HWND hScrollWnd);
290 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos, HWND hScrollWnd);
291 static INT LISTVIEW_GetTopIndex(HWND hwnd);
292 static BOOL LISTVIEW_EnsureVisible(HWND hwnd, INT nItem, BOOL bPartial);
293
294 /******** Defines that LISTVIEW_ProcessLetterKeys uses ****************/
295 #define KEY_DELAY       450
296
297 #define COUNTOF(array) (sizeof(array)/sizeof(array[0]))
298
299 static inline BOOL is_textW(LPCWSTR text)
300 {
301   return text != NULL && text != LPSTR_TEXTCALLBACKW;
302 }
303
304 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
305 {
306   /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
307   return is_textW(text);
308 }
309
310 static inline int textlenT(LPCWSTR text, BOOL isW)
311 {
312   return !is_textT(text, isW) ? 0 :
313          isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
314 }
315
316 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
317 {
318   if (isDestW)
319     if (isSrcW) lstrcpynW(dest, src, max);
320     else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
321   else
322     if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
323     else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
324 }
325
326 static inline LPCSTR debugstr_t(LPCWSTR text, BOOL isW)
327 {
328   return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
329 }
330
331 static inline LPCSTR debugstr_tn(LPCWSTR text, BOOL isW, INT n)
332 {
333   return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
334 }
335
336 static inline LPCSTR lv_dmp_str(LPCWSTR text, BOOL isW, INT n)
337 {
338     return debugstr_tn(text, isW, min(n, textlenT(text, isW)));
339 }
340
341 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
342 {
343   LPWSTR wstr = (LPWSTR)text;
344
345   TRACE("(text=%s, isW=%d)\n", debugstr_t(text, isW), isW);
346   if (!isW && text)
347   {
348     INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
349     wstr = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
350     if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
351   }
352   TRACE("   wstr=%s\n", debugstr_w(wstr));
353   return wstr;
354 }
355
356 static inline void textfreeT(LPWSTR wstr, BOOL isW)
357 {
358   if (!isW && wstr) HeapFree(GetProcessHeap(), 0, wstr);
359 }
360
361 /*
362  * dest is a pointer to a Unicode string
363  * src is a pointer to a string (Unicode if isW, ANSI if !isW)
364  */
365 static inline BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
366 {
367     LPWSTR pszText = textdupTtoW(src, isW);
368     BOOL bResult = TRUE;
369     if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
370     bResult = Str_SetPtrW(dest, pszText);
371     textfreeT(pszText, isW);
372     return bResult;
373 }
374
375 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
376                                       WPARAM wParam, LPARAM lParam, BOOL isW)
377 {
378   if (isW)
379     return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
380   else
381     return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
382 }
383
384 static inline BOOL notify(HWND self, INT code, LPNMHDR pnmh)
385 {
386   pnmh->hwndFrom = self;
387   pnmh->idFrom = GetWindowLongW(self, GWL_ID);
388   pnmh->code = code;
389   return (BOOL)SendMessageW(GetParent(self), WM_NOTIFY,
390                             (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
391 }
392
393 static inline BOOL hdr_notify(HWND self, INT code)
394 {
395   NMHDR nmh;
396   return notify(self, code, &nmh);
397 }
398
399 static inline BOOL listview_notify(HWND self, INT code, LPNMLISTVIEW plvnm)
400 {
401   return notify(self, code, (LPNMHDR)plvnm);
402 }
403
404 static int tabNotification[] = {
405   LVN_BEGINLABELEDITW, LVN_BEGINLABELEDITA,
406   LVN_ENDLABELEDITW, LVN_ENDLABELEDITA,
407   LVN_GETDISPINFOW, LVN_GETDISPINFOA,
408   LVN_SETDISPINFOW, LVN_SETDISPINFOA,
409   LVN_ODFINDITEMW, LVN_ODFINDITEMA,
410   LVN_GETINFOTIPW, LVN_GETINFOTIPA,
411   0
412 };
413
414 static int get_ansi_notification(INT unicodeNotificationCode)
415 {
416   int *pTabNotif = tabNotification;
417   while (*pTabNotif && (unicodeNotificationCode != *pTabNotif++));
418   if (*pTabNotif) return *pTabNotif;
419   ERR("unknown notification %x\n", unicodeNotificationCode);
420   return unicodeNotificationCode;
421 }
422
423 /*
424   Send notification. depends on dispinfoW having same
425   structure as dispinfoA.
426   self : listview handle
427   notificationCode : *Unicode* notification code
428   pdi : dispinfo structure (can be unicode or ansi)
429   isW : TRUE if dispinfo is Unicode
430 */
431 static BOOL dispinfo_notifyT(HWND self, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
432 {
433   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(self, 0);
434   BOOL bResult = FALSE;
435   BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
436   INT realNotifCode;
437   INT cchTempBufMax = 0, savCchTextMax = 0;
438   LPWSTR pszTempBuf = NULL, savPszText = NULL;
439
440   TRACE("(self=%x, code=%x, pdi=%p, isW=%d)\n", self, notificationCode, pdi, isW);
441   TRACE("   notifyFormat=%s\n",
442         infoPtr->notifyFormat == NFR_UNICODE ? "NFR_UNICODE" :
443         infoPtr->notifyFormat == NFR_ANSI ? "NFR_ANSI" : "(not set)");
444   if (infoPtr->notifyFormat == NFR_ANSI)
445     realNotifCode = get_ansi_notification(notificationCode);
446   else
447     realNotifCode = notificationCode;
448
449   if (is_textT(pdi->item.pszText, isW))
450   {
451     if (isW && infoPtr->notifyFormat == NFR_ANSI)
452         convertToAnsi = TRUE;
453     if (!isW && infoPtr->notifyFormat == NFR_UNICODE)
454         convertToUnicode = TRUE;
455   }
456
457   if (convertToAnsi || convertToUnicode)
458   {
459     TRACE("   we have to convert the text to the correct format\n");
460     if (notificationCode != LVN_GETDISPINFOW)
461     { /* length of existing text */
462       cchTempBufMax = convertToUnicode ?
463       MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
464       WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
465     }
466     else
467       cchTempBufMax = pdi->item.cchTextMax;
468
469     pszTempBuf = HeapAlloc(GetProcessHeap(), 0,
470         (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
471     if (!pszTempBuf) return FALSE;
472     if (convertToUnicode)
473       MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
474                           pszTempBuf, cchTempBufMax);
475     else
476       WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
477                           cchTempBufMax, NULL, NULL);
478     TRACE("   text=%s\n", debugstr_t(pszTempBuf, convertToUnicode));
479     savCchTextMax = pdi->item.cchTextMax;
480     savPszText = pdi->item.pszText;
481     pdi->item.pszText = pszTempBuf;
482     pdi->item.cchTextMax = cchTempBufMax;
483   }
484
485   bResult = notify(self, realNotifCode, (LPNMHDR)pdi);
486
487   if (convertToUnicode || convertToAnsi)
488   { /* convert back result */
489     TRACE("   returned text=%s\n", debugstr_t(pdi->item.pszText, convertToUnicode));
490     if (convertToUnicode) /* note : pointer can be changed by app ! */
491       WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
492                           savCchTextMax, NULL, NULL);
493     else
494       MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
495                           savPszText, savCchTextMax);
496     pdi->item.pszText = savPszText; /* restores our buffer */
497     pdi->item.cchTextMax = savCchTextMax;
498     HeapFree(GetProcessHeap(), 0, pszTempBuf);
499   }
500   return bResult;
501 }
502
503 static inline LRESULT LISTVIEW_GetItemW(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal)
504 {
505   return LISTVIEW_GetItemT(hwnd, lpLVItem, internal, TRUE);
506 }
507
508 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
509 {
510   int res;
511
512   n = min(min(n, strlenW(s1)), strlenW(s2));
513   res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
514   return res ? res - 2 : res;
515 }
516
517 static char* debuglvitem_t(LPLVITEMW lpLVItem, BOOL isW)
518 {
519   static int index = 0;
520   static char buffers[20][256];
521   char* buf = buffers[index++ % 20];
522   if (lpLVItem == NULL) return "(null)";
523   snprintf(buf, 256, "{mask=%x, iItem=%d, iSubItem=%d, state=%x, stateMask=%x,"
524            " pszText=%s, cchTextMax=%d, iImage=%d, lParam=%lx, iIndent=%d}",
525            lpLVItem->mask, lpLVItem->iItem, lpLVItem->iSubItem,
526            lpLVItem->state, lpLVItem->stateMask,
527            lpLVItem->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
528               lv_dmp_str(lpLVItem->pszText, isW, 80),
529            lpLVItem->cchTextMax, lpLVItem->iImage, lpLVItem->lParam,
530            lpLVItem->iIndent);
531   return buf;
532 }
533
534 static char* debuglvcolumn_t(LPLVCOLUMNW lpColumn, BOOL isW)
535 {
536   static int index = 0;
537   static char buffers[20][256];
538   char* buf = buffers[index++ % 20];
539   if (lpColumn == NULL) return "(null)";
540   snprintf(buf, 256, "{mask=%x, fmt=%x, cx=%d,"
541            " pszText=%s, cchTextMax=%d, iSubItem=%d}",
542            lpColumn->mask, lpColumn->fmt, lpColumn->cx,
543            lpColumn->mask & LVCF_TEXT ? lpColumn->pszText == LPSTR_TEXTCALLBACKW ? "(callback)" :
544               lv_dmp_str(lpColumn->pszText, isW, 80): "",
545            lpColumn->mask & LVCF_TEXT ? lpColumn->cchTextMax: 0, lpColumn->iSubItem);
546   return buf;
547 }
548
549 static void LISTVIEW_DumpListview(LISTVIEW_INFO *iP, INT line)
550 {
551   DWORD dwStyle = GetWindowLongW (iP->hwndSelf, GWL_STYLE);
552   TRACE("listview %08x at line %d, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n",
553         iP->hwndSelf, line, iP->clrBk, iP->clrText, iP->clrTextBk,
554         iP->nItemHeight, iP->nItemWidth, dwStyle);
555   TRACE("listview %08x at line %d, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx\n",
556         iP->hwndSelf, line, iP->himlNormal, iP->himlSmall, iP->himlState,
557         iP->nFocusedItem, iP->nHotItem, iP->dwExStyle);
558   TRACE("listview %08x at line %d, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld\n",
559         iP->hwndSelf, line, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy,
560         iP->iconSpacing.cx, iP->iconSpacing.cy);
561 }
562
563 static BOOL
564 LISTVIEW_SendCustomDrawNotify (HWND hwnd, DWORD dwDrawStage, HDC hdc,
565                                RECT rc)
566 {
567   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
568   NMLVCUSTOMDRAW nmcdhdr;
569   LPNMCUSTOMDRAW nmcd;
570
571   TRACE("(hwnd=%x, dwDrawStage=%lx, hdc=%x, rc=?)\n", hwnd, dwDrawStage, hdc);
572
573   nmcd= & nmcdhdr.nmcd;
574   nmcd->hdr.hwndFrom = hwnd;
575   nmcd->hdr.idFrom =  GetWindowLongW( hwnd, GWL_ID);
576   nmcd->hdr.code   = NM_CUSTOMDRAW;
577   nmcd->dwDrawStage= dwDrawStage;
578   nmcd->hdc        = hdc;
579   nmcd->rc.left    = rc.left;
580   nmcd->rc.right   = rc.right;
581   nmcd->rc.bottom  = rc.bottom;
582   nmcd->rc.top     = rc.top;
583   nmcd->dwItemSpec = 0;
584   nmcd->uItemState = 0;
585   nmcd->lItemlParam= 0;
586   nmcdhdr.clrText  = infoPtr->clrText;
587   nmcdhdr.clrTextBk= infoPtr->clrBk;
588
589   return (BOOL)SendMessageW (GetParent (hwnd), WM_NOTIFY,
590               (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
591 }
592
593 static BOOL
594 LISTVIEW_SendCustomDrawItemNotify (HWND hwnd, HDC hdc,
595                                    UINT iItem, UINT iSubItem,
596                                    UINT uItemDrawState)
597 {
598  LISTVIEW_INFO *infoPtr;
599  NMLVCUSTOMDRAW nmcdhdr;
600  LPNMCUSTOMDRAW nmcd;
601  DWORD dwDrawStage,dwItemSpec;
602  UINT uItemState;
603  INT retval;
604  RECT itemRect;
605  LVITEMW item;
606
607  infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
608
609  ZeroMemory(&item,sizeof(item));
610  item.iItem = iItem;
611  item.mask = LVIF_PARAM;
612  ListView_GetItemW(hwnd,&item);
613
614  dwDrawStage=CDDS_ITEM | uItemDrawState;
615  dwItemSpec=iItem;
616  uItemState=0;
617
618  if (LISTVIEW_IsSelected(hwnd,iItem)) uItemState|=CDIS_SELECTED;
619  if (iItem==infoPtr->nFocusedItem)   uItemState|=CDIS_FOCUS;
620  if (iItem==infoPtr->nHotItem)       uItemState|=CDIS_HOT;
621
622  itemRect.left = LVIR_BOUNDS;
623  LISTVIEW_GetItemRect(hwnd, iItem, &itemRect);
624
625  nmcd= & nmcdhdr.nmcd;
626  nmcd->hdr.hwndFrom = hwnd;
627  nmcd->hdr.idFrom =  GetWindowLongW( hwnd, GWL_ID);
628  nmcd->hdr.code   = NM_CUSTOMDRAW;
629  nmcd->dwDrawStage= dwDrawStage;
630  nmcd->hdc        = hdc;
631  nmcd->rc.left    = itemRect.left;
632  nmcd->rc.right   = itemRect.right;
633  nmcd->rc.bottom  = itemRect.bottom;
634  nmcd->rc.top     = itemRect.top;
635  nmcd->dwItemSpec = dwItemSpec;
636  nmcd->uItemState = uItemState;
637  nmcd->lItemlParam= item.lParam;
638  nmcdhdr.clrText  = infoPtr->clrText;
639  nmcdhdr.clrTextBk= infoPtr->clrBk;
640  nmcdhdr.iSubItem   =iSubItem;
641
642  TRACE("drawstage=%lx hdc=%x item=%lx, itemstate=%x, lItemlParam=%lx\n",
643        nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
644        nmcd->uItemState, nmcd->lItemlParam);
645
646  retval=SendMessageW (GetParent (hwnd), WM_NOTIFY,
647                  (WPARAM) GetWindowLongW( hwnd, GWL_ID), (LPARAM)&nmcdhdr);
648
649  infoPtr->clrText=nmcdhdr.clrText;
650  infoPtr->clrBk  =nmcdhdr.clrTextBk;
651  return (BOOL) retval;
652 }
653
654
655 /*************************************************************************
656  *              LISTVIEW_ProcessLetterKeys
657  *
658  *  Processes keyboard messages generated by pressing the letter keys
659  *  on the keyboard.
660  *  What this does is perform a case insensitive search from the
661  *  current position with the following quirks:
662  *  - If two chars or more are pressed in quick succession we search
663  *    for the corresponding string (e.g. 'abc').
664  *  - If there is a delay we wipe away the current search string and
665  *    restart with just that char.
666  *  - If the user keeps pressing the same character, whether slowly or
667  *    fast, so that the search string is entirely composed of this
668  *    character ('aaaaa' for instance), then we search for first item
669  *    that starting with that character.
670  *  - If the user types the above character in quick succession, then
671  *    we must also search for the corresponding string ('aaaaa'), and
672  *    go to that string if there is a match.
673  *
674  * RETURNS
675  *
676  *  Zero.
677  *
678  * BUGS
679  *
680  *  - The current implementation has a list of characters it will
681  *    accept and it ignores averything else. In particular it will
682  *    ignore accentuated characters which seems to match what
683  *    Windows does. But I'm not sure it makes sense to follow
684  *    Windows there.
685  *  - We don't sound a beep when the search fails.
686  *
687  * SEE ALSO
688  *
689  *  TREEVIEW_ProcessLetterKeys
690  */
691 static INT LISTVIEW_ProcessLetterKeys(
692     HWND hwnd, /* handle to the window */
693     WPARAM charCode, /* the character code, the actual character */
694     LPARAM keyData /* key data */
695     )
696 {
697     LISTVIEW_INFO *infoPtr;
698     INT nItem;
699     INT nSize;
700     INT endidx,idx;
701     LVITEMW item;
702     WCHAR buffer[MAX_PATH];
703     DWORD timestamp,elapsed;
704
705     /* simple parameter checking */
706     if (!hwnd || !charCode || !keyData)
707         return 0;
708
709     infoPtr=(LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
710     if (!infoPtr)
711         return 0;
712
713     /* only allow the valid WM_CHARs through */
714     if (!isalnum(charCode) &&
715         charCode != '.' && charCode != '`' && charCode != '!' &&
716         charCode != '@' && charCode != '#' && charCode != '$' &&
717         charCode != '%' && charCode != '^' && charCode != '&' &&
718         charCode != '*' && charCode != '(' && charCode != ')' &&
719         charCode != '-' && charCode != '_' && charCode != '+' &&
720         charCode != '=' && charCode != '\\'&& charCode != ']' &&
721         charCode != '}' && charCode != '[' && charCode != '{' &&
722         charCode != '/' && charCode != '?' && charCode != '>' &&
723         charCode != '<' && charCode != ',' && charCode != '~')
724         return 0;
725
726     nSize=GETITEMCOUNT(infoPtr);
727     /* if there's one item or less, there is no where to go */
728     if (nSize <= 1)
729         return 0;
730
731     /* compute how much time elapsed since last keypress */
732     timestamp=GetTickCount();
733     if (timestamp > infoPtr->lastKeyPressTimestamp) {
734         elapsed=timestamp-infoPtr->lastKeyPressTimestamp;
735     } else {
736         elapsed=infoPtr->lastKeyPressTimestamp-timestamp;
737     }
738
739     /* update the search parameters */
740     infoPtr->lastKeyPressTimestamp=timestamp;
741     if (elapsed < KEY_DELAY) {
742         if (infoPtr->nSearchParamLength < COUNTOF(infoPtr->szSearchParam)) {
743             infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
744         }
745         if (infoPtr->charCode != charCode) {
746             infoPtr->charCode=charCode=0;
747         }
748     } else {
749         infoPtr->charCode=charCode;
750         infoPtr->szSearchParam[0]=charCode;
751         infoPtr->nSearchParamLength=1;
752         /* Redundant with the 1 char string */
753         charCode=0;
754     }
755
756     /* and search from the current position */
757     nItem=-1;
758     if (infoPtr->nFocusedItem >= 0) {
759         endidx=infoPtr->nFocusedItem;
760         idx=endidx;
761         /* if looking for single character match,
762          * then we must always move forward
763          */
764         if (infoPtr->nSearchParamLength == 1)
765             idx++;
766     } else {
767         endidx=nSize;
768         idx=0;
769     }
770     do {
771         if (idx == nSize) {
772             if (endidx == nSize)
773                 break;
774             idx=0;
775         }
776
777         /* get item */
778         ZeroMemory(&item, sizeof(item));
779         item.mask = LVIF_TEXT;
780         item.iItem = idx;
781         item.iSubItem = 0;
782         item.pszText = buffer;
783         item.cchTextMax = COUNTOF(buffer);
784         ListView_GetItemW( hwnd, &item );
785
786         /* check for a match */
787         if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
788             nItem=idx;
789             break;
790         } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
791                     (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
792             /* This would work but we must keep looking for a longer match */
793             nItem=idx;
794         }
795         idx++;
796     } while (idx != endidx);
797
798     if (nItem != -1) {
799         if (LISTVIEW_KeySelection(hwnd, nItem) != FALSE) {
800             /* refresh client area */
801             InvalidateRect(hwnd, NULL, TRUE);
802             UpdateWindow(hwnd);
803         }
804     }
805
806     return 0;
807 }
808
809 /*************************************************************************
810  * LISTVIEW_UpdateHeaderSize [Internal]
811  *
812  * Function to resize the header control
813  *
814  * PARAMS
815  *     hwnd             [I] handle to a window
816  *     nNewScrollPos    [I] Scroll Pos to Set
817  *
818  * RETURNS
819  *     nothing
820  *
821  * NOTES
822  */
823 static VOID LISTVIEW_UpdateHeaderSize(HWND hwnd, INT nNewScrollPos)
824 {
825     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
826     RECT winRect;
827     POINT point[2];
828
829     GetWindowRect(infoPtr->hwndHeader, &winRect);
830     point[0].x = winRect.left;
831     point[0].y = winRect.top;
832     point[1].x = winRect.right;
833     point[1].y = winRect.bottom;
834
835     MapWindowPoints(HWND_DESKTOP, hwnd, point, 2);
836     point[0].x = -nNewScrollPos;
837     point[1].x += nNewScrollPos;
838
839     SetWindowPos(infoPtr->hwndHeader,0,
840         point[0].x,point[0].y,point[1].x,point[1].y,
841         SWP_NOZORDER | SWP_NOACTIVATE);
842 }
843
844 /***
845  * DESCRIPTION:
846  * Update the scrollbars. This functions should be called whenever
847  * the content, size or view changes.
848  *
849  * PARAMETER(S):
850  * [I] HWND : window handle
851  *
852  * RETURN:
853  * None
854  */
855 static VOID LISTVIEW_UpdateScroll(HWND hwnd)
856 {
857   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
858   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
859   UINT uView =  lStyle & LVS_TYPEMASK;
860   INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
861   INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
862   SCROLLINFO scrollInfo;
863
864   if (lStyle & LVS_NOSCROLL) return;
865
866   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
867   scrollInfo.cbSize = sizeof(SCROLLINFO);
868
869   if (uView == LVS_LIST)
870   {
871     /* update horizontal scrollbar */
872
873     INT nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
874     INT nCountPerRow = LISTVIEW_GetCountPerRow(hwnd);
875     INT nNumOfItems = GETITEMCOUNT(infoPtr);
876
877     scrollInfo.nMax = nNumOfItems / nCountPerColumn;
878     TRACE("items=%d, perColumn=%d, perRow=%d\n",
879           nNumOfItems, nCountPerColumn, nCountPerRow);
880     if((nNumOfItems % nCountPerColumn) == 0)
881     {
882         scrollInfo.nMax--;
883     }
884     scrollInfo.nPos = ListView_GetTopIndex(hwnd) / nCountPerColumn;
885     scrollInfo.nPage = nCountPerRow;
886     scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
887     TRACE("LVS_LIST\n");
888     SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
889     ShowScrollBar(hwnd, SB_VERT, FALSE);
890   }
891   else if (uView == LVS_REPORT)
892   {
893     BOOL test;
894
895     /* update vertical scrollbar */
896     scrollInfo.nMin = 0;
897     scrollInfo.nMax = GETITEMCOUNT(infoPtr) - 1;
898     scrollInfo.nPos = ListView_GetTopIndex(hwnd);
899     scrollInfo.nPage = LISTVIEW_GetCountPerColumn(hwnd);
900     scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
901     test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
902     TRACE("LVS_REPORT Vert. nMax=%d, nPage=%d, test=%d\n",
903           scrollInfo.nMax, scrollInfo.nPage, test);
904     SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
905     ShowScrollBar(hwnd, SB_VERT, (test) ? FALSE : TRUE);
906
907     /* update horizontal scrollbar */
908     nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
909     if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
910        || GETITEMCOUNT(infoPtr) == 0)
911     {
912       scrollInfo.nPos = 0;
913     }
914     scrollInfo.nMin = 0;
915     scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE  ;
916     scrollInfo.nPage = nListWidth;
917     scrollInfo.nMax = max(infoPtr->nItemWidth, 0)-1;
918     test = (scrollInfo.nMin >= scrollInfo.nMax - max((INT)scrollInfo.nPage - 1, 0));
919     TRACE("LVS_REPORT Horz. nMax=%d, nPage=%d, test=%d\n",
920           scrollInfo.nMax, scrollInfo.nPage, test);
921     SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
922     ShowScrollBar(hwnd, SB_HORZ, (test) ? FALSE : TRUE);
923
924     /* Update the Header Control */
925     scrollInfo.fMask = SIF_POS;
926     GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
927     LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
928
929   }
930   else
931   {
932     RECT rcView;
933
934     if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
935     {
936       INT nViewWidth = rcView.right - rcView.left;
937       INT nViewHeight = rcView.bottom - rcView.top;
938
939       /* Update Horizontal Scrollbar */
940       scrollInfo.fMask = SIF_POS;
941       if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) == FALSE
942         || GETITEMCOUNT(infoPtr) == 0)
943       {
944         scrollInfo.nPos = 0;
945       }
946       scrollInfo.nMax = max(nViewWidth, 0)-1;
947       scrollInfo.nMin = 0;
948       scrollInfo.nPage = nListWidth;
949       scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
950       TRACE("LVS_ICON/SMALLICON Horz.\n");
951       SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
952
953       /* Update Vertical Scrollbar */
954       nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
955       scrollInfo.fMask = SIF_POS;
956       if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) == FALSE
957         || GETITEMCOUNT(infoPtr) == 0)
958       {
959         scrollInfo.nPos = 0;
960       }
961       scrollInfo.nMax = max(nViewHeight,0)-1;
962       scrollInfo.nMin = 0;
963       scrollInfo.nPage = nListHeight;
964       scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE;
965       TRACE("LVS_ICON/SMALLICON Vert.\n");
966       SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
967     }
968   }
969 }
970
971 /***
972  * DESCRIPTION:
973  * Prints a message for unsupported window styles.
974  * A kind of TODO list for window styles.
975  *
976  * PARAMETER(S):
977  * [I] LONG : window style
978  *
979  * RETURN:
980  * None
981  */
982 static VOID LISTVIEW_UnsupportedStyles(LONG lStyle)
983 {
984   if ((LVS_TYPESTYLEMASK & lStyle) == LVS_NOSCROLL)
985     FIXME("  LVS_NOSCROLL\n");
986
987   if (lStyle & LVS_NOLABELWRAP)
988     FIXME("  LVS_NOLABELWRAP\n");
989
990   if (!(lStyle & LVS_SHAREIMAGELISTS))
991     FIXME("  !LVS_SHAREIMAGELISTS\n");
992
993   if (lStyle & LVS_SORTASCENDING)
994     FIXME("  LVS_SORTASCENDING\n");
995
996   if (lStyle & LVS_SORTDESCENDING)
997     FIXME("  LVS_SORTDESCENDING\n");
998 }
999
1000 /***
1001  * DESCRIPTION:
1002  * Aligns the items with the top edge of the window.
1003  *
1004  * PARAMETER(S):
1005  * [I] HWND : window handle
1006  *
1007  * RETURN:
1008  * None
1009  */
1010 static VOID LISTVIEW_AlignTop(HWND hwnd)
1011 {
1012   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1013   UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1014   INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1015   POINT ptItem;
1016   RECT rcView;
1017   INT i, off_x=0, off_y=0;
1018
1019   if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1020   {
1021     /* Since SetItemPosition uses upper-left of icon, and for
1022        style=LVS_ICON the icon is not left adjusted, get the offset */
1023     if (uView == LVS_ICON)
1024     {
1025       off_y = ICON_TOP_PADDING;
1026       off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1027     }
1028     ptItem.x = off_x;
1029     ptItem.y = off_y;
1030     ZeroMemory(&rcView, sizeof(RECT));
1031     TRACE("Icon  off.x=%d, off.y=%d, left=%d, right=%d\n",
1032           off_x, off_y,
1033           infoPtr->rcList.left, infoPtr->rcList.right);
1034
1035     if (nListWidth > infoPtr->nItemWidth)
1036     {
1037       for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1038       {
1039         if ((ptItem.x-off_x) + infoPtr->nItemWidth > nListWidth)
1040         {
1041           ptItem.x = off_x;
1042           ptItem.y += infoPtr->nItemHeight;
1043         }
1044
1045         LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1046         ptItem.x += infoPtr->nItemWidth;
1047         rcView.right = max(rcView.right, ptItem.x);
1048       }
1049
1050       rcView.right -= off_x;
1051       rcView.bottom = (ptItem.y-off_y) + infoPtr->nItemHeight;
1052     }
1053     else
1054     {
1055       for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1056       {
1057         LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1058         ptItem.y += infoPtr->nItemHeight;
1059       }
1060
1061       rcView.right = infoPtr->nItemWidth;
1062       rcView.bottom = ptItem.y-off_y;
1063     }
1064
1065     LISTVIEW_SetViewRect(hwnd, &rcView);
1066   }
1067 }
1068
1069 /***
1070  * DESCRIPTION:
1071  * Aligns the items with the left edge of the window.
1072  *
1073  * PARAMETER(S):
1074  * [I] HWND : window handle
1075  *
1076  * RETURN:
1077  * None
1078  */
1079 static VOID LISTVIEW_AlignLeft(HWND hwnd)
1080 {
1081   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1082   UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1083   INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1084   POINT ptItem;
1085   RECT rcView;
1086   INT i, off_x=0, off_y=0;
1087
1088   if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1089   {
1090     /* Since SetItemPosition uses upper-left of icon, and for
1091        style=LVS_ICON the icon is not left adjusted, get the offset */
1092     if (uView == LVS_ICON)
1093     {
1094       off_y = ICON_TOP_PADDING;
1095       off_x = (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
1096     }
1097     ptItem.x = off_x;
1098     ptItem.y = off_y;
1099     ZeroMemory(&rcView, sizeof(RECT));
1100     TRACE("Icon  off.x=%d, off.y=%d\n", off_x, off_y);
1101
1102     if (nListHeight > infoPtr->nItemHeight)
1103     {
1104       for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1105       {
1106         if (ptItem.y + infoPtr->nItemHeight > nListHeight)
1107         {
1108           ptItem.y = off_y;
1109           ptItem.x += infoPtr->nItemWidth;
1110         }
1111
1112         LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1113         ptItem.y += infoPtr->nItemHeight;
1114         rcView.bottom = max(rcView.bottom, ptItem.y);
1115       }
1116
1117       rcView.right = ptItem.x + infoPtr->nItemWidth;
1118     }
1119     else
1120     {
1121       for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1122       {
1123         LISTVIEW_SetItemPosition(hwnd, i, ptItem.x, ptItem.y);
1124         ptItem.x += infoPtr->nItemWidth;
1125       }
1126
1127       rcView.bottom = infoPtr->nItemHeight;
1128       rcView.right = ptItem.x;
1129     }
1130
1131     LISTVIEW_SetViewRect(hwnd, &rcView);
1132   }
1133 }
1134
1135 /***
1136  * DESCRIPTION:
1137  * Set the bounding rectangle of all the items.
1138  *
1139  * PARAMETER(S):
1140  * [I] HWND : window handle
1141  * [I] LPRECT : bounding rectangle
1142  *
1143  * RETURN:
1144  *   SUCCESS : TRUE
1145  *   FAILURE : FALSE
1146  */
1147 static LRESULT LISTVIEW_SetViewRect(HWND hwnd, LPRECT lprcView)
1148 {
1149   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
1150   BOOL bResult = FALSE;
1151
1152   TRACE("(hwnd=%x, left=%d, top=%d, right=%d, bottom=%d)\n", hwnd,
1153         lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1154
1155   if (lprcView != NULL)
1156   {
1157     bResult = TRUE;
1158     infoPtr->rcView.left = lprcView->left;
1159     infoPtr->rcView.top = lprcView->top;
1160     infoPtr->rcView.right = lprcView->right;
1161     infoPtr->rcView.bottom = lprcView->bottom;
1162   }
1163
1164   return bResult;
1165 }
1166
1167 /***
1168  * DESCRIPTION:
1169  * Retrieves the bounding rectangle of all the items.
1170  *
1171  * PARAMETER(S):
1172  * [I] HWND : window handle
1173  * [O] LPRECT : bounding rectangle
1174  *
1175  * RETURN:
1176  *   SUCCESS : TRUE
1177  *   FAILURE : FALSE
1178  */
1179 static LRESULT LISTVIEW_GetViewRect(HWND hwnd, LPRECT lprcView)
1180 {
1181   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
1182   BOOL bResult = FALSE;
1183   POINT ptOrigin;
1184
1185   TRACE("(hwnd=%x, lprcView=%p)\n", hwnd, lprcView);
1186
1187   if (lprcView != NULL)
1188   {
1189     bResult = LISTVIEW_GetOrigin(hwnd, &ptOrigin);
1190     if (bResult != FALSE)
1191     {
1192       lprcView->left = infoPtr->rcView.left + ptOrigin.x;
1193       lprcView->top = infoPtr->rcView.top + ptOrigin.y;
1194       lprcView->right = infoPtr->rcView.right + ptOrigin.x;
1195       lprcView->bottom = infoPtr->rcView.bottom + ptOrigin.y;
1196     }
1197
1198     TRACE("(left=%d, top=%d, right=%d, bottom=%d)\n",
1199           lprcView->left, lprcView->top, lprcView->right, lprcView->bottom);
1200   }
1201
1202   return bResult;
1203 }
1204
1205 /***
1206  * DESCRIPTION:
1207  * Retrieves the subitem pointer associated with the subitem index.
1208  *
1209  * PARAMETER(S):
1210  * [I] HDPA : DPA handle for a specific item
1211  * [I] INT : index of subitem
1212  *
1213  * RETURN:
1214  *   SUCCESS : subitem pointer
1215  *   FAILURE : NULL
1216  */
1217 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems,
1218                                                 INT nSubItem)
1219 {
1220   LISTVIEW_SUBITEM *lpSubItem;
1221   INT i;
1222
1223   for (i = 1; i < hdpaSubItems->nItemCount; i++)
1224   {
1225     lpSubItem = (LISTVIEW_SUBITEM *) DPA_GetPtr(hdpaSubItems, i);
1226     if (lpSubItem != NULL)
1227     {
1228       if (lpSubItem->iSubItem == nSubItem)
1229       {
1230         return lpSubItem;
1231       }
1232     }
1233   }
1234
1235   return NULL;
1236 }
1237
1238 /***
1239  * DESCRIPTION:
1240  * Calculates the width of an item.
1241  *
1242  * PARAMETER(S):
1243  * [I] HWND : window handle
1244  * [I] LONG : window style
1245  *
1246  * RETURN:
1247  * Returns item width.
1248  */
1249 static INT LISTVIEW_GetItemWidth(HWND hwnd)
1250 {
1251   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1252   LONG style = GetWindowLongW(hwnd, GWL_STYLE);
1253   UINT uView = style & LVS_TYPEMASK;
1254   INT nHeaderItemCount;
1255   RECT rcHeaderItem;
1256   INT nItemWidth = 0;
1257   INT nLabelWidth;
1258   INT i;
1259
1260   TRACE("(hwnd=%x)\n", hwnd);
1261
1262   if (uView == LVS_ICON)
1263   {
1264     nItemWidth = infoPtr->iconSpacing.cx;
1265   }
1266   else if (uView == LVS_REPORT)
1267   {
1268     /* calculate width of header */
1269     nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1270     for (i = 0; i < nHeaderItemCount; i++)
1271     {
1272       if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1273       {
1274         nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1275       }
1276     }
1277   }
1278   else  /* for LVS_SMALLICON and LVS_LIST */
1279   {
1280     for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
1281     {
1282       nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, i);
1283       nItemWidth = max(nItemWidth, nLabelWidth);
1284     }
1285
1286     /* default label size */
1287     if (GETITEMCOUNT(infoPtr) == 0)
1288     {
1289       nItemWidth = DEFAULT_COLUMN_WIDTH;
1290     }
1291     else
1292     {
1293       if (nItemWidth == 0)
1294       {
1295         nItemWidth = DEFAULT_LABEL_WIDTH;
1296       }
1297       else
1298       {
1299         /* add padding */
1300         nItemWidth += WIDTH_PADDING;
1301
1302         if (infoPtr->himlSmall != NULL)
1303         {
1304           nItemWidth += infoPtr->iconSize.cx;
1305         }
1306
1307         if (infoPtr->himlState != NULL)
1308         {
1309           nItemWidth += infoPtr->iconSize.cx;
1310         }
1311         nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth);
1312       }
1313     }
1314   }
1315   if(nItemWidth == 0)
1316   {
1317       /* nItemWidth Cannot be Zero */
1318       nItemWidth = 1;
1319   }
1320   return nItemWidth;
1321 }
1322
1323 /***
1324  * DESCRIPTION:
1325  * Calculates the width of a specific item.
1326  *
1327  * PARAMETER(S):
1328  * [I] HWND : window handle
1329  * [I] LPSTR : string
1330  *
1331  * RETURN:
1332  * Returns the width of an item width a specified string.
1333  */
1334 static INT LISTVIEW_CalculateWidth(HWND hwnd, INT nItem)
1335 {
1336   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1337   UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1338   INT nHeaderItemCount;
1339   RECT rcHeaderItem;
1340   INT nItemWidth = 0;
1341   INT i;
1342
1343   TRACE("(hwnd=%x)\n", hwnd);
1344
1345   if (uView == LVS_ICON)
1346   {
1347     nItemWidth = infoPtr->iconSpacing.cx;
1348   }
1349   else if (uView == LVS_REPORT)
1350   {
1351     /* calculate width of header */
1352     nHeaderItemCount = Header_GetItemCount(infoPtr->hwndHeader);
1353     for (i = 0; i < nHeaderItemCount; i++)
1354     {
1355       if (Header_GetItemRect(infoPtr->hwndHeader, i, &rcHeaderItem) != 0)
1356       {
1357         nItemWidth += (rcHeaderItem.right - rcHeaderItem.left);
1358       }
1359     }
1360   }
1361   else
1362   {
1363     /* get width of string */
1364     nItemWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
1365
1366     /* default label size */
1367     if (GETITEMCOUNT(infoPtr) == 0)
1368     {
1369       nItemWidth = DEFAULT_COLUMN_WIDTH;
1370     }
1371     else
1372     {
1373       if (nItemWidth == 0)
1374       {
1375         nItemWidth = DEFAULT_LABEL_WIDTH;
1376       }
1377       else
1378       {
1379         /* add padding */
1380         nItemWidth += WIDTH_PADDING;
1381
1382         if (infoPtr->himlSmall != NULL)
1383         {
1384           nItemWidth += infoPtr->iconSize.cx;
1385         }
1386
1387         if (infoPtr->himlState != NULL)
1388         {
1389           nItemWidth += infoPtr->iconSize.cx;
1390         }
1391       }
1392     }
1393   }
1394
1395   return nItemWidth;
1396 }
1397
1398 /***
1399  * DESCRIPTION:
1400  * Retrieves and saves important text metrics info for the current
1401  * Listview font.
1402  *
1403  * PARAMETER(S):
1404  * [I] HWND : window handle
1405  *
1406  */
1407 static VOID LISTVIEW_SaveTextMetrics(HWND hwnd)
1408 {
1409   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1410   TEXTMETRICW tm;
1411   HDC hdc = GetDC(hwnd);
1412   HFONT hOldFont = SelectObject(hdc, infoPtr->hFont);
1413   INT oldHeight, oldACW;
1414
1415   GetTextMetricsW(hdc, &tm);
1416
1417   oldHeight = infoPtr->ntmHeight;
1418   oldACW = infoPtr->ntmAveCharWidth;
1419   infoPtr->ntmHeight = tm.tmHeight;
1420   infoPtr->ntmAveCharWidth = tm.tmAveCharWidth;
1421
1422   SelectObject(hdc, hOldFont);
1423   ReleaseDC(hwnd, hdc);
1424   TRACE("tmHeight old=%d,new=%d; tmAveCharWidth old=%d,new=%d\n",
1425         oldHeight, infoPtr->ntmHeight, oldACW, infoPtr->ntmAveCharWidth);
1426 }
1427
1428
1429 /***
1430  * DESCRIPTION:
1431  * Calculates the height of an item.
1432  *
1433  * PARAMETER(S):
1434  * [I] HWND : window handle
1435  *
1436  * RETURN:
1437  * Returns item height.
1438  */
1439 static INT LISTVIEW_GetItemHeight(HWND hwnd)
1440 {
1441   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1442   UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
1443   INT nItemHeight = 0;
1444
1445   if (uView == LVS_ICON)
1446   {
1447     nItemHeight = infoPtr->iconSpacing.cy;
1448   }
1449   else
1450   {
1451     if(infoPtr->himlState || infoPtr->himlSmall)
1452       nItemHeight = max(infoPtr->ntmHeight, infoPtr->iconSize.cy) + HEIGHT_PADDING;
1453     else
1454       nItemHeight = infoPtr->ntmHeight;
1455   }
1456
1457   return nItemHeight;
1458 }
1459
1460
1461 static void LISTVIEW_PrintSelectionRanges(HWND hwnd)
1462 {
1463   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1464   LISTVIEW_SELECTION *selection;
1465   INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1466   INT i;
1467
1468   TRACE("Selections are:\n");
1469   for (i = 0; i < topSelection; i++)
1470   {
1471     selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,i);
1472     TRACE("     %lu - %lu\n",selection->lower,selection->upper);
1473   }
1474 }
1475
1476 /***
1477  * DESCRIPTION:
1478  * A compare function for selection ranges
1479  *
1480  *PARAMETER(S)
1481  * [I] LPVOID : Item 1;
1482  * [I] LPVOID : Item 2;
1483  * [I] LPARAM : flags
1484  *
1485  *RETURNS:
1486  * >0 : if Item 1 > Item 2
1487  * <0 : if Item 2 > Item 1
1488  * 0 : if Item 1 == Item 2
1489  */
1490 static INT CALLBACK LISTVIEW_CompareSelectionRanges(LPVOID range1, LPVOID range2,
1491                                                     LPARAM flags)
1492 {
1493   int l1 = ((LISTVIEW_SELECTION*)(range1))->lower;
1494   int l2 = ((LISTVIEW_SELECTION*)(range2))->lower;
1495   int u1 = ((LISTVIEW_SELECTION*)(range1))->upper;
1496   int u2 = ((LISTVIEW_SELECTION*)(range2))->upper;
1497   int rc=0;
1498
1499   if (u1 < l2)
1500     rc= -1;
1501
1502   if (u2 < l1)
1503      rc= 1;
1504
1505   return rc;
1506 }
1507
1508 /**
1509 * DESCRIPTION:
1510 * Adds a selection range.
1511 *
1512 * PARAMETER(S):
1513 * [I] HWND : window handle
1514 * [I] INT : lower item index
1515 * [I] INT : upper item index
1516 *
1517 * RETURN:
1518 * None
1519 */
1520 static VOID LISTVIEW_AddSelectionRange(HWND hwnd, INT lItem, INT uItem)
1521 {
1522  LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1523  LISTVIEW_SELECTION *selection;
1524  INT topSelection = infoPtr->hdpaSelectionRanges->nItemCount;
1525  BOOL lowerzero=FALSE;
1526
1527  selection = (LISTVIEW_SELECTION *)COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1528  selection->lower = lItem;
1529  selection->upper = uItem;
1530
1531  TRACE("Add range %i - %i\n", lItem, uItem);
1532  if (topSelection)
1533  {
1534    LISTVIEW_SELECTION *checkselection,*checkselection2;
1535    INT index,mergeindex;
1536
1537    /* find overlapping selections */
1538    /* we want to catch adjacent ranges so expand our range by 1 */
1539
1540    selection->upper++;
1541    if (selection->lower == 0)
1542      lowerzero = TRUE;
1543    else
1544      selection->lower--;
1545
1546    index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1547                       LISTVIEW_CompareSelectionRanges,
1548                       0,0);
1549    selection->upper --;
1550    if (lowerzero)
1551      lowerzero=FALSE;
1552    else
1553      selection->lower ++;
1554
1555    if (index >=0)
1556    {
1557      checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1558      TRACE("Merge with index %i (%lu - %lu)\n",index,checkselection->lower,
1559            checkselection->upper);
1560
1561      checkselection->lower = min(selection->lower,checkselection->lower);
1562      checkselection->upper = max(selection->upper,checkselection->upper);
1563
1564      TRACE("New range (%lu - %lu)\n", checkselection->lower,
1565            checkselection->upper);
1566
1567      COMCTL32_Free(selection);
1568
1569      /* merge now common selection ranges in the lower group*/
1570      do
1571      {
1572         checkselection->upper ++;
1573         if (checkselection->lower == 0)
1574           lowerzero = TRUE;
1575         else
1576           checkselection->lower --;
1577
1578         TRACE("search lower range (%lu - %lu)\n", checkselection->lower,
1579               checkselection->upper);
1580
1581         /* not sorted yet */
1582         mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection, 0,
1583                                 LISTVIEW_CompareSelectionRanges, 0,
1584                                 0);
1585
1586         checkselection->upper --;
1587         if (lowerzero)
1588           lowerzero = FALSE;
1589         else
1590           checkselection->lower ++;
1591
1592         if (mergeindex >=0  && mergeindex != index)
1593         {
1594           TRACE("Merge with index %i\n",mergeindex);
1595           checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1596                                        mergeindex);
1597           checkselection->lower = min(checkselection->lower,
1598                                       checkselection2->lower);
1599           checkselection->upper = max(checkselection->upper,
1600                                       checkselection2->upper);
1601           COMCTL32_Free(checkselection2);
1602           DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1603           index --;
1604         }
1605      }
1606      while (mergeindex > -1 && mergeindex <index);
1607
1608      /* merge now common selection ranges in the upper group*/
1609     do
1610     {
1611        checkselection->upper ++;
1612        if (checkselection->lower == 0)
1613          lowerzero = TRUE;
1614        else
1615          checkselection->lower --;
1616
1617        TRACE("search upper range %i (%lu - %lu)\n",index,
1618              checkselection->lower, checkselection->upper);
1619
1620        /* not sorted yet */
1621        mergeindex = DPA_Search(infoPtr->hdpaSelectionRanges, checkselection,
1622                                index+1,
1623                                LISTVIEW_CompareSelectionRanges, 0,
1624                                0);
1625
1626        checkselection->upper --;
1627        if (lowerzero)
1628          lowerzero = FALSE;
1629        else
1630          checkselection->lower ++;
1631
1632        if (mergeindex >=0 && mergeindex !=index)
1633        {
1634          TRACE("Merge with index %i\n",mergeindex);
1635          checkselection2 = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1636                                       mergeindex);
1637          checkselection->lower = min(checkselection->lower,
1638                                      checkselection2->lower);
1639          checkselection->upper = max(checkselection->upper,
1640                                      checkselection2->upper);
1641          COMCTL32_Free(checkselection2);
1642          DPA_DeletePtr(infoPtr->hdpaSelectionRanges,mergeindex);
1643        }
1644     }
1645     while (mergeindex > -1);
1646    }
1647    else
1648    {
1649
1650      index = DPA_Search(infoPtr->hdpaSelectionRanges, selection, 0,
1651                        LISTVIEW_CompareSelectionRanges, 0,
1652                        DPAS_INSERTAFTER);
1653
1654      TRACE("Insert before index %i\n",index);
1655      if (index == -1)
1656        index = 0;
1657      DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,selection);
1658    }
1659  }
1660  else
1661  {
1662    DPA_InsertPtr(infoPtr->hdpaSelectionRanges,0,selection);
1663  }
1664  /*
1665   * Incase of error
1666   */
1667  DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1668  LISTVIEW_PrintSelectionRanges(hwnd);
1669 }
1670
1671 /**
1672 * DESCRIPTION:
1673 * check if a specified index is selected.
1674 *
1675 * PARAMETER(S):
1676 * [I] HWND : window handle
1677 * [I] INT : item index
1678 *
1679 * RETURN:
1680 * None
1681 */
1682 static BOOL LISTVIEW_IsSelected(HWND hwnd, INT nItem)
1683 {
1684   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1685   LISTVIEW_SELECTION selection;
1686   INT index;
1687
1688   selection.upper = nItem;
1689   selection.lower = nItem;
1690
1691   index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1692                       LISTVIEW_CompareSelectionRanges,
1693                       0,DPAS_SORTED);
1694   if (index != -1)
1695     return TRUE;
1696   else
1697     return FALSE;
1698 }
1699
1700 /***
1701 * DESCRIPTION:
1702 * Removes all selection ranges
1703 *
1704 * Parameters(s):
1705 *   HWND: window handle
1706 *
1707 * RETURNS:
1708 *   SUCCESS : TRUE
1709 *   FAILURE : TRUE
1710 */
1711 static LRESULT LISTVIEW_RemoveAllSelections(HWND hwnd)
1712 {
1713   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1714   LISTVIEW_SELECTION *selection;
1715   INT i;
1716   LVITEMW item;
1717
1718   TRACE("(0x%x)\n",hwnd);
1719
1720   ZeroMemory(&item,sizeof(item));
1721   item.stateMask = LVIS_SELECTED;
1722
1723   do
1724   {
1725     selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
1726     if (selection)
1727     {
1728       TRACE("Removing %lu to %lu\n",selection->lower, selection->upper);
1729       for (i = selection->lower; i<=selection->upper; i++)
1730         LISTVIEW_SetItemState(hwnd,i,&item);
1731       LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,selection->upper);
1732     }
1733   }
1734   while (infoPtr->hdpaSelectionRanges->nItemCount>0);
1735
1736   TRACE("done\n");
1737   return TRUE;
1738 }
1739
1740 /**
1741 * DESCRIPTION:
1742 * Removes a range selections.
1743 *
1744 * PARAMETER(S):
1745 * [I] HWND : window handle
1746 * [I] INT : lower item index
1747 * [I] INT : upper item index
1748 *
1749 * RETURN:
1750 * None
1751 */
1752 static VOID LISTVIEW_RemoveSelectionRange(HWND hwnd, INT lItem, INT uItem)
1753 {
1754   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1755   LISTVIEW_SELECTION removeselection,*checkselection;
1756   INT index;
1757
1758   removeselection.lower = lItem;
1759   removeselection.upper = uItem;
1760
1761   TRACE("Remove range %lu - %lu\n",removeselection.lower,removeselection.upper);
1762   LISTVIEW_PrintSelectionRanges(hwnd);
1763
1764   index = DPA_Search(infoPtr->hdpaSelectionRanges, &removeselection, 0,
1765                      LISTVIEW_CompareSelectionRanges,
1766                      0,0);
1767
1768   if (index == -1)
1769     return;
1770
1771
1772   checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,
1773                               index);
1774
1775   TRACE("Matches range index %i (%lu-%lu)\n",index,checkselection->lower,
1776         checkselection->upper);
1777
1778   /* case 1: Same */
1779   if ((checkselection->upper == removeselection.upper) &&
1780      (checkselection->lower == removeselection.lower))
1781   {
1782     DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1783     TRACE("Case 1\n");
1784   }
1785   /* case 2: engulf */
1786   else if (((checkselection->upper < removeselection.upper) &&
1787       (checkselection->lower > removeselection.lower))||
1788      ((checkselection->upper <= removeselection.upper) &&
1789       (checkselection->lower > removeselection.lower)) ||
1790      ((checkselection->upper < removeselection.upper) &&
1791       (checkselection->lower >= removeselection.lower)))
1792
1793   {
1794     DPA_DeletePtr(infoPtr->hdpaSelectionRanges,index);
1795     /* do it again because others may also get caught */
1796     TRACE("Case 2\n");
1797     LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1798   }
1799   /* case 3: overlap upper */
1800   else if ((checkselection->upper < removeselection.upper) &&
1801       (checkselection->lower < removeselection.lower))
1802   {
1803     checkselection->upper = removeselection.lower - 1;
1804     TRACE("Case 3\n");
1805     LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1806   }
1807   /* case 4: overlap lower */
1808   else if ((checkselection->upper > removeselection.upper) &&
1809       (checkselection->lower > removeselection.lower))
1810   {
1811     checkselection->lower = removeselection.upper + 1;
1812     TRACE("Case 4\n");
1813     LISTVIEW_RemoveSelectionRange(hwnd,lItem,uItem);
1814   }
1815   /* case 5: fully internal */
1816   else if (checkselection->upper == removeselection.upper)
1817     checkselection->upper = removeselection.lower - 1;
1818   else if (checkselection->lower == removeselection.lower)
1819     checkselection->lower = removeselection.upper + 1;
1820   else
1821   {
1822     /* bisect the range */
1823     LISTVIEW_SELECTION *newselection;
1824
1825     newselection = (LISTVIEW_SELECTION *)
1826                           COMCTL32_Alloc(sizeof(LISTVIEW_SELECTION));
1827     newselection -> lower = checkselection->lower;
1828     newselection -> upper = removeselection.lower - 1;
1829     checkselection -> lower = removeselection.upper + 1;
1830     DPA_InsertPtr(infoPtr->hdpaSelectionRanges,index,newselection);
1831     TRACE("Case 5\n");
1832     DPA_Sort(infoPtr->hdpaSelectionRanges,LISTVIEW_CompareSelectionRanges,0);
1833   }
1834   LISTVIEW_PrintSelectionRanges(hwnd);
1835 }
1836
1837 /**
1838 * DESCRIPTION:
1839 * Updates the various indices after an item has been inserted or deleted.
1840 *
1841 * PARAMETER(S):
1842 * [I] HWND : window handle
1843 * [I] INT : item index
1844 * [I] INT : Direction of shift, +1 or -1.
1845 *
1846 * RETURN:
1847 * None
1848 */
1849 static VOID LISTVIEW_ShiftIndices(HWND hwnd, INT nItem, INT direction)
1850 {
1851   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1852   LISTVIEW_SELECTION selection,*checkselection;
1853   INT index;
1854
1855   TRACE("Shifting %iu, %i steps\n",nItem,direction);
1856
1857   selection.upper = nItem;
1858   selection.lower = nItem;
1859
1860   index = DPA_Search(infoPtr->hdpaSelectionRanges, &selection, 0,
1861                      LISTVIEW_CompareSelectionRanges,
1862                      0,DPAS_SORTED|DPAS_INSERTAFTER);
1863
1864   while ((index < infoPtr->hdpaSelectionRanges->nItemCount)&&(index != -1))
1865   {
1866     checkselection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,index);
1867     if ((checkselection->lower >= nItem)&&
1868        ((int)(checkselection->lower + direction) >= 0))
1869         checkselection->lower += direction;
1870     if ((checkselection->upper >= nItem)&&
1871        ((int)(checkselection->upper + direction) >= 0))
1872         checkselection->upper += direction;
1873     index ++;
1874   }
1875
1876   /* Note that the following will fail if direction != +1 and -1 */
1877   if (infoPtr->nSelectionMark > nItem)
1878       infoPtr->nSelectionMark += direction;
1879   else if (infoPtr->nSelectionMark == nItem)
1880   {
1881     if (direction > 0)
1882       infoPtr->nSelectionMark += direction;
1883     else if (infoPtr->nSelectionMark >= GETITEMCOUNT(infoPtr))
1884       infoPtr->nSelectionMark = GETITEMCOUNT(infoPtr) - 1;
1885   }
1886
1887   if (infoPtr->nFocusedItem > nItem)
1888     infoPtr->nFocusedItem += direction;
1889   else if (infoPtr->nFocusedItem == nItem)
1890   {
1891     if (direction > 0)
1892       infoPtr->nFocusedItem += direction;
1893     else
1894     {
1895       if (infoPtr->nFocusedItem >= GETITEMCOUNT(infoPtr))
1896         infoPtr->nFocusedItem = GETITEMCOUNT(infoPtr) - 1;
1897       if (infoPtr->nFocusedItem >= 0)
1898         LISTVIEW_SetItemFocus(hwnd, infoPtr->nFocusedItem);
1899     }
1900   }
1901   /* But we are not supposed to modify nHotItem! */
1902 }
1903
1904
1905 /**
1906  * DESCRIPTION:
1907  * Adds a block of selections.
1908  *
1909  * PARAMETER(S):
1910  * [I] HWND : window handle
1911  * [I] INT : item index
1912  *
1913  * RETURN:
1914  * None
1915  */
1916 static VOID LISTVIEW_AddGroupSelection(HWND hwnd, INT nItem)
1917 {
1918   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1919   INT nFirst = min(infoPtr->nSelectionMark, nItem);
1920   INT nLast = max(infoPtr->nSelectionMark, nItem);
1921   INT i;
1922   LVITEMW item;
1923
1924   if (nFirst == -1)
1925     nFirst = nItem;
1926
1927   ZeroMemory(&item,sizeof(item));
1928   item.stateMask = LVIS_SELECTED;
1929   item.state = LVIS_SELECTED;
1930
1931   for (i = nFirst; i <= nLast; i++)
1932     LISTVIEW_SetItemState(hwnd,i,&item);
1933
1934   LISTVIEW_SetItemFocus(hwnd, nItem);
1935   infoPtr->nSelectionMark = nItem;
1936 }
1937
1938
1939 /***
1940  * DESCRIPTION:
1941  * Adds a single selection.
1942  *
1943  * PARAMETER(S):
1944  * [I] HWND : window handle
1945  * [I] INT : item index
1946  *
1947  * RETURN:
1948  * None
1949  */
1950 static VOID LISTVIEW_AddSelection(HWND hwnd, INT nItem)
1951 {
1952   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1953   LVITEMW item;
1954
1955   ZeroMemory(&item,sizeof(item));
1956   item.state = LVIS_SELECTED;
1957   item.stateMask = LVIS_SELECTED;
1958
1959   LISTVIEW_SetItemState(hwnd,nItem,&item);
1960
1961   LISTVIEW_SetItemFocus(hwnd, nItem);
1962   infoPtr->nSelectionMark = nItem;
1963 }
1964
1965 /***
1966  * DESCRIPTION:
1967  * Selects or unselects an item.
1968  *
1969  * PARAMETER(S):
1970  * [I] HWND : window handle
1971  * [I] INT : item index
1972  *
1973  * RETURN:
1974  *   SELECT: TRUE
1975  *   UNSELECT : FALSE
1976  */
1977 static BOOL LISTVIEW_ToggleSelection(HWND hwnd, INT nItem)
1978 {
1979   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
1980   BOOL bResult;
1981   LVITEMW item;
1982
1983   ZeroMemory(&item,sizeof(item));
1984   item.stateMask = LVIS_SELECTED;
1985
1986   if (LISTVIEW_IsSelected(hwnd,nItem))
1987   {
1988     LISTVIEW_SetItemState(hwnd,nItem,&item);
1989     bResult = FALSE;
1990   }
1991   else
1992   {
1993     item.state = LVIS_SELECTED;
1994     LISTVIEW_SetItemState(hwnd,nItem,&item);
1995     bResult = TRUE;
1996   }
1997
1998   LISTVIEW_SetItemFocus(hwnd, nItem);
1999   infoPtr->nSelectionMark = nItem;
2000
2001   return bResult;
2002 }
2003
2004 /***
2005  * DESCRIPTION:
2006  * Selects items based on view coordinates.
2007  *
2008  * PARAMETER(S):
2009  * [I] HWND : window handle
2010  * [I] RECT : selection rectangle
2011  *
2012  * RETURN:
2013  * None
2014  */
2015 static VOID LISTVIEW_SetSelectionRect(HWND hwnd, RECT rcSelRect)
2016 {
2017   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2018   POINT ptItem;
2019   INT i;
2020   LVITEMW item;
2021
2022   ZeroMemory(&item,sizeof(item));
2023   item.stateMask = LVIS_SELECTED;
2024
2025   for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
2026   {
2027     LISTVIEW_GetItemPosition(hwnd, i, &ptItem);
2028
2029     if (PtInRect(&rcSelRect, ptItem) != FALSE)
2030       item.state = LVIS_SELECTED;
2031     else
2032       item.state = 0;
2033     LISTVIEW_SetItemState(hwnd,i,&item);
2034   }
2035 }
2036
2037 /***
2038  * DESCRIPTION:
2039  * Sets a single group selection.
2040  *
2041  * PARAMETER(S):
2042  * [I] HWND : window handle
2043  * [I] INT : item index
2044  *
2045  * RETURN:
2046  * None
2047  */
2048 static VOID LISTVIEW_SetGroupSelection(HWND hwnd, INT nItem)
2049 {
2050   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2051   UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
2052   LVITEMW item;
2053
2054   ZeroMemory(&item,sizeof(item));
2055   item.stateMask = LVIS_SELECTED;
2056
2057   if ((uView == LVS_LIST) || (uView == LVS_REPORT))
2058   {
2059     INT i;
2060     INT nFirst, nLast;
2061
2062     if (infoPtr->nSelectionMark == -1)
2063     {
2064       infoPtr->nSelectionMark = nFirst = nLast = nItem;
2065     }
2066     else
2067     {
2068       nFirst = min(infoPtr->nSelectionMark, nItem);
2069       nLast = max(infoPtr->nSelectionMark, nItem);
2070     }
2071
2072     for (i = 0; i <= GETITEMCOUNT(infoPtr); i++)
2073     {
2074       if ((i < nFirst) || (i > nLast))
2075         item.state = 0;
2076       else
2077         item.state = LVIS_SELECTED;
2078       LISTVIEW_SetItemState(hwnd,i,&item);
2079     }
2080   }
2081   else
2082   {
2083     RECT rcItem;
2084     RECT rcSelMark;
2085     RECT rcSel;
2086     LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcItem);
2087     LISTVIEW_GetItemBoundBox(hwnd, infoPtr->nSelectionMark, &rcSelMark);
2088     rcSel.left = min(rcSelMark.left, rcItem.left);
2089     rcSel.top = min(rcSelMark.top, rcItem.top);
2090     rcSel.right = max(rcSelMark.right, rcItem.right);
2091     rcSel.bottom = max(rcSelMark.bottom, rcItem.bottom);
2092     LISTVIEW_SetSelectionRect(hwnd, rcSel);
2093     TRACE("item %d (%d,%d)-(%d,%d), mark %d (%d,%d)-(%d,%d), sel (%d,%d)-(%d,%d)\n",
2094           nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
2095           infoPtr->nSelectionMark,
2096           rcSelMark.left, rcSelMark.top, rcSelMark.right, rcSelMark.bottom,
2097           rcSel.left, rcSel.top, rcSel.right, rcSel.bottom);
2098
2099   }
2100
2101   LISTVIEW_SetItemFocus(hwnd, nItem);
2102 }
2103
2104 /***
2105  * DESCRIPTION:
2106  * Manages the item focus.
2107  *
2108  * PARAMETER(S):
2109  * [I] HWND : window handle
2110  * [I] INT : item index
2111  *
2112  * RETURN:
2113  *   TRUE : focused item changed
2114  *   FALSE : focused item has NOT changed
2115  */
2116 static BOOL LISTVIEW_SetItemFocus(HWND hwnd, INT nItem)
2117 {
2118   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2119   BOOL bResult = FALSE;
2120   LVITEMW lvItem;
2121
2122   if (infoPtr->nFocusedItem != nItem)
2123   {
2124     if (infoPtr->nFocusedItem >= 0)
2125     {
2126       INT oldFocus = infoPtr->nFocusedItem;
2127       bResult = TRUE;
2128       infoPtr->nFocusedItem = -1;
2129       ZeroMemory(&lvItem, sizeof(lvItem));
2130       lvItem.stateMask = LVIS_FOCUSED;
2131       LISTVIEW_SetItemState(hwnd, oldFocus, &lvItem);
2132
2133     }
2134
2135     lvItem.state =  LVIS_FOCUSED;
2136     lvItem.stateMask = LVIS_FOCUSED;
2137     LISTVIEW_SetItemState(hwnd, nItem, &lvItem);
2138
2139     infoPtr->nFocusedItem = nItem;
2140     LISTVIEW_EnsureVisible(hwnd, nItem, FALSE);
2141   }
2142
2143   return bResult;
2144 }
2145
2146 /***
2147  * DESCRIPTION:
2148  * Sets a single selection.
2149  *
2150  * PARAMETER(S):
2151  * [I] HWND : window handle
2152  * [I] INT : item index
2153  *
2154  * RETURN:
2155  * None
2156  */
2157 static VOID LISTVIEW_SetSelection(HWND hwnd, INT nItem)
2158 {
2159   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2160   LVITEMW lvItem;
2161
2162   ZeroMemory(&lvItem, sizeof(lvItem));
2163   lvItem.stateMask = LVIS_FOCUSED;
2164   LISTVIEW_SetItemState(hwnd, infoPtr->nFocusedItem, &lvItem);
2165
2166   LISTVIEW_RemoveAllSelections(hwnd);
2167
2168   lvItem.state =   LVIS_FOCUSED|LVIS_SELECTED;
2169   lvItem.stateMask = LVIS_FOCUSED|LVIS_SELECTED;
2170   LISTVIEW_SetItemState(hwnd, nItem, &lvItem);
2171
2172   infoPtr->nFocusedItem = nItem;
2173   infoPtr->nSelectionMark = nItem;
2174 }
2175
2176 /***
2177  * DESCRIPTION:
2178  * Set selection(s) with keyboard.
2179  *
2180  * PARAMETER(S):
2181  * [I] HWND : window handle
2182  * [I] INT : item index
2183  *
2184  * RETURN:
2185  *   SUCCESS : TRUE (needs to be repainted)
2186  *   FAILURE : FALSE (nothing has changed)
2187  */
2188 static BOOL LISTVIEW_KeySelection(HWND hwnd, INT nItem)
2189 {
2190   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2191   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2192   WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
2193   WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
2194   BOOL bResult = FALSE;
2195
2196   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
2197   {
2198     if (lStyle & LVS_SINGLESEL)
2199     {
2200       bResult = TRUE;
2201       LISTVIEW_SetSelection(hwnd, nItem);
2202       ListView_EnsureVisible(hwnd, nItem, FALSE);
2203     }
2204     else
2205     {
2206       if (wShift)
2207       {
2208         bResult = TRUE;
2209         LISTVIEW_SetGroupSelection(hwnd, nItem);
2210       }
2211       else if (wCtrl)
2212       {
2213         bResult = LISTVIEW_SetItemFocus(hwnd, nItem);
2214       }
2215       else
2216       {
2217         bResult = TRUE;
2218         LISTVIEW_SetSelection(hwnd, nItem);
2219         ListView_EnsureVisible(hwnd, nItem, FALSE);
2220       }
2221     }
2222   }
2223
2224   return bResult;
2225 }
2226
2227 /***
2228  * DESCRIPTION:
2229  * Called when the mouse is being actively tracked and has hovered for a specified
2230  * amount of time
2231  *
2232  * PARAMETER(S):
2233  * [I] HWND : window handle
2234  * [I] wParam : key indicator
2235  * [I] lParam : mouse position
2236  *
2237  * RETURN:
2238  *   0 if the message was processed, non-zero if there was an error
2239  *
2240  * INFO:
2241  * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
2242  * over the item for a certain period of time.
2243  *
2244  */
2245 static LRESULT LISTVIEW_MouseHover(HWND hwnd, WPARAM wParam, LPARAM lParam)
2246 {
2247   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2248   POINT pt;
2249
2250   pt.x = (INT)LOWORD(lParam);
2251   pt.y = (INT)HIWORD(lParam);
2252
2253   if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2254     /* select the item under the cursor */
2255     LISTVIEW_MouseSelection(hwnd, pt);
2256   }
2257
2258   return 0;
2259 }
2260
2261 /***
2262  * DESCRIPTION:
2263  * Called whenever WM_MOUSEMOVE is received.
2264  *
2265  * PARAMETER(S):
2266  * [I] HWND : window handle
2267  * [I] wParam : key indicators
2268  * [I] lParam : cursor position
2269  *
2270  * RETURN:
2271  *   0 if the message is processed, non-zero if there was an error
2272  */
2273 static LRESULT LISTVIEW_MouseMove(HWND hwnd, WPARAM wParam, LPARAM lParam)
2274 {
2275   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2276   TRACKMOUSEEVENT trackinfo;
2277
2278   /* see if we are supposed to be tracking mouse hovering */
2279   if(infoPtr->dwExStyle & LVS_EX_TRACKSELECT) {
2280      /* fill in the trackinfo struct */
2281      trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
2282      trackinfo.dwFlags = TME_QUERY;
2283      trackinfo.hwndTrack = hwnd;
2284      trackinfo.dwHoverTime = infoPtr->dwHoverTime;
2285
2286      /* see if we are already tracking this hwnd */
2287      _TrackMouseEvent(&trackinfo);
2288
2289      if(!(trackinfo.dwFlags & TME_HOVER)) {
2290        trackinfo.dwFlags = TME_HOVER;
2291
2292        /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
2293        _TrackMouseEvent(&trackinfo);
2294     }
2295   }
2296
2297   return 0;
2298 }
2299
2300 /***
2301  * DESCRIPTION:
2302  * Selects an item based on coordinates.
2303  *
2304  * PARAMETER(S):
2305  * [I] HWND : window handle
2306  * [I] POINT : mouse click ccordinates
2307  *
2308  * RETURN:
2309  *   SUCCESS : item index
2310  *   FAILURE : -1
2311  */
2312 static LRESULT LISTVIEW_MouseSelection(HWND hwnd, POINT pt)
2313 {
2314   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2315   RECT rcItem;
2316   INT i,topindex,bottomindex;
2317   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2318   UINT uView = lStyle & LVS_TYPEMASK;
2319
2320   topindex = LISTVIEW_GetTopIndex(hwnd);
2321   if (uView == LVS_REPORT)
2322   {
2323     bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
2324     bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
2325   }
2326   else
2327   {
2328     bottomindex = GETITEMCOUNT(infoPtr);
2329   }
2330
2331   for (i = topindex; i < bottomindex; i++)
2332   {
2333     rcItem.left = LVIR_SELECTBOUNDS;
2334     if (LISTVIEW_GetItemRect(hwnd, i, &rcItem) == TRUE)
2335     {
2336       if (PtInRect(&rcItem, pt) != FALSE)
2337       {
2338         return i;
2339       }
2340     }
2341   }
2342
2343   return -1;
2344 }
2345
2346 /***
2347  * DESCRIPTION:
2348  * Removes a column.
2349  *
2350  * PARAMETER(S):
2351  * [IO] HDPA : dynamic pointer array handle
2352  * [I] INT : column index (subitem index)
2353  *
2354  * RETURN:
2355  *   SUCCCESS : TRUE
2356  *   FAILURE : FALSE
2357  */
2358 static BOOL LISTVIEW_RemoveColumn(HDPA hdpaItems, INT nSubItem)
2359 {
2360   BOOL bResult = TRUE;
2361   HDPA hdpaSubItems;
2362   INT i;
2363
2364   for (i = 0; i < hdpaItems->nItemCount; i++)
2365   {
2366     hdpaSubItems = (HDPA)DPA_GetPtr(hdpaItems, i);
2367     if (hdpaSubItems != NULL)
2368     {
2369       if (LISTVIEW_RemoveSubItem(hdpaSubItems, nSubItem) == FALSE)
2370       {
2371         bResult = FALSE;
2372       }
2373     }
2374   }
2375
2376   return bResult;
2377 }
2378
2379 /***
2380  * DESCRIPTION:
2381  * Removes a subitem at a given position.
2382  *
2383  * PARAMETER(S):
2384  * [IO] HDPA : dynamic pointer array handle
2385  * [I] INT : subitem index
2386  *
2387  * RETURN:
2388  *   SUCCCESS : TRUE
2389  *   FAILURE : FALSE
2390  */
2391 static BOOL LISTVIEW_RemoveSubItem(HDPA hdpaSubItems, INT nSubItem)
2392 {
2393   LISTVIEW_SUBITEM *lpSubItem;
2394   INT i;
2395
2396   for (i = 1; i < hdpaSubItems->nItemCount; i++)
2397   {
2398     lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2399     if (lpSubItem != NULL)
2400     {
2401       if (lpSubItem->iSubItem == nSubItem)
2402       {
2403         /* free string */
2404         if (is_textW(lpSubItem->pszText))
2405           COMCTL32_Free(lpSubItem->pszText);
2406
2407         /* free item */
2408         COMCTL32_Free(lpSubItem);
2409
2410         /* free dpa memory */
2411         if (DPA_DeletePtr(hdpaSubItems, i) == NULL)
2412           return FALSE;
2413       }
2414       else if (lpSubItem->iSubItem > nSubItem)
2415         return TRUE;
2416     }
2417   }
2418
2419   return TRUE;
2420 }
2421
2422 /***
2423  * DESCRIPTION:
2424  * Compares the item information.
2425  *
2426  * PARAMETER(S):
2427  * [I] LISTVIEW_ITEM *: destination item
2428  * [I] LPLVITEM : source item
2429  * [I] isW : TRUE if lpLVItem is Unicode, FALSE it it's ANSI
2430  *
2431  * RETURN:
2432  *   SUCCCESS : TRUE (EQUAL)
2433  *   FAILURE : FALSE (NOT EQUAL)
2434  */
2435 static UINT LISTVIEW_GetItemChangesT(LISTVIEW_ITEM *lpItem, LPLVITEMW lpLVItem, BOOL isW)
2436 {
2437   UINT uChanged = 0;
2438
2439   if ((lpItem != NULL) && (lpLVItem != NULL))
2440   {
2441     if (lpLVItem->mask & LVIF_STATE)
2442     {
2443       if ((lpItem->state & lpLVItem->stateMask) !=
2444           (lpLVItem->state & lpLVItem->stateMask))
2445         uChanged |= LVIF_STATE;
2446     }
2447
2448     if (lpLVItem->mask & LVIF_IMAGE)
2449     {
2450       if (lpItem->iImage != lpLVItem->iImage)
2451         uChanged |= LVIF_IMAGE;
2452     }
2453
2454     if (lpLVItem->mask & LVIF_PARAM)
2455     {
2456       if (lpItem->lParam != lpLVItem->lParam)
2457         uChanged |= LVIF_PARAM;
2458     }
2459
2460     if (lpLVItem->mask & LVIF_INDENT)
2461     {
2462       if (lpItem->iIndent != lpLVItem->iIndent)
2463         uChanged |= LVIF_INDENT;
2464     }
2465
2466     if (lpLVItem->mask & LVIF_TEXT)
2467     {
2468       if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2469       {
2470         if (lpItem->pszText != LPSTR_TEXTCALLBACKW)
2471           uChanged |= LVIF_TEXT;
2472       }
2473       else
2474       {
2475         if (lpItem->pszText == LPSTR_TEXTCALLBACKW)
2476         {
2477           uChanged |= LVIF_TEXT;
2478         }
2479         else
2480         {
2481           if (lpLVItem->pszText)
2482           {
2483             if (lpItem->pszText)
2484             {
2485               LPWSTR pszText = textdupTtoW(lpLVItem->pszText, isW);
2486               if (pszText && strcmpW(pszText, lpItem->pszText))
2487                 uChanged |= LVIF_TEXT;
2488               textfreeT(pszText, isW);
2489             }
2490             else
2491             {
2492               uChanged |= LVIF_TEXT;
2493             }
2494           }
2495           else
2496           {
2497             if (lpItem->pszText)
2498               uChanged |= LVIF_TEXT;
2499           }
2500         }
2501       }
2502     }
2503   }
2504   return uChanged;
2505 }
2506
2507 /***
2508  * DESCRIPTION:
2509  * Initializes item attributes.
2510  *
2511  * PARAMETER(S):
2512  * [I] HWND : window handle
2513  * [O] LISTVIEW_ITEM *: destination item
2514  * [I] LPLVITEM : source item
2515  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2516  *
2517  * RETURN:
2518  *   SUCCCESS : TRUE
2519  *   FAILURE : FALSE
2520  */
2521 static BOOL LISTVIEW_InitItemT(HWND hwnd, LISTVIEW_ITEM *lpItem,
2522                               LPLVITEMW lpLVItem, BOOL isW)
2523 {
2524   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2525   BOOL bResult = FALSE;
2526
2527   if ((lpItem != NULL) && (lpLVItem != NULL))
2528   {
2529     bResult = TRUE;
2530
2531     if (lpLVItem->mask & LVIF_STATE)
2532     {
2533       lpItem->state &= ~lpLVItem->stateMask;
2534       lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
2535     }
2536
2537     if (lpLVItem->mask & LVIF_IMAGE)
2538       lpItem->iImage = lpLVItem->iImage;
2539
2540     if (lpLVItem->mask & LVIF_PARAM)
2541       lpItem->lParam = lpLVItem->lParam;
2542
2543     if (lpLVItem->mask & LVIF_INDENT)
2544       lpItem->iIndent = lpLVItem->iIndent;
2545
2546     if (lpLVItem->mask & LVIF_TEXT)
2547     {
2548       if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2549       {
2550         if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2551           return FALSE;
2552
2553         if (is_textW(lpItem->pszText))
2554           COMCTL32_Free(lpItem->pszText);
2555
2556         lpItem->pszText = LPSTR_TEXTCALLBACKW;
2557       }
2558       else
2559         bResult = textsetptrT(&lpItem->pszText, lpLVItem->pszText, isW);
2560     }
2561   }
2562
2563   return bResult;
2564 }
2565
2566 /***
2567  * DESCRIPTION:
2568  * Initializes subitem attributes.
2569  *
2570  * NOTE: The documentation specifies that the operation fails if the user
2571  * tries to set the indent of a subitem.
2572  *
2573  * PARAMETER(S):
2574  * [I] HWND : window handle
2575  * [O] LISTVIEW_SUBITEM *: destination subitem
2576  * [I] LPLVITEM : source subitem
2577  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2578  *
2579  * RETURN:
2580  *   SUCCCESS : TRUE
2581  *   FAILURE : FALSE
2582  */
2583 static BOOL LISTVIEW_InitSubItemT(HWND hwnd, LISTVIEW_SUBITEM *lpSubItem,
2584                                   LPLVITEMW lpLVItem, BOOL isW)
2585 {
2586   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2587   BOOL bResult = FALSE;
2588
2589   TRACE("(hwnd=%x, lpSubItem=%p, lpLVItem=%s, isW=%d)\n",
2590         hwnd, lpSubItem, debuglvitem_t(lpLVItem, isW), isW);
2591
2592   if ((lpSubItem != NULL) && (lpLVItem != NULL))
2593   {
2594     if (!(lpLVItem->mask & LVIF_INDENT))
2595     {
2596       bResult = TRUE;
2597
2598       lpSubItem->iSubItem = lpLVItem->iSubItem;
2599
2600       if (lpLVItem->mask & LVIF_IMAGE)
2601         lpSubItem->iImage = lpLVItem->iImage;
2602
2603       if (lpLVItem->mask & LVIF_TEXT)
2604       {
2605         if (lpLVItem->pszText == LPSTR_TEXTCALLBACKW)
2606         {
2607           if ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
2608             return FALSE;
2609
2610           if (is_textW(lpSubItem->pszText))
2611             COMCTL32_Free(lpSubItem->pszText);
2612
2613           lpSubItem->pszText = LPSTR_TEXTCALLBACKW;
2614         }
2615         else
2616           bResult = textsetptrT(&lpSubItem->pszText, lpLVItem->pszText, isW);
2617       }
2618     }
2619   }
2620
2621   return bResult;
2622 }
2623
2624 /***
2625  * DESCRIPTION:
2626  * Adds a subitem at a given position (column index).
2627  *
2628  * PARAMETER(S):
2629  * [I] HWND : window handle
2630  * [I] LPLVITEM : new subitem atttributes
2631  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2632  *
2633  * RETURN:
2634  *   SUCCESS : TRUE
2635  *   FAILURE : FALSE
2636  */
2637 static BOOL LISTVIEW_AddSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2638 {
2639   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2640   LISTVIEW_SUBITEM *lpSubItem = NULL;
2641   BOOL bResult = FALSE;
2642   HDPA hdpaSubItems;
2643   INT nPosition, nItem;
2644   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2645
2646   TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2647
2648   if (lStyle & LVS_OWNERDATA)
2649     return FALSE;
2650
2651   if (lpLVItem != NULL)
2652   {
2653     hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2654     if (hdpaSubItems != NULL)
2655     {
2656       lpSubItem = (LISTVIEW_SUBITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_SUBITEM));
2657       if (lpSubItem != NULL)
2658       {
2659         ZeroMemory(lpSubItem, sizeof(LISTVIEW_SUBITEM));
2660         if (LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW))
2661         {
2662           nPosition = LISTVIEW_FindInsertPosition(hdpaSubItems,
2663                                                   lpSubItem->iSubItem);
2664           nItem = DPA_InsertPtr(hdpaSubItems, nPosition, lpSubItem);
2665           if (nItem != -1) bResult = TRUE;
2666         }
2667       }
2668     }
2669   }
2670
2671   /* cleanup if unsuccessful */
2672   if (!bResult && lpSubItem) COMCTL32_Free(lpSubItem);
2673
2674   return bResult;
2675 }
2676
2677 /***
2678  * DESCRIPTION:
2679  * Finds the dpa insert position (array index).
2680  *
2681  * PARAMETER(S):
2682  * [I] HWND : window handle
2683  * [I] INT : subitem index
2684  *
2685  * RETURN:
2686  *   SUCCESS : TRUE
2687  *   FAILURE : FALSE
2688  */
2689 static INT LISTVIEW_FindInsertPosition(HDPA hdpaSubItems, INT nSubItem)
2690 {
2691   LISTVIEW_SUBITEM *lpSubItem;
2692   INT i;
2693
2694   for (i = 1; i < hdpaSubItems->nItemCount; i++)
2695   {
2696     lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2697     if (lpSubItem && lpSubItem->iSubItem > nSubItem)
2698       return i;
2699   }
2700
2701   return hdpaSubItems->nItemCount;
2702 }
2703
2704 /***
2705  * DESCRIPTION:
2706  * Retrieves a listview subitem at a given position (column index).
2707  *
2708  * PARAMETER(S):
2709  * [I] HWND : window handle
2710  * [I] INT : subitem index
2711  *
2712  * RETURN:
2713  *   SUCCESS : TRUE
2714  *   FAILURE : FALSE
2715  */
2716 static LISTVIEW_SUBITEM* LISTVIEW_GetSubItem(HDPA hdpaSubItems, INT nSubItem)
2717 {
2718   LISTVIEW_SUBITEM *lpSubItem;
2719   INT i;
2720
2721   for (i = 1; i < hdpaSubItems->nItemCount; i++)
2722   {
2723     lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
2724     if (lpSubItem != NULL)
2725     {
2726       if (lpSubItem->iSubItem == nSubItem)
2727         return lpSubItem;
2728       else if (lpSubItem->iSubItem > nSubItem)
2729         return NULL;
2730     }
2731   }
2732
2733   return NULL;
2734 }
2735
2736 /***
2737  * DESCRIPTION:
2738  * Sets item attributes.
2739  *
2740  * PARAMETER(S):
2741  * [I] HWND : window handle
2742  * [I] LPLVITEM : new item atttributes
2743  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2744  *
2745  * RETURN:
2746  *   SUCCESS : TRUE
2747  *   FAILURE : FALSE
2748  */
2749 static BOOL LISTVIEW_SetMainItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2750 {
2751   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2752   BOOL bResult = FALSE;
2753   HDPA hdpaSubItems;
2754   LISTVIEW_ITEM *lpItem;
2755   NMLISTVIEW nmlv;
2756   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2757   UINT uChanged;
2758   UINT uView = lStyle & LVS_TYPEMASK;
2759   INT item_width;
2760   RECT rcItem;
2761
2762   TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2763
2764   if (lStyle & LVS_OWNERDATA)
2765   {
2766     if ((lpLVItem->iSubItem == 0)&&(lpLVItem->mask == LVIF_STATE))
2767     {
2768       LVITEMW itm;
2769
2770       ZeroMemory(&itm, sizeof(itm));
2771       itm.mask = LVIF_STATE | LVIF_PARAM;
2772       itm.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
2773       itm.iItem = lpLVItem->iItem;
2774       itm.iSubItem = 0;
2775       ListView_GetItemW(hwnd, &itm);
2776
2777
2778       ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2779       nmlv.uNewState = lpLVItem->state;
2780       nmlv.uOldState = itm.state;
2781       nmlv.uChanged = LVIF_STATE;
2782       nmlv.lParam = itm.lParam;
2783       nmlv.iItem = lpLVItem->iItem;
2784
2785       if ((itm.state & lpLVItem->stateMask) !=
2786           (lpLVItem->state & lpLVItem->stateMask))
2787       {
2788         /*
2789          * As per MSDN LVN_ITEMCHANGING notifications are _NOT_ sent
2790          * by LVS_OWERNDATA list controls
2791          */
2792           if (lpLVItem->stateMask & LVIS_FOCUSED)
2793           {
2794             if (lpLVItem->state & LVIS_FOCUSED)
2795               infoPtr->nFocusedItem = lpLVItem->iItem;
2796             else if (infoPtr->nFocusedItem == lpLVItem->iItem)
2797               infoPtr->nFocusedItem = -1;
2798           }
2799           if (lpLVItem->stateMask & LVIS_SELECTED)
2800           {
2801             if (lpLVItem->state & LVIS_SELECTED)
2802             {
2803               if (lStyle & LVS_SINGLESEL) LISTVIEW_RemoveAllSelections(hwnd);
2804               LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,lpLVItem->iItem);
2805             }
2806             else
2807               LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2808                                             lpLVItem->iItem);
2809           }
2810
2811           listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2812
2813           rcItem.left = LVIR_BOUNDS;
2814           LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2815       if (!infoPtr->bIsDrawing)
2816           InvalidateRect(hwnd, &rcItem, TRUE);
2817         }
2818       return TRUE;
2819     }
2820     return FALSE;
2821   }
2822
2823   if (lpLVItem != NULL)
2824   {
2825     if (lpLVItem->iSubItem == 0)
2826     {
2827       hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2828       if (hdpaSubItems != NULL && hdpaSubItems != (HDPA)-1)
2829       {
2830         lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, lpLVItem->iSubItem);
2831         if (lpItem != NULL)
2832         {
2833           ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
2834           nmlv.lParam = lpItem->lParam;
2835           uChanged = LISTVIEW_GetItemChangesT(lpItem, lpLVItem, isW);
2836           if (uChanged != 0)
2837           {
2838             if (uChanged & LVIF_STATE)
2839             {
2840               nmlv.uNewState = lpLVItem->state & lpLVItem->stateMask;
2841               nmlv.uOldState = lpItem->state & lpLVItem->stateMask;
2842
2843               if (nmlv.uNewState & LVIS_SELECTED)
2844               {
2845                 /*
2846                  * This is redundant if called through SetSelection
2847                  *
2848                  * however is required if the used directly calls SetItem
2849                  * to set the selection.
2850                  */
2851                 if (lStyle & LVS_SINGLESEL)
2852                   LISTVIEW_RemoveAllSelections(hwnd);
2853
2854                 LISTVIEW_AddSelectionRange(hwnd,lpLVItem->iItem,
2855                                              lpLVItem->iItem);
2856               }
2857               else if (lpLVItem->stateMask & LVIS_SELECTED)
2858               {
2859                 LISTVIEW_RemoveSelectionRange(hwnd,lpLVItem->iItem,
2860                                               lpLVItem->iItem);
2861               }
2862               if (nmlv.uNewState & LVIS_FOCUSED)
2863               {
2864                 /*
2865                  * This is a fun hoop to jump to try to catch if
2866                  * the user is calling us directly to call focus or if
2867                  * this function is being called as a result of a
2868                  * SetItemFocus call.
2869                  */
2870                 if (infoPtr->nFocusedItem >= 0)
2871                   LISTVIEW_SetItemFocus(hwnd, lpLVItem->iItem);
2872               }
2873             }
2874
2875             nmlv.uChanged = uChanged;
2876             nmlv.iItem = lpLVItem->iItem;
2877             nmlv.lParam = lpItem->lParam;
2878             /* send LVN_ITEMCHANGING notification */
2879             listview_notify(hwnd, LVN_ITEMCHANGING, &nmlv);
2880
2881             /* copy information */
2882             bResult = LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW);
2883
2884             /* if LVS_LIST or LVS_SMALLICON, update the width of the items
2885                based on the width of the items text */
2886             if((uView == LVS_LIST) || (uView == LVS_SMALLICON))
2887             {
2888               item_width = LISTVIEW_GetStringWidthT(hwnd, lpItem->pszText, TRUE);
2889
2890               if(item_width > infoPtr->nItemWidth)
2891                   infoPtr->nItemWidth = item_width;
2892             }
2893
2894             /* send LVN_ITEMCHANGED notification */
2895             listview_notify(hwnd, LVN_ITEMCHANGED, &nmlv);
2896           }
2897           else
2898           {
2899             bResult = TRUE;
2900           }
2901
2902           if (uChanged)
2903           {
2904             rcItem.left = LVIR_BOUNDS;
2905             LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2906            if (!infoPtr->bIsDrawing)
2907             InvalidateRect(hwnd, &rcItem, TRUE);
2908           }
2909         }
2910       }
2911     }
2912   }
2913
2914   return bResult;
2915 }
2916
2917 /***
2918  * DESCRIPTION:
2919  * Sets subitem attributes.
2920  *
2921  * PARAMETER(S):
2922  * [I] HWND : window handle
2923  * [I] LPLVITEM : new subitem atttributes
2924  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2925  *
2926  * RETURN:
2927  *   SUCCESS : TRUE
2928  *   FAILURE : FALSE
2929  */
2930 static BOOL LISTVIEW_SetSubItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2931 {
2932   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2933   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
2934   BOOL bResult = FALSE;
2935   HDPA hdpaSubItems;
2936   LISTVIEW_SUBITEM *lpSubItem;
2937   RECT rcItem;
2938
2939   TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", hwnd, debuglvitem_t(lpLVItem, isW), isW);
2940
2941   if (lStyle & LVS_OWNERDATA)
2942     return FALSE;
2943
2944   if (lpLVItem != NULL)
2945   {
2946     if (lpLVItem->iSubItem > 0)
2947     {
2948       hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
2949       if (hdpaSubItems != NULL)
2950       {
2951         /* set subitem only if column is present */
2952         if (Header_GetItemCount(infoPtr->hwndHeader) > lpLVItem->iSubItem)
2953         {
2954           lpSubItem = LISTVIEW_GetSubItem(hdpaSubItems, lpLVItem->iSubItem);
2955           if (lpSubItem != NULL)
2956             bResult = LISTVIEW_InitSubItemT(hwnd, lpSubItem, lpLVItem, isW);
2957           else
2958             bResult = LISTVIEW_AddSubItemT(hwnd, lpLVItem, isW);
2959
2960           rcItem.left = LVIR_BOUNDS;
2961           LISTVIEW_GetItemRect(hwnd, lpLVItem->iItem, &rcItem);
2962           InvalidateRect(hwnd, &rcItem, FALSE);
2963         }
2964       }
2965     }
2966   }
2967
2968   return bResult;
2969 }
2970
2971 /***
2972  * DESCRIPTION:
2973  * Sets item attributes.
2974  *
2975  * PARAMETER(S):
2976  * [I] HWND : window handle
2977  * [I] LPLVITEM : new item atttributes
2978  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
2979  *
2980  * RETURN:
2981  *   SUCCESS : TRUE
2982  *   FAILURE : FALSE
2983  */
2984 static BOOL LISTVIEW_SetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
2985 {
2986   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
2987
2988   if (!lpLVItem || lpLVItem->iItem < 0 ||
2989       lpLVItem->iItem>=GETITEMCOUNT(infoPtr))
2990     return FALSE;
2991   if (lpLVItem->iSubItem == 0)
2992     return LISTVIEW_SetMainItemT(hwnd, lpLVItem, isW);
2993   else
2994     return LISTVIEW_SetSubItemT(hwnd, lpLVItem, isW);
2995 }
2996
2997 /***
2998  * DESCRIPTION:
2999  * Retrieves the index of the item at coordinate (0, 0) of the client area.
3000  *
3001  * PARAMETER(S):
3002  * [I] HWND : window handle
3003  *
3004  * RETURN:
3005  * item index
3006  */
3007 static INT LISTVIEW_GetTopIndex(HWND hwnd)
3008 {
3009   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3010   UINT uView = lStyle & LVS_TYPEMASK;
3011   INT nItem = 0;
3012   SCROLLINFO scrollInfo;
3013
3014   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
3015   scrollInfo.cbSize = sizeof(SCROLLINFO);
3016   scrollInfo.fMask = SIF_POS;
3017
3018   if (uView == LVS_LIST)
3019   {
3020     if ((lStyle & WS_HSCROLL) && GetScrollInfo(hwnd, SB_HORZ, &scrollInfo))
3021       nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(hwnd);
3022   }
3023   else if (uView == LVS_REPORT)
3024   {
3025     if ((lStyle & WS_VSCROLL) && GetScrollInfo(hwnd, SB_VERT, &scrollInfo))
3026       nItem = scrollInfo.nPos;
3027   }
3028
3029   return nItem;
3030 }
3031
3032 /***
3033  * DESCRIPTION:
3034  * Draws a subitem.
3035  *
3036  * PARAMETER(S):
3037  * [I] HWND : window handle
3038  * [I] HDC : device context handle
3039  * [I] INT : item index
3040  * [I] INT : subitem index
3041  * [I] RECT * : clipping rectangle
3042  *
3043  * RETURN:
3044  * None
3045  */
3046 static VOID LISTVIEW_DrawSubItem(HWND hwnd, HDC hdc, INT nItem, INT nSubItem,
3047                                  RECT rcItem, BOOL Selected)
3048 {
3049   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3050   WCHAR szDispText[DISP_TEXT_SIZE];
3051   LVITEMW lvItem;
3052   LVCOLUMNW lvColumn;
3053   UINT textoutOptions = ETO_CLIPPED | ETO_OPAQUE;
3054   RECT rcTemp;
3055   INT textLeft;
3056   INT nLabelWidth = 0;
3057
3058   TRACE("(hwnd=%x, hdc=%x, nItem=%d, nSubItem=%d)\n", hwnd, hdc,
3059         nItem, nSubItem);
3060
3061   /* get information needed for drawing the item */
3062   ZeroMemory(&lvItem, sizeof(lvItem));
3063   lvItem.mask = LVIF_TEXT;
3064   lvItem.iItem = nItem;
3065   lvItem.iSubItem = nSubItem;
3066   lvItem.cchTextMax = DISP_TEXT_SIZE;
3067   lvItem.pszText = szDispText;
3068   *lvItem.pszText = '\0';
3069   LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
3070   TRACE("   lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3071
3072   ZeroMemory(&lvColumn, sizeof(lvColumn));
3073   lvColumn.mask = LVCF_FMT;
3074   LISTVIEW_GetColumnT(hwnd, nSubItem, &lvColumn, TRUE);
3075   textLeft = rcItem.left;
3076   TRACE("lvColumn.fmt=%d\n", lvColumn.fmt);
3077   if (lvColumn.fmt != LVCFMT_LEFT)
3078   {
3079     if ((nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE)))
3080     {
3081       if (lvColumn.fmt == LVCFMT_RIGHT)
3082         textLeft = rcItem.right - nLabelWidth;
3083       else
3084         textLeft = rcItem.left + (rcItem.right-rcItem.left-nLabelWidth)/2;
3085     }
3086   }
3087
3088
3089   /* redraw the background of the item */
3090   rcTemp = rcItem;
3091   if(infoPtr->nColumnCount == (nSubItem + 1))
3092     rcTemp.right  = infoPtr->rcList.right;
3093   else
3094     rcTemp.right += WIDTH_PADDING;
3095
3096   LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3097
3098   /* set item colors */
3099   if (ListView_GetItemState(hwnd,nItem,LVIS_SELECTED) && Selected)
3100   {
3101     if (infoPtr->bFocus)
3102     {
3103       SetBkColor(hdc, comctl32_color.clrHighlight);
3104       SetTextColor(hdc, comctl32_color.clrHighlightText);
3105     }
3106     else
3107     {
3108       SetBkColor(hdc, comctl32_color.clr3dFace);
3109       SetTextColor(hdc, comctl32_color.clrBtnText);
3110     }
3111   }
3112   else
3113   {
3114     if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3115     {
3116        SetBkMode(hdc, TRANSPARENT);
3117        textoutOptions &= ~ETO_OPAQUE;
3118     }
3119     else
3120     {
3121       SetBkMode(hdc, OPAQUE);
3122       SetBkColor(hdc, infoPtr->clrTextBk);
3123     }
3124
3125     SetTextColor(hdc, infoPtr->clrText);
3126   }
3127
3128   TRACE("drawing text %s, l=%d, t=%d, rect=(%d,%d)-(%d,%d)\n",
3129         debugstr_w(lvItem.pszText), textLeft, rcItem.top,
3130         rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
3131   ExtTextOutW(hdc, textLeft, rcItem.top, textoutOptions,
3132               &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3133
3134   if (Selected)
3135   {
3136     /* fill in the gap */
3137     RECT rec;
3138     if (nSubItem < Header_GetItemCount(infoPtr->hwndHeader)-1)
3139     {
3140       CopyRect(&rec,&rcItem);
3141       rec.left = rec.right;
3142       rec.right = rec.left+REPORT_MARGINX;
3143       ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3144         &rec, NULL, 0, NULL);
3145     }
3146     CopyRect(&rec,&rcItem);
3147     rec.right = rec.left;
3148     rec.left = rec.left - REPORT_MARGINX;
3149     ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3150     &rec, NULL, 0, NULL);
3151   }
3152 }
3153
3154
3155 /***
3156  * DESCRIPTION:
3157  * Draws an item.
3158  *
3159  * PARAMETER(S):
3160  * [I] HWND : window handle
3161  * [I] HDC : device context handle
3162  * [I] INT : item index
3163  * [I] RECT * : clipping rectangle
3164  *
3165  * RETURN:
3166  * None
3167  */
3168 static VOID LISTVIEW_DrawItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem, BOOL FullSelect, RECT* SuggestedFocus)
3169 {
3170   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3171   WCHAR szDispText[DISP_TEXT_SIZE];
3172   INT nLabelWidth;
3173   LVITEMW lvItem;
3174   INT nMixMode;
3175   DWORD dwBkColor;
3176   DWORD dwTextColor,dwTextX;
3177   BOOL bImage = FALSE;
3178   INT   iBkMode = -1;
3179   UINT  textoutOptions = ETO_OPAQUE | ETO_CLIPPED;
3180   RECT rcTemp;
3181
3182   TRACE("(hwnd=%x, hdc=%x, nItem=%d)\n", hwnd, hdc, nItem);
3183
3184
3185   /* get information needed for drawing the item */
3186   ZeroMemory(&lvItem, sizeof(lvItem));
3187   lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_INDENT;
3188   lvItem.stateMask = LVIS_SELECTED |  LVIS_STATEIMAGEMASK;
3189   lvItem.iItem = nItem;
3190   lvItem.iSubItem = 0;
3191   lvItem.cchTextMax = DISP_TEXT_SIZE;
3192   lvItem.pszText = szDispText;
3193   *lvItem.pszText = '\0';
3194   LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
3195   TRACE("   lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3196
3197   /* redraw the background of the item */
3198   rcTemp = rcItem;
3199   if(infoPtr->nColumnCount == (nItem + 1))
3200     rcTemp.right = infoPtr->rcList.right;
3201   else
3202     rcTemp.right+=WIDTH_PADDING;
3203
3204   LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3205
3206   /* do indent */
3207   if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
3208   {
3209     rcItem.left += infoPtr->iconSize.cx * lvItem.iIndent;
3210
3211     if (SuggestedFocus)
3212       SuggestedFocus->left += infoPtr->iconSize.cx * lvItem.iIndent;
3213   }
3214
3215   /* state icons */
3216   if (infoPtr->himlState != NULL)
3217   {
3218      UINT uStateImage = (lvItem.state & LVIS_STATEIMAGEMASK) >> 12;
3219      if (uStateImage > 0)
3220      {
3221        ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcItem.left,
3222                       rcItem.top, ILD_NORMAL);
3223      }
3224
3225      rcItem.left += infoPtr->iconSize.cx;
3226      if (SuggestedFocus)
3227        SuggestedFocus->left += infoPtr->iconSize.cx;
3228      bImage = TRUE;
3229   }
3230
3231   /* small icons */
3232   if (infoPtr->himlSmall != NULL)
3233   {
3234     if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE) &&
3235         (lvItem.iImage>=0))
3236     {
3237       ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3238       ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3239                      rcItem.top, ILD_SELECTED);
3240     }
3241     else if (lvItem.iImage>=0)
3242     {
3243       ImageList_SetBkColor(infoPtr->himlSmall, CLR_NONE);
3244       ImageList_Draw(infoPtr->himlSmall, lvItem.iImage, hdc, rcItem.left,
3245                      rcItem.top, ILD_NORMAL);
3246     }
3247
3248     rcItem.left += infoPtr->iconSize.cx;
3249
3250     if (SuggestedFocus)
3251       SuggestedFocus->left += infoPtr->iconSize.cx;
3252     bImage = TRUE;
3253   }
3254
3255   /* Don't bother painting item being edited */
3256   if (infoPtr->Editing && lvItem.state & LVIS_FOCUSED && !FullSelect)
3257       return;
3258
3259   if ((lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus != FALSE))
3260   {
3261     /* set item colors */
3262     dwBkColor = SetBkColor(hdc, comctl32_color.clrHighlight);
3263     dwTextColor = SetTextColor(hdc, comctl32_color.clrHighlightText);
3264     /* set raster mode */
3265     nMixMode = SetROP2(hdc, R2_XORPEN);
3266   }
3267   else if ((GetWindowLongW(hwnd, GWL_STYLE) & LVS_SHOWSELALWAYS) &&
3268            (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus == FALSE))
3269   {
3270     dwBkColor = SetBkColor(hdc, comctl32_color.clr3dFace);
3271     dwTextColor = SetTextColor(hdc, comctl32_color.clrBtnText);
3272     /* set raster mode */
3273     nMixMode = SetROP2(hdc, R2_COPYPEN);
3274   }
3275   else
3276   {
3277     /* set item colors */
3278     if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3279     {
3280       dwBkColor = GetBkColor(hdc);
3281       iBkMode = SetBkMode(hdc, TRANSPARENT);
3282       textoutOptions &= ~ETO_OPAQUE;
3283     }
3284     else
3285     {
3286       dwBkColor = SetBkColor(hdc, infoPtr->clrTextBk);
3287       iBkMode = SetBkMode(hdc, OPAQUE);
3288     }
3289
3290     dwTextColor = SetTextColor(hdc, infoPtr->clrText);
3291     /* set raster mode */
3292     nMixMode = SetROP2(hdc, R2_COPYPEN);
3293   }
3294
3295   nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
3296   if (rcItem.left + nLabelWidth < rcItem.right)
3297   {
3298     if (!FullSelect)
3299       rcItem.right = rcItem.left + nLabelWidth + TRAILING_PADDING;
3300     if (bImage)
3301       rcItem.right += IMAGE_PADDING;
3302   }
3303
3304   /* draw label */
3305   dwTextX = rcItem.left + 1;
3306   if (bImage)
3307     dwTextX += IMAGE_PADDING;
3308
3309   if (lvItem.pszText) {
3310     TRACE("drawing text  hwnd=%x, rect=(%d,%d)-(%d,%d)\n",
3311           hwnd, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
3312     ExtTextOutW(hdc, dwTextX, rcItem.top, textoutOptions,
3313                 &rcItem, lvItem.pszText, lstrlenW(lvItem.pszText), NULL);
3314   }
3315
3316   if ((FullSelect)&&(Header_GetItemCount(infoPtr->hwndHeader) > 1))
3317   {
3318     /* fill in the gap */
3319     RECT rec;
3320     CopyRect(&rec,&rcItem);
3321     rec.left = rec.right;
3322     rec.right = rec.left+REPORT_MARGINX;
3323     ExtTextOutW(hdc, rec.left , rec.top, textoutOptions,
3324                 &rec, NULL, 0, NULL);
3325   }
3326
3327   if (!FullSelect)
3328       CopyRect(SuggestedFocus,&rcItem);
3329
3330   if (nMixMode != 0)
3331   {
3332     SetROP2(hdc, R2_COPYPEN);
3333     SetBkColor(hdc, dwBkColor);
3334     SetTextColor(hdc, dwTextColor);
3335     if (iBkMode != -1)
3336       SetBkMode(hdc, iBkMode);
3337   }
3338 }
3339
3340 /***
3341  * DESCRIPTION:
3342  * Draws an item when in large icon display mode.
3343  *
3344  * PARAMETER(S):
3345  * [I] HWND : window handle
3346  * [I] HDC : device context handle
3347  * [I] INT : item index
3348  * [I] RECT : clipping rectangle
3349  * [O] RECT * : The text rectangle about which to draw the focus
3350  *
3351  * RETURN:
3352  * None
3353  */
3354 static VOID LISTVIEW_DrawLargeItem(HWND hwnd, HDC hdc, INT nItem, RECT rcItem,
3355                                    RECT *SuggestedFocus)
3356 {
3357   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3358   WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3359   LVITEMW lvItem;
3360   UINT uFormat = DT_TOP | DT_CENTER | DT_WORDBREAK | DT_NOPREFIX |
3361                  DT_EDITCONTROL ;
3362   /* Maintain this format in line with the one in LISTVIEW_UpdateLargeItemLabelRect*/
3363   RECT rcTemp;
3364
3365   TRACE("(hwnd=%x, hdc=%x, nItem=%d, left=%d, top=%d, right=%d, bottom=%d)\n",
3366         hwnd, hdc, nItem, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
3367
3368   /* get information needed for drawing the item */
3369   ZeroMemory(&lvItem, sizeof(lvItem));
3370   lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
3371   lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3372   lvItem.iItem = nItem;
3373   lvItem.iSubItem = 0;
3374   lvItem.cchTextMax = DISP_TEXT_SIZE;
3375   lvItem.pszText = szDispText;
3376   *lvItem.pszText = '\0';
3377   LISTVIEW_GetItemW(hwnd, &lvItem, FALSE);
3378   TRACE("   lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3379
3380   /* redraw the background of the item */
3381   rcTemp = rcItem;
3382   if(infoPtr->nColumnCount == (nItem + 1))
3383     rcTemp.right = infoPtr->rcList.right;
3384   else
3385     rcTemp.right+=WIDTH_PADDING;
3386     /* The comment doesn't say WIDTH_PADDING applies to large icons */
3387
3388   TRACE("background rect (%d,%d)-(%d,%d)\n",
3389         rcTemp.left, rcTemp.top, rcTemp.right, rcTemp.bottom);
3390
3391   LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3392
3393
3394   /* Figure out text colours etc. depending on state
3395    * At least the following states exist; there may be more.
3396    * Many items may be selected
3397    * At most one item may have the focus
3398    * The application may not actually be active currently
3399    * 1. The item is not selected in any way
3400    * 2. The cursor is flying over the icon or text and the text is being
3401    *    expanded because it is not fully displayed currently.
3402    * 3. The item is selected and is focussed, i.e. the user has not clicked
3403    *    in the blank area of the window, and the window (or application?)
3404    *    still has the focus.
3405    * 4. As 3 except that a different window has the focus
3406    * 5. The item is the selected item of all the items, but the user has
3407    *    clicked somewhere else on the window.
3408    * Only a few of these are handled currently. In particular 2 is not yet
3409    * handled since we do not support the functionality currently (or at least
3410    * we didn't when I wrote this)
3411    */
3412
3413   if (lvItem.state & LVIS_SELECTED)
3414   {
3415     /* set item colors */
3416     SetBkColor(hdc, comctl32_color.clrHighlight);
3417     SetTextColor(hdc, comctl32_color.clrHighlightText);
3418     SetBkMode (hdc, OPAQUE);
3419     /* set raster mode */
3420     SetROP2(hdc, R2_XORPEN);
3421     /* When exactly is it in XOR? while being dragged? */
3422   }
3423   else
3424   {
3425     /* set item colors */
3426     if ( (infoPtr->clrTextBk == CLR_DEFAULT) || (infoPtr->clrTextBk == CLR_NONE) )
3427     {
3428        SetBkMode(hdc, TRANSPARENT);
3429     }
3430     else
3431     {
3432       SetBkMode(hdc, OPAQUE);
3433       SetBkColor(hdc, infoPtr->clrTextBk);
3434     }
3435
3436     SetTextColor(hdc, infoPtr->clrText);
3437     /* set raster mode */
3438     SetROP2(hdc, R2_COPYPEN);
3439   }
3440
3441   /* In cases 2,3 and 5 (see above) the full text is displayed, with word
3442    * wrapping and long words split.
3443    * In cases 1 and 4 only a portion of the text is displayed with word
3444    * wrapping and both word and end ellipsis.  (I don't yet know about path
3445    * ellipsis)
3446    */
3447   uFormat |= ( (lvItem.state & LVIS_FOCUSED) && infoPtr->bFocus) ?
3448           DT_NOCLIP
3449       :
3450           DT_WORD_ELLIPSIS | DT_END_ELLIPSIS;
3451
3452   /* draw the icon */
3453   if (infoPtr->himlNormal != NULL)
3454   {
3455     if (lvItem.iImage >= 0)
3456     {
3457       ImageList_Draw (infoPtr->himlNormal, lvItem.iImage, hdc, rcItem.left,
3458                       rcItem.top,
3459                       (lvItem.state & LVIS_SELECTED) ? ILD_SELECTED : ILD_NORMAL);
3460     }
3461   }
3462
3463   /* Draw the text below the icon */
3464
3465   /* Don't bother painting item being edited */
3466   if ((infoPtr->Editing && (lvItem.state & LVIS_FOCUSED)) ||
3467       !lstrlenW(lvItem.pszText))
3468   {
3469     SetRectEmpty(SuggestedFocus);
3470     return;
3471   }
3472
3473   /* Since rcItem.left is left point of icon, compute left point of item box */
3474   rcItem.left -= ((infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2);
3475   rcItem.right = rcItem.left + infoPtr->nItemWidth;
3476   rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3477   TRACE("bound box for text+icon (%d,%d)-(%d,%d), iS.cx=%ld, nItemWidth=%d\n",
3478         rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
3479         infoPtr->iconSize.cx, infoPtr->nItemWidth);
3480   TRACE("rcList (%d,%d)-(%d,%d), rcView (%d,%d)-(%d,%d)\n",
3481         infoPtr->rcList.left,    infoPtr->rcList.top,
3482         infoPtr->rcList.right,   infoPtr->rcList.bottom,
3483         infoPtr->rcView.left,    infoPtr->rcView.top,
3484         infoPtr->rcView.right,   infoPtr->rcView.bottom);
3485
3486   InflateRect(&rcItem, -(2*CAPTION_BORDER), 0);
3487   rcItem.top += infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
3488
3489
3490   /* draw label */
3491
3492   /* I am sure of most of the uFormat values.  However I am not sure about
3493    * whether we need or do not need the following:
3494    * DT_EXTERNALLEADING, DT_INTERNAL, DT_CALCRECT, DT_NOFULLWIDTHCHARBREAK,
3495    * DT_PATH_ELLIPSIS, DT_RTLREADING,
3496    * We certainly do not need
3497    * DT_BOTTOM, DT_VCENTER, DT_MODIFYSTRING, DT_LEFT, DT_RIGHT, DT_PREFIXONLY,
3498    * DT_SINGLELINE, DT_TABSTOP, DT_EXPANDTABS
3499    */
3500
3501   /* If the text is being drawn without clipping (i.e. the full text) then we
3502    * need to jump through a few hoops to ensure that it all gets displayed and
3503    * that the background is complete
3504    */
3505   if (uFormat & DT_NOCLIP)
3506   {
3507       RECT rcBack=rcItem;
3508       HBRUSH hBrush = CreateSolidBrush(GetBkColor (hdc));
3509       int dx, dy, old_wid, new_wid;
3510       DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat | DT_CALCRECT);
3511       /* Microsoft, in their great wisdom, have decided that the rectangle
3512        * returned by DrawText on DT_CALCRECT will only guarantee the dimension,
3513        * not the location.  So we have to do the centring ourselves (and take
3514        * responsibility for agreeing off-by-one consistency with them).
3515        */
3516       old_wid = rcItem.right-rcItem.left;
3517       new_wid = rcBack.right - rcBack.left;
3518       dx = rcBack.left - rcItem.left + (new_wid-old_wid)/2;
3519       dy = rcBack.top - rcItem.top;
3520       OffsetRect (&rcItem, dx, dy);
3521       FillRect(hdc, &rcItem, hBrush);
3522       DeleteObject(hBrush);
3523   }
3524   /* else ? What if we are losing the focus? will we not get a complete
3525    * background?
3526    */
3527   DrawTextW (hdc, lvItem.pszText, -1, &rcItem, uFormat);
3528
3529   CopyRect(SuggestedFocus, &rcItem);
3530 }
3531
3532 /***
3533  * DESCRIPTION:
3534  * Draws listview items when in report display mode.
3535  *
3536  * PARAMETER(S):
3537  * [I] HWND : window handle
3538  * [I] HDC : device context handle
3539  *
3540  * RETURN:
3541  * None
3542  */
3543 static VOID LISTVIEW_RefreshReport(HWND hwnd, HDC hdc, DWORD cdmode)
3544 {
3545   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3546   SCROLLINFO scrollInfo;
3547   INT nDrawPosY = infoPtr->rcList.top;
3548   INT nColumnCount;
3549   RECT rcItem, rcTemp;
3550   INT  j;
3551   INT nItem;
3552   INT nLast;
3553   BOOL FullSelected;
3554   DWORD cditemmode = CDRF_DODEFAULT;
3555   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3556   INT scrollOffset;
3557
3558   TRACE("\n");
3559   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
3560   scrollInfo.cbSize = sizeof(SCROLLINFO);
3561   scrollInfo.fMask = SIF_POS;
3562
3563   nItem = ListView_GetTopIndex(hwnd);
3564
3565   /* add 1 for displaying a partial item at the bottom */
3566   nLast = nItem + LISTVIEW_GetCountPerColumn(hwnd) + 1;
3567   nLast = min(nLast, GETITEMCOUNT(infoPtr));
3568
3569   /* send cache hint notification */
3570   if (GetWindowLongW(hwnd,GWL_STYLE) & LVS_OWNERDATA)
3571   {
3572     NMLVCACHEHINT nmlv;
3573
3574     nmlv.hdr.hwndFrom = hwnd;
3575     nmlv.hdr.idFrom = GetWindowLongW(hwnd,GWL_ID);
3576     nmlv.hdr.code = LVN_ODCACHEHINT;
3577     nmlv.iFrom = nItem;
3578     nmlv.iTo   = nLast;
3579
3580     SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)nmlv.hdr.idFrom,
3581                  (LPARAM)&nmlv);
3582   }
3583
3584   nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
3585   infoPtr->nColumnCount = nColumnCount; /* update nColumnCount */
3586   FullSelected = infoPtr->dwExStyle & LVS_EX_FULLROWSELECT;
3587
3588   /* clear the background of any part of the control that doesn't contain items */
3589   SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3590   LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3591
3592   /* nothing to draw */
3593   if(GETITEMCOUNT(infoPtr) == 0)
3594     return;
3595
3596   /* Get scroll bar info once before loop */
3597   GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
3598   scrollOffset = scrollInfo.nPos;
3599
3600   for (; nItem < nLast; nItem++)
3601   {
3602     RECT SuggestedFocusRect;
3603
3604     /* Do Owner Draw */
3605     if (lStyle & LVS_OWNERDRAWFIXED)
3606     {
3607         UINT uID = GetWindowLongW( hwnd, GWL_ID);
3608         DRAWITEMSTRUCT dis;
3609         LVITEMW item;
3610         RECT br;
3611
3612         TRACE("Owner Drawn\n");
3613         dis.CtlType = ODT_LISTVIEW;
3614         dis.CtlID = uID;
3615         dis.itemID = nItem;
3616         dis.itemAction = ODA_DRAWENTIRE;
3617         dis.itemState = 0;
3618
3619         if (LISTVIEW_IsSelected(hwnd,nItem)) dis.itemState|=ODS_SELECTED;
3620         if (nItem==infoPtr->nFocusedItem)   dis.itemState|=ODS_FOCUS;
3621
3622         dis.hwndItem = hwnd;
3623         dis.hDC = hdc;
3624
3625         Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3626
3627         dis.rcItem.left = -scrollOffset;
3628         dis.rcItem.right = max(dis.rcItem.left, br.right - scrollOffset);
3629         dis.rcItem.top = nDrawPosY;
3630         dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3631
3632         ZeroMemory(&item,sizeof(item));
3633         item.iItem = nItem;
3634         item.mask = LVIF_PARAM;
3635         ListView_GetItemW(hwnd, &item);
3636
3637         dis.itemData = item.lParam;
3638
3639         if (SendMessageW(GetParent(hwnd),WM_DRAWITEM,(WPARAM)uID,(LPARAM)&dis))
3640         {
3641           nDrawPosY += infoPtr->nItemHeight;
3642           continue;
3643         }
3644     }
3645
3646     if (FullSelected)
3647     {
3648       RECT ir,br;
3649
3650       Header_GetItemRect(infoPtr->hwndHeader, 0, &ir);
3651       Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
3652
3653       ir.left += REPORT_MARGINX;
3654       ir.right = max(ir.left, br.right - REPORT_MARGINX);
3655       ir.top = nDrawPosY;
3656       ir.bottom = ir.top + infoPtr->nItemHeight;
3657
3658       CopyRect(&SuggestedFocusRect,&ir);
3659     }
3660
3661     for (j = 0; j < nColumnCount; j++)
3662     {
3663       if (cdmode & CDRF_NOTIFYITEMDRAW)
3664         cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, j,
3665                                                       CDDS_ITEMPREPAINT);
3666       if (cditemmode & CDRF_SKIPDEFAULT)
3667         continue;
3668
3669       Header_GetItemRect(infoPtr->hwndHeader, j, &rcItem);
3670
3671       rcItem.left += REPORT_MARGINX;
3672       rcItem.right = max(rcItem.left, rcItem.right - REPORT_MARGINX);
3673       rcItem.top = nDrawPosY;
3674       rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3675
3676       /* Offset the Scroll Bar Pos */
3677       rcItem.left -= scrollOffset;
3678       rcItem.right -= scrollOffset;
3679
3680       if (j == 0)
3681       {
3682         LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FullSelected,
3683                           &SuggestedFocusRect);
3684       }
3685       else
3686       {
3687         LISTVIEW_DrawSubItem(hwnd, hdc, nItem, j, rcItem, FullSelected);
3688       }
3689
3690       if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3691         LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3692                                       CDDS_ITEMPOSTPAINT);
3693     }
3694     /*
3695      * Draw Focus Rect
3696      */
3697     if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3698     {
3699       BOOL rop=FALSE;
3700       if (FullSelected && LISTVIEW_GetItemState(hwnd,nItem,LVIS_SELECTED))
3701         rop = SetROP2(hdc, R2_XORPEN);
3702
3703       Rectangle(hdc, SuggestedFocusRect.left, SuggestedFocusRect.top,
3704                 SuggestedFocusRect.right,SuggestedFocusRect.bottom);
3705
3706       if (rop)
3707         SetROP2(hdc, R2_COPYPEN);
3708     }
3709     nDrawPosY += infoPtr->nItemHeight;
3710   }
3711 }
3712
3713 /***
3714  * DESCRIPTION:
3715  * Retrieves the number of items that can fit vertically in the client area.
3716  *
3717  * PARAMETER(S):
3718  * [I] HWND : window handle
3719  *
3720  * RETURN:
3721  * Number of items per row.
3722  */
3723 static INT LISTVIEW_GetCountPerRow(HWND hwnd)
3724 {
3725   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3726   UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3727   INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
3728   INT nCountPerRow = 1;
3729
3730   if (nListWidth > 0)
3731   {
3732     if (uView != LVS_REPORT)
3733     {
3734       nCountPerRow =  nListWidth / infoPtr->nItemWidth;
3735       if (nCountPerRow == 0) nCountPerRow = 1;
3736     }
3737   }
3738
3739   return nCountPerRow;
3740 }
3741
3742 /***
3743  * DESCRIPTION:
3744  * Retrieves the number of items that can fit horizontally in the client
3745  * area.
3746  *
3747  * PARAMETER(S):
3748  * [I] HWND : window handle
3749  *
3750  * RETURN:
3751  * Number of items per column.
3752  */
3753 static INT LISTVIEW_GetCountPerColumn(HWND hwnd)
3754 {
3755   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd,0);
3756   INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
3757   INT nCountPerColumn = 1;
3758
3759   if (nListHeight > 0)
3760   {
3761     TRACE("rcList=(%d,%d)-(%d,%d), nItemHeight=%d, CYHSCROLL=%d\n",
3762           infoPtr->rcList.left,infoPtr->rcList.top,
3763           infoPtr->rcList.right,infoPtr->rcList.bottom,
3764           infoPtr->nItemHeight,
3765           GetSystemMetrics(SM_CYHSCROLL));
3766     nCountPerColumn =  nListHeight / infoPtr->nItemHeight;
3767     if (nCountPerColumn == 0) nCountPerColumn = 1;
3768   }
3769
3770   return nCountPerColumn;
3771 }
3772
3773 /***
3774  * DESCRIPTION:
3775  * Retrieves the number of columns needed to display all the items when in
3776  * list display mode.
3777  *
3778  * PARAMETER(S):
3779  * [I] HWND : window handle
3780  *
3781  * RETURN:
3782  * Number of columns.
3783  */
3784 static INT LISTVIEW_GetColumnCount(HWND hwnd)
3785 {
3786   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3787   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
3788   INT nColumnCount = 0;
3789
3790   if ((lStyle & LVS_TYPEMASK) == LVS_LIST)
3791   {
3792     nColumnCount = infoPtr->rcList.right / infoPtr->nItemWidth;
3793     if (infoPtr->rcList.right % infoPtr->nItemWidth) nColumnCount++;
3794   }
3795
3796   return nColumnCount;
3797 }
3798
3799
3800 /***
3801  * DESCRIPTION:
3802  * Draws listview items when in list display mode.
3803  *
3804  * PARAMETER(S):
3805  * [I] HWND : window handle
3806  * [I] HDC : device context handle
3807  *
3808  * RETURN:
3809  * None
3810  */
3811 static VOID LISTVIEW_RefreshList(HWND hwnd, HDC hdc, DWORD cdmode)
3812 {
3813   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3814   RECT rcItem, FocusRect, rcTemp;
3815   INT i, j;
3816   INT nItem;
3817   INT nColumnCount;
3818   INT nCountPerColumn;
3819   INT nItemWidth = infoPtr->nItemWidth;
3820   INT nItemHeight = infoPtr->nItemHeight;
3821   DWORD cditemmode = CDRF_DODEFAULT;
3822
3823   /* get number of fully visible columns */
3824   nColumnCount = LISTVIEW_GetColumnCount(hwnd);
3825   infoPtr->nColumnCount = nColumnCount;
3826   nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
3827   nItem = ListView_GetTopIndex(hwnd);
3828   TRACE("nColumnCount=%d, nCountPerColumn=%d, start item=%d\n",
3829         nColumnCount, nCountPerColumn, nItem);
3830
3831   /* paint the background of the control that doesn't contain any items */
3832   SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3833   LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3834
3835   /* nothing to draw, return here */
3836   if(GETITEMCOUNT(infoPtr) == 0)
3837     return;
3838
3839   for (i = 0; i < nColumnCount; i++)
3840   {
3841     for (j = 0; j < nCountPerColumn; j++, nItem++)
3842     {
3843       if (nItem >= GETITEMCOUNT(infoPtr))
3844         return;
3845
3846       if (cdmode & CDRF_NOTIFYITEMDRAW)
3847         cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, nItem, 0,
3848                                                       CDDS_ITEMPREPAINT);
3849       if (cditemmode & CDRF_SKIPDEFAULT)
3850         continue;
3851
3852       rcItem.top = j * nItemHeight;
3853       rcItem.left = i * nItemWidth;
3854       rcItem.bottom = rcItem.top + nItemHeight;
3855       rcItem.right = rcItem.left + nItemWidth;
3856       LISTVIEW_DrawItem(hwnd, hdc, nItem, rcItem, FALSE, &FocusRect);
3857       /*
3858        * Draw Focus Rect
3859        */
3860       if (LISTVIEW_GetItemState(hwnd,nItem,LVIS_FOCUSED) && infoPtr->bFocus)
3861       Rectangle(hdc, FocusRect.left, FocusRect.top,
3862                 FocusRect.right,FocusRect.bottom);
3863
3864       if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3865         LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, nItem, 0,
3866                                           CDDS_ITEMPOSTPAINT);
3867
3868     }
3869   }
3870 }
3871
3872 /***
3873  * DESCRIPTION:
3874  * Draws listview items when in icon or small icon display mode.
3875  *
3876  * PARAMETER(S):
3877  * [I] HWND : window handle
3878  * [I] HDC : device context handle
3879  *
3880  * RETURN:
3881  * None
3882  */
3883 static VOID LISTVIEW_RefreshIcon(HWND hwnd, HDC hdc, BOOL bSmall, DWORD cdmode)
3884 {
3885   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3886   POINT ptPosition;
3887   POINT ptOrigin;
3888   RECT rcItem, SuggestedFocus, rcTemp;
3889   INT i;
3890   DWORD cditemmode = CDRF_DODEFAULT;
3891
3892   TRACE("\n");
3893   infoPtr->nColumnCount = 1; /* set this to an arbitrary value to prevent */
3894                              /* DrawItem from erasing the incorrect background area */
3895
3896   /* paint the background of the control that doesn't contain any items */
3897   SubtractRect(&rcTemp, &infoPtr->rcList, &infoPtr->rcView);
3898   LISTVIEW_FillBackground(hwnd, hdc, &rcTemp);
3899
3900   /* nothing to draw, return here */
3901   if(GETITEMCOUNT(infoPtr) == 0)
3902     return;
3903
3904   LISTVIEW_GetOrigin(hwnd, &ptOrigin);
3905   for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
3906   {
3907     if (cdmode & CDRF_NOTIFYITEMDRAW)
3908       cditemmode = LISTVIEW_SendCustomDrawItemNotify (hwnd, hdc, i, 0,
3909                                                       CDDS_ITEMPREPAINT);
3910     if (cditemmode & CDRF_SKIPDEFAULT)
3911         continue;
3912
3913     LISTVIEW_GetItemPosition(hwnd, i, &ptPosition);
3914     ptPosition.x += ptOrigin.x;
3915     ptPosition.y += ptOrigin.y;
3916
3917     if (ptPosition.y + infoPtr->nItemHeight > infoPtr->rcList.top)
3918     {
3919       if (ptPosition.x + infoPtr->nItemWidth > infoPtr->rcList.left)
3920       {
3921         if (ptPosition.y < infoPtr->rcList.bottom)
3922         {
3923           if (ptPosition.x < infoPtr->rcList.right)
3924           {
3925             rcItem.top = ptPosition.y;
3926             rcItem.left = ptPosition.x;
3927             rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
3928             rcItem.right = rcItem.left + infoPtr->nItemWidth;
3929             if (bSmall)
3930               LISTVIEW_DrawItem(hwnd, hdc, i, rcItem, FALSE, &SuggestedFocus);
3931             else
3932               LISTVIEW_DrawLargeItem(hwnd, hdc, i, rcItem, &SuggestedFocus);
3933             /*
3934              * Draw Focus Rect
3935              */
3936             if (LISTVIEW_GetItemState(hwnd,i,LVIS_FOCUSED) &&
3937                 infoPtr->bFocus && !IsRectEmpty(&SuggestedFocus))
3938               Rectangle(hdc, SuggestedFocus.left, SuggestedFocus.top,
3939                         SuggestedFocus.right,SuggestedFocus.bottom);
3940           }
3941         }
3942       }
3943     }
3944     if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3945         LISTVIEW_SendCustomDrawItemNotify(hwnd, hdc, i, 0,
3946                                           CDDS_ITEMPOSTPAINT);
3947   }
3948 }
3949
3950 /***
3951  * DESCRIPTION:
3952  * Draws listview items.
3953  *
3954  * PARAMETER(S):
3955  * [I] HWND : window handle
3956  * [I] HDC : device context handle
3957  *
3958  * RETURN:
3959  * NoneX
3960  */
3961 static VOID LISTVIEW_Refresh(HWND hwnd, HDC hdc)
3962 {
3963   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
3964   UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
3965   HFONT hOldFont;
3966   HPEN hPen, hOldPen;
3967   DWORD cdmode;
3968   RECT rect;
3969
3970   infoPtr->bIsDrawing = TRUE;
3971   LISTVIEW_DumpListview (infoPtr, __LINE__);
3972
3973   GetClientRect(hwnd, &rect);
3974   cdmode = LISTVIEW_SendCustomDrawNotify(hwnd,CDDS_PREPAINT,hdc,rect);
3975
3976   if (cdmode == CDRF_SKIPDEFAULT) return;
3977
3978   /* select font */
3979   hOldFont = SelectObject(hdc, infoPtr->hFont);
3980
3981   /* select the dotted pen (for drawing the focus box) */
3982   hPen = CreatePen(PS_ALTERNATE, 1, 0);
3983   hOldPen = SelectObject(hdc, hPen);
3984
3985   /* select transparent brush (for drawing the focus box) */
3986   SelectObject(hdc, GetStockObject(NULL_BRUSH));
3987
3988   TRACE("\n");
3989   if (uView == LVS_LIST)
3990     LISTVIEW_RefreshList(hwnd, hdc, cdmode);
3991   else if (uView == LVS_REPORT)
3992     LISTVIEW_RefreshReport(hwnd, hdc, cdmode);
3993   else if (uView == LVS_SMALLICON)
3994     LISTVIEW_RefreshIcon(hwnd, hdc, TRUE, cdmode);
3995   else if (uView == LVS_ICON)
3996     LISTVIEW_RefreshIcon(hwnd, hdc, FALSE, cdmode);
3997
3998   /* unselect objects */
3999   SelectObject(hdc, hOldFont);
4000   SelectObject(hdc, hOldPen);
4001
4002   /* delete pen */
4003   DeleteObject(hPen);
4004
4005   if (cdmode & CDRF_NOTIFYPOSTPAINT)
4006       LISTVIEW_SendCustomDrawNotify(hwnd, CDDS_POSTPAINT, hdc, rect);
4007
4008   infoPtr->bIsDrawing = FALSE;
4009 }
4010
4011
4012 /***
4013  * DESCRIPTION:
4014  * Calculates the approximate width and height of a given number of items.
4015  *
4016  * PARAMETER(S):
4017  * [I] HWND : window handle
4018  * [I] INT : number of items
4019  * [I] INT : width
4020  * [I] INT : height
4021  *
4022  * RETURN:
4023  * Returns a DWORD. The width in the low word and the height in high word.
4024  */
4025 static LRESULT LISTVIEW_ApproximateViewRect(HWND hwnd, INT nItemCount,
4026                                             WORD wWidth, WORD wHeight)
4027 {
4028   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4029   UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4030   INT nItemCountPerColumn = 1;
4031   INT nColumnCount = 0;
4032   DWORD dwViewRect = 0;
4033
4034   if (nItemCount == -1)
4035     nItemCount = GETITEMCOUNT(infoPtr);
4036
4037   if (uView == LVS_LIST)
4038   {
4039     if (wHeight == 0xFFFF)
4040     {
4041       /* use current height */
4042       wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4043     }
4044
4045     if (wHeight < infoPtr->nItemHeight)
4046       wHeight = infoPtr->nItemHeight;
4047
4048     if (nItemCount > 0)
4049     {
4050       if (infoPtr->nItemHeight > 0)
4051       {
4052         nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4053         if (nItemCountPerColumn == 0)
4054           nItemCountPerColumn = 1;
4055
4056         if (nItemCount % nItemCountPerColumn != 0)
4057           nColumnCount = nItemCount / nItemCountPerColumn;
4058         else
4059           nColumnCount = nItemCount / nItemCountPerColumn + 1;
4060       }
4061     }
4062
4063     /* Microsoft padding magic */
4064     wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4065     wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4066
4067     dwViewRect = MAKELONG(wWidth, wHeight);
4068   }
4069   else if (uView == LVS_REPORT)
4070     FIXME("uView == LVS_REPORT: not implemented\n");
4071   else if (uView == LVS_SMALLICON)
4072     FIXME("uView == LVS_SMALLICON: not implemented\n");
4073   else if (uView == LVS_ICON)
4074     FIXME("uView == LVS_ICON: not implemented\n");
4075
4076   return dwViewRect;
4077 }
4078
4079 /***
4080  * DESCRIPTION:
4081  * Arranges listview items in icon display mode.
4082  *
4083  * PARAMETER(S):
4084  * [I] HWND : window handle
4085  * [I] INT : alignment code
4086  *
4087  * RETURN:
4088  *   SUCCESS : TRUE
4089  *   FAILURE : FALSE
4090  */
4091 static LRESULT LISTVIEW_Arrange(HWND hwnd, INT nAlignCode)
4092 {
4093   UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4094   BOOL bResult = FALSE;
4095
4096   if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4097   {
4098     switch (nAlignCode)
4099     {
4100     case LVA_ALIGNLEFT:
4101       FIXME("nAlignCode=LVA_ALIGNLEFT: not implemented\n");
4102       break;
4103     case LVA_ALIGNTOP:
4104       FIXME("nAlignCode=LVA_ALIGNTOP: not implemented\n");
4105       break;
4106     case LVA_DEFAULT:
4107       FIXME("nAlignCode=LVA_DEFAULT: not implemented\n");
4108       break;
4109     case LVA_SNAPTOGRID:
4110       FIXME("nAlignCode=LVA_SNAPTOGRID: not implemented\n");
4111       break;
4112     }
4113   }
4114
4115   return bResult;
4116 }
4117
4118 /* << LISTVIEW_CreateDragImage >> */
4119
4120
4121 /***
4122  * DESCRIPTION:
4123  * Removes all listview items and subitems.
4124  *
4125  * PARAMETER(S):
4126  * [I] HWND : window handle
4127  *
4128  * RETURN:
4129  *   SUCCESS : TRUE
4130  *   FAILURE : FALSE
4131  */
4132 static LRESULT LISTVIEW_DeleteAllItems(HWND hwnd)
4133 {
4134   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4135   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4136   UINT uView = lStyle & LVS_TYPEMASK;
4137   LISTVIEW_ITEM *lpItem;
4138   LISTVIEW_SUBITEM *lpSubItem;
4139   NMLISTVIEW nmlv;
4140   BOOL bSuppress;
4141   BOOL bResult = FALSE;
4142   HDPA hdpaSubItems;
4143
4144   TRACE("(hwnd=%x,)\n", hwnd);
4145
4146   LISTVIEW_RemoveAllSelections(hwnd);
4147   infoPtr->nSelectionMark=-1;
4148   infoPtr->nFocusedItem=-1;
4149   /* But we are supposed to leave nHotItem as is! */
4150
4151   if (lStyle & LVS_OWNERDATA)
4152   {
4153     infoPtr->hdpaItems->nItemCount = 0;
4154     InvalidateRect(hwnd, NULL, TRUE);
4155     return TRUE;
4156   }
4157
4158   if (GETITEMCOUNT(infoPtr) > 0)
4159   {
4160     INT i, j;
4161
4162     /* send LVN_DELETEALLITEMS notification */
4163     /* verify if subsequent LVN_DELETEITEM notifications should be
4164        suppressed */
4165     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4166     nmlv.iItem = -1;
4167     bSuppress = listview_notify(hwnd, LVN_DELETEALLITEMS, &nmlv);
4168
4169     for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
4170     {
4171       hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4172       if (hdpaSubItems != NULL)
4173       {
4174         for (j = 1; j < hdpaSubItems->nItemCount; j++)
4175         {
4176           lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, j);
4177           if (lpSubItem != NULL)
4178           {
4179             /* free subitem string */
4180             if (is_textW(lpSubItem->pszText))
4181               COMCTL32_Free(lpSubItem->pszText);
4182
4183             /* free subitem */
4184             COMCTL32_Free(lpSubItem);
4185           }
4186         }
4187
4188         lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4189         if (lpItem != NULL)
4190         {
4191           if (!bSuppress)
4192           {
4193             /* send LVN_DELETEITEM notification */
4194             nmlv.iItem = i;
4195             nmlv.lParam = lpItem->lParam;
4196             listview_notify(hwnd, LVN_DELETEITEM, &nmlv);
4197           }
4198
4199           /* free item string */
4200           if (is_textW(lpItem->pszText))
4201             COMCTL32_Free(lpItem->pszText);
4202
4203           /* free item */
4204           COMCTL32_Free(lpItem);
4205         }
4206
4207         DPA_Destroy(hdpaSubItems);
4208       }
4209     }
4210
4211     /* reinitialize listview memory */
4212     bResult = DPA_DeleteAllPtrs(infoPtr->hdpaItems);
4213
4214     /* align items (set position of each item) */
4215     if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4216     {
4217       if (lStyle & LVS_ALIGNLEFT)
4218       {
4219         LISTVIEW_AlignLeft(hwnd);
4220       }
4221       else
4222       {
4223         LISTVIEW_AlignTop(hwnd);
4224       }
4225     }
4226
4227     LISTVIEW_UpdateScroll(hwnd);
4228
4229     /* invalidate client area (optimization needed) */
4230     InvalidateRect(hwnd, NULL, TRUE);
4231   }
4232
4233   return bResult;
4234 }
4235
4236 /***
4237  * DESCRIPTION:
4238  * Removes a column from the listview control.
4239  *
4240  * PARAMETER(S):
4241  * [I] HWND : window handle
4242  * [I] INT : column index
4243  *
4244  * RETURN:
4245  *   SUCCESS : TRUE
4246  *   FAILURE : FALSE
4247  */
4248 static LRESULT LISTVIEW_DeleteColumn(HWND hwnd, INT nColumn)
4249 {
4250   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4251   UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4252   UINT uOwnerData = GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA;
4253   BOOL bResult = FALSE;
4254
4255   if (Header_DeleteItem(infoPtr->hwndHeader, nColumn) != FALSE)
4256   {
4257     bResult = uOwnerData ? TRUE : LISTVIEW_RemoveColumn(infoPtr->hdpaItems, nColumn);
4258
4259     /* Need to reset the item width when deleting a column */
4260     infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
4261
4262     /* reset scroll parameters */
4263     if (uView == LVS_REPORT)
4264     {
4265       /* update scrollbar(s) */
4266       LISTVIEW_UpdateScroll(hwnd);
4267
4268       /* refresh client area */
4269       InvalidateRect(hwnd, NULL, FALSE);
4270     }
4271   }
4272
4273   return bResult;
4274 }
4275
4276 /***
4277  * DESCRIPTION:
4278  * Removes an item from the listview control.
4279  *
4280  * PARAMETER(S):
4281  * [I] HWND : window handle
4282  * [I] INT : item index
4283  *
4284  * RETURN:
4285  *   SUCCESS : TRUE
4286  *   FAILURE : FALSE
4287  */
4288 static LRESULT LISTVIEW_DeleteItem(HWND hwnd, INT nItem)
4289 {
4290   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4291   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4292   UINT uView = lStyle & LVS_TYPEMASK;
4293   LONG lCtrlId = GetWindowLongW(hwnd, GWL_ID);
4294   NMLISTVIEW nmlv;
4295   BOOL bResult = FALSE;
4296   HDPA hdpaSubItems;
4297   LISTVIEW_ITEM *lpItem;
4298   LISTVIEW_SUBITEM *lpSubItem;
4299   INT i;
4300   LVITEMW item;
4301
4302   TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
4303
4304
4305   /* First, send LVN_DELETEITEM notification. */
4306   memset(&nmlv, 0, sizeof (NMLISTVIEW));
4307   nmlv.hdr.hwndFrom = hwnd;
4308   nmlv.hdr.idFrom = lCtrlId;
4309   nmlv.hdr.code = LVN_DELETEITEM;
4310   nmlv.iItem = nItem;
4311   SendMessageW(GetParent(hwnd), WM_NOTIFY, (WPARAM)lCtrlId,
4312                (LPARAM)&nmlv);
4313
4314
4315   /* remove it from the selection range */
4316   ZeroMemory(&item,sizeof(item));
4317   item.stateMask = LVIS_SELECTED;
4318   LISTVIEW_SetItemState(hwnd,nItem,&item);
4319
4320   if (lStyle & LVS_OWNERDATA)
4321   {
4322     infoPtr->hdpaItems->nItemCount --;
4323     InvalidateRect(hwnd, NULL, TRUE);
4324     return TRUE;
4325   }
4326
4327   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
4328   {
4329     /* initialize memory */
4330     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4331
4332     hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4333     if (hdpaSubItems != NULL)
4334     {
4335       for (i = 1; i < hdpaSubItems->nItemCount; i++)
4336       {
4337         lpSubItem = (LISTVIEW_SUBITEM *)DPA_GetPtr(hdpaSubItems, i);
4338         if (lpSubItem != NULL)
4339         {
4340           /* free item string */
4341           if (is_textW(lpSubItem->pszText))
4342             COMCTL32_Free(lpSubItem->pszText);
4343
4344           /* free item */
4345           COMCTL32_Free(lpSubItem);
4346         }
4347       }
4348
4349       lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
4350       if (lpItem != NULL)
4351       {
4352         /* free item string */
4353         if (is_textW(lpItem->pszText))
4354           COMCTL32_Free(lpItem->pszText);
4355
4356         /* free item */
4357         COMCTL32_Free(lpItem);
4358       }
4359
4360       bResult = DPA_Destroy(hdpaSubItems);
4361     }
4362
4363     LISTVIEW_ShiftIndices(hwnd,nItem,-1);
4364
4365     /* align items (set position of each item) */
4366     if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4367     {
4368       if (lStyle & LVS_ALIGNLEFT)
4369         LISTVIEW_AlignLeft(hwnd);
4370       else
4371         LISTVIEW_AlignTop(hwnd);
4372     }
4373
4374     LISTVIEW_UpdateScroll(hwnd);
4375
4376     /* refresh client area */
4377     InvalidateRect(hwnd, NULL, TRUE);
4378   }
4379
4380   return bResult;
4381 }
4382
4383
4384 /***
4385  * DESCRIPTION:
4386  * Return edit control handle of current edit label
4387  *
4388  * PARAMETER(S):
4389  * [I] HWND : window handle
4390  *
4391  * RETURN:
4392  *   SUCCESS : HWND
4393  *   FAILURE : 0
4394  */
4395 static LRESULT LISTVIEW_GetEditControl(HWND hwnd)
4396 {
4397   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4398   return infoPtr->hwndEdit;
4399 }
4400
4401
4402 /***
4403  * DESCRIPTION:
4404  * Callback implementation for editlabel control
4405  *
4406  * PARAMETER(S):
4407  * [I] HWND : window handle
4408  * [I] LPSTR : modified text
4409  * [I] DWORD : item index
4410  * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4411  *
4412  * RETURN:
4413  *   SUCCESS : TRUE
4414  *   FAILURE : FALSE
4415  */
4416 static BOOL LISTVIEW_EndEditLabelT(HWND hwnd, LPWSTR pszText, DWORD nItem, BOOL isW)
4417 {
4418   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4419   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4420   NMLVDISPINFOW dispInfo;
4421   LISTVIEW_ITEM *lpItem;
4422   HDPA hdpaSubItems;
4423   LISTVIEW_ITEM lvItemRef;
4424   LVITEMW item;
4425   BOOL bResult = TRUE;
4426
4427   TRACE("(hwnd=%x, pszText=%s, nItem=%ld, isW=%d)\n", hwnd, debugstr_t(pszText, isW), nItem, isW);
4428
4429   if (!(lStyle & LVS_OWNERDATA))
4430   {
4431     if (!(hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4432           return FALSE;
4433
4434     if (!(lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4435           return FALSE;
4436   }
4437   else
4438   {
4439     ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4440     ZeroMemory(&item,sizeof(item));
4441     item.iItem = nItem;
4442     item.iSubItem = 0;
4443     item.mask = LVIF_PARAM | LVIF_STATE;
4444     ListView_GetItemW(hwnd, &item);
4445     lvItemRef.state = item.state;
4446     lvItemRef.iImage = item.iImage;
4447     lvItemRef.lParam = item.lParam;
4448     lpItem = &lvItemRef;
4449   }
4450
4451   ZeroMemory(&dispInfo, sizeof(dispInfo));
4452   dispInfo.item.mask = 0;
4453   dispInfo.item.iItem = nItem;
4454   dispInfo.item.state = lpItem->state;
4455   dispInfo.item.stateMask = 0;
4456   dispInfo.item.pszText = pszText;
4457   dispInfo.item.cchTextMax = textlenT(pszText, isW);
4458   dispInfo.item.iImage = lpItem->iImage;
4459   dispInfo.item.lParam = lpItem->lParam;
4460   infoPtr->Editing = FALSE;
4461
4462   /* Do we need to update the Item Text */
4463   if(dispinfo_notifyT(hwnd, LVN_ENDLABELEDITW, &dispInfo, isW))
4464     if (lpItem->pszText != LPSTR_TEXTCALLBACKW && !(lStyle & LVS_OWNERDATA))
4465       bResult = textsetptrT(&lpItem->pszText, pszText, isW);
4466
4467   return bResult;
4468 }
4469
4470 /***
4471  * DESCRIPTION:
4472  * Callback implementation for editlabel control
4473  *
4474  * PARAMETER(S):
4475  * [I] HWND : window handle
4476  * [I] LPSTR : modified text
4477  * [I] DWORD : item index
4478  *
4479  * RETURN:
4480  *   SUCCESS : TRUE
4481  *   FAILURE : FALSE
4482  */
4483 static BOOL LISTVIEW_EndEditLabelW(HWND hwnd, LPWSTR pszText, DWORD nItem)
4484 {
4485     return LISTVIEW_EndEditLabelT(hwnd, pszText, nItem, TRUE);
4486 }
4487
4488 /***
4489  * DESCRIPTION:
4490  * Callback implementation for editlabel control
4491  *
4492  * PARAMETER(S):
4493  * [I] HWND : window handle
4494  * [I] LPSTR : modified text
4495  * [I] DWORD : item index
4496  *
4497  * RETURN:
4498  *   SUCCESS : TRUE
4499  *   FAILURE : FALSE
4500  */
4501 static BOOL LISTVIEW_EndEditLabelA(HWND hwnd, LPSTR pszText, DWORD nItem)
4502 {
4503     return LISTVIEW_EndEditLabelT(hwnd, (LPWSTR)pszText, nItem, FALSE);
4504 }
4505
4506 /***
4507  * DESCRIPTION:
4508  * Begin in place editing of specified list view item
4509  *
4510  * PARAMETER(S):
4511  * [I] HWND : window handle
4512  * [I] INT : item index
4513  * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4514  *
4515  * RETURN:
4516  *   SUCCESS : TRUE
4517  *   FAILURE : FALSE
4518  */
4519 static HWND LISTVIEW_EditLabelT(HWND hwnd, INT nItem, BOOL isW)
4520 {
4521   NMLVDISPINFOW dispInfo;
4522   RECT rect;
4523   LISTVIEW_ITEM *lpItem;
4524   HWND hedit;
4525   HINSTANCE hinst = GetWindowLongW(hwnd, GWL_HINSTANCE);
4526   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4527   HDPA hdpaSubItems;
4528   WCHAR szDispText[DISP_TEXT_SIZE];
4529   LVITEMW lvItem;
4530   LISTVIEW_ITEM lvItemRef;
4531   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
4532
4533   if (~GetWindowLongW(hwnd, GWL_STYLE) & LVS_EDITLABELS)
4534       return FALSE;
4535
4536   /* Is the EditBox still there, if so remove it */
4537   if(infoPtr->hwndEdit != 0)
4538   {
4539       SetFocus(hwnd);
4540       infoPtr->hwndEdit = 0;
4541   }
4542
4543   LISTVIEW_SetSelection(hwnd, nItem);
4544   LISTVIEW_SetItemFocus(hwnd, nItem);
4545
4546   if (!(lStyle & LVS_OWNERDATA))
4547   {
4548     if (NULL == (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
4549           return 0;
4550
4551     if (NULL == (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
4552           return 0;
4553   }
4554   else
4555   {
4556     LVITEMW item;
4557     ZeroMemory(&lvItemRef,sizeof(LISTVIEW_ITEM));
4558     ZeroMemory(&item, sizeof(item));
4559     item.iItem = nItem;
4560     item.iSubItem = 0;
4561     item.mask = LVIF_PARAM | LVIF_STATE;
4562     ListView_GetItemW(hwnd, &item);
4563     lvItemRef.iImage = item.iImage;
4564     lvItemRef.state = item.state;
4565     lvItemRef.lParam = item.lParam;
4566     lpItem = &lvItemRef;
4567   }
4568
4569   /* get information needed for drawing the item */
4570   ZeroMemory(&lvItem, sizeof(lvItem));
4571   lvItem.mask = LVIF_TEXT;
4572   lvItem.iItem = nItem;
4573   lvItem.iSubItem = 0;
4574   lvItem.cchTextMax = DISP_TEXT_SIZE;
4575   lvItem.pszText = szDispText;
4576   *lvItem.pszText = '\0';
4577   LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, isW);
4578
4579   ZeroMemory(&dispInfo, sizeof(dispInfo));
4580   dispInfo.item.mask = 0;
4581   dispInfo.item.iItem = nItem;
4582   dispInfo.item.state = lpItem->state;
4583   dispInfo.item.stateMask = 0;
4584   dispInfo.item.pszText = lvItem.pszText;
4585   dispInfo.item.cchTextMax = lstrlenW(lvItem.pszText);
4586   dispInfo.item.iImage = lpItem->iImage;
4587   dispInfo.item.lParam = lpItem->lParam;
4588
4589   if (dispinfo_notifyT(hwnd, LVN_BEGINLABELEDITW, &dispInfo, isW))
4590           return 0;
4591
4592   rect.left = LVIR_LABEL;
4593   if (!LISTVIEW_GetItemRect(hwnd, nItem, &rect))
4594           return 0;
4595
4596   if (!(hedit = CreateEditLabelT(szDispText , WS_VISIBLE,
4597                  rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, hwnd, hinst,
4598                  isW ? LISTVIEW_EndEditLabelW : (EditlblCallbackW)LISTVIEW_EndEditLabelA,
4599                  nItem, isW)))
4600          return 0;
4601
4602   infoPtr->hwndEdit = hedit;
4603
4604   ShowWindow(infoPtr->hwndEdit,SW_NORMAL);
4605   infoPtr->Editing = TRUE;
4606   SetFocus(infoPtr->hwndEdit);
4607   SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4608   return infoPtr->hwndEdit;
4609 }
4610
4611
4612 /***
4613  * DESCRIPTION:
4614  * Ensures the specified item is visible, scrolling into view if necessary.
4615  *
4616  * PARAMETER(S):
4617  * [I] HWND : window handle
4618  * [I] INT : item index
4619  * [I] BOOL : partially or entirely visible
4620  *
4621  * RETURN:
4622  *   SUCCESS : TRUE
4623  *   FAILURE : FALSE
4624  */
4625 static BOOL LISTVIEW_EnsureVisible(HWND hwnd, INT nItem, BOOL bPartial)
4626 {
4627   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4628   UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
4629   INT nScrollPosHeight = 0;
4630   INT nScrollPosWidth = 0;
4631   SCROLLINFO scrollInfo;
4632   RECT rcItem;
4633   BOOL bRedraw = FALSE;
4634
4635   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
4636   scrollInfo.cbSize = sizeof(SCROLLINFO);
4637   scrollInfo.fMask = SIF_POS;
4638
4639   /* ALWAYS bPartial == FALSE, FOR NOW! */
4640
4641   rcItem.left = LVIR_BOUNDS;
4642   if (LISTVIEW_GetItemRect(hwnd, nItem, &rcItem) != FALSE)
4643   {
4644     if (rcItem.left < infoPtr->rcList.left)
4645     {
4646       if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4647       {
4648         /* scroll left */
4649         bRedraw = TRUE;
4650         if (uView == LVS_LIST)
4651         {
4652           nScrollPosWidth = infoPtr->nItemWidth;
4653           rcItem.left += infoPtr->rcList.left;
4654         }
4655         else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4656         {
4657           nScrollPosWidth = 1;
4658           rcItem.left += infoPtr->rcList.left;
4659         }
4660
4661         /* When in LVS_REPORT view, the scroll position should
4662            not be updated. */
4663         if (nScrollPosWidth != 0)
4664         {
4665           if (rcItem.left % nScrollPosWidth == 0)
4666             scrollInfo.nPos += rcItem.left / nScrollPosWidth;
4667           else
4668             scrollInfo.nPos += rcItem.left / nScrollPosWidth - 1;
4669
4670           SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4671         }
4672       }
4673     }
4674     else if (rcItem.right > infoPtr->rcList.right)
4675     {
4676       if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
4677       {
4678         /* scroll right */
4679         bRedraw = TRUE;
4680         if (uView == LVS_LIST)
4681         {
4682           rcItem.right -= infoPtr->rcList.right;
4683           nScrollPosWidth = infoPtr->nItemWidth;
4684         }
4685         else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4686         {
4687           rcItem.right -= infoPtr->rcList.right;
4688           nScrollPosWidth = 1;
4689         }
4690
4691         /* When in LVS_REPORT view, the scroll position should
4692            not be updated. */
4693         if (nScrollPosWidth != 0)
4694         {
4695           if (rcItem.right % nScrollPosWidth == 0)
4696             scrollInfo.nPos += rcItem.right / nScrollPosWidth;
4697           else
4698             scrollInfo.nPos += rcItem.right / nScrollPosWidth + 1;
4699
4700           SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
4701         }
4702       }
4703     }
4704
4705     if (rcItem.top < infoPtr->rcList.top)
4706     {
4707       /* scroll up */
4708       bRedraw = TRUE;
4709       if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4710       {
4711         if (uView == LVS_REPORT)
4712         {
4713           rcItem.top -= infoPtr->rcList.top;
4714           nScrollPosHeight = infoPtr->nItemHeight;
4715         }
4716         else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4717         {
4718           nScrollPosHeight = 1;
4719           rcItem.top += infoPtr->rcList.top;
4720         }
4721
4722         if (rcItem.top % nScrollPosHeight == 0)
4723           scrollInfo.nPos += rcItem.top / nScrollPosHeight;
4724         else
4725           scrollInfo.nPos += rcItem.top / nScrollPosHeight - 1;
4726
4727         SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4728       }
4729     }
4730     else if (rcItem.bottom > infoPtr->rcList.bottom)
4731     {
4732       /* scroll down */
4733       bRedraw = TRUE;
4734       if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
4735       {
4736         if (uView == LVS_REPORT)
4737         {
4738           rcItem.bottom -= infoPtr->rcList.bottom;
4739           nScrollPosHeight = infoPtr->nItemHeight;
4740         }
4741         else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4742         {
4743           nScrollPosHeight = 1;
4744           rcItem.bottom -= infoPtr->rcList.bottom;
4745         }
4746
4747         if (rcItem.bottom % nScrollPosHeight == 0)
4748           scrollInfo.nPos += rcItem.bottom / nScrollPosHeight;
4749         else
4750           scrollInfo.nPos += rcItem.bottom / nScrollPosHeight + 1;
4751
4752         SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
4753       }
4754     }
4755   }
4756
4757   if(bRedraw)
4758     InvalidateRect(hwnd,NULL,TRUE);
4759   return TRUE;
4760 }
4761
4762 /***
4763  * DESCRIPTION:
4764  * Retrieves the nearest item, given a position and a direction.
4765  *
4766  * PARAMETER(S):
4767  * [I] HWND : window handle
4768  * [I] POINT : start position
4769  * [I] UINT : direction
4770  *
4771  * RETURN:
4772  * Item index if successdful, -1 otherwise.
4773  */
4774 static INT LISTVIEW_GetNearestItem(HWND hwnd, POINT pt, UINT vkDirection)
4775 {
4776   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4777   LV_INTHIT lvIntHit;
4778   INT nItem = -1;
4779   RECT rcView;
4780
4781   TRACE("point %ld,%ld, direction %s\n", pt.x, pt.y,
4782         (vkDirection == VK_DOWN) ? "VK_DOWN" :
4783         ((vkDirection == VK_UP) ? "VK_UP" :
4784         ((vkDirection == VK_LEFT) ? "VK_LEFT" : "VK_RIGHT")));
4785
4786   if (LISTVIEW_GetViewRect(hwnd, &rcView) != FALSE)
4787   {
4788     ZeroMemory(&lvIntHit, sizeof(lvIntHit));
4789     LISTVIEW_GetOrigin(hwnd, &lvIntHit.ht.pt);
4790     lvIntHit.ht.pt.x += pt.x;
4791     lvIntHit.ht.pt.y += pt.y;
4792
4793     if (vkDirection == VK_DOWN)
4794       lvIntHit.ht.pt.y += infoPtr->nItemHeight;
4795     else if (vkDirection == VK_UP)
4796       lvIntHit.ht.pt.y -= infoPtr->nItemHeight;
4797     else if (vkDirection == VK_LEFT)
4798       lvIntHit.ht.pt.x -= infoPtr->nItemWidth;
4799     else if (vkDirection == VK_RIGHT)
4800       lvIntHit.ht.pt.x += infoPtr->nItemWidth;
4801
4802     if (PtInRect(&rcView, lvIntHit.ht.pt) == FALSE)
4803       return -1;
4804     else
4805     {
4806       nItem = LISTVIEW_SuperHitTestItem(hwnd, &lvIntHit, TRUE);
4807       return nItem == -1 ? lvIntHit.iDistItem : nItem;
4808     }
4809   }
4810
4811   return nItem;
4812 }
4813
4814 /***
4815  * DESCRIPTION:
4816  * Searches for an item with specific characteristics.
4817  *
4818  * PARAMETER(S):
4819  * [I] hwnd : window handle
4820  * [I] nStart : base item index
4821  * [I] lpFindInfo : item information to look for
4822  *
4823  * RETURN:
4824  *   SUCCESS : index of item
4825  *   FAILURE : -1
4826  */
4827 static LRESULT LISTVIEW_FindItemW(HWND hwnd, INT nStart,
4828                                   LPLVFINDINFOW lpFindInfo)
4829 {
4830   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4831   POINT ptItem;
4832   WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4833   LVITEMW lvItem;
4834   BOOL bWrap = FALSE;
4835   INT nItem = nStart;
4836   INT nLast = GETITEMCOUNT(infoPtr);
4837
4838   if ((nItem >= -1) && (lpFindInfo != NULL))
4839   {
4840     ZeroMemory(&lvItem, sizeof(lvItem));
4841
4842     if (lpFindInfo->flags & LVFI_PARAM)
4843     {
4844       lvItem.mask |= LVIF_PARAM;
4845     }
4846
4847     if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4848     {
4849       lvItem.mask |= LVIF_TEXT;
4850       lvItem.pszText = szDispText;
4851       lvItem.cchTextMax = DISP_TEXT_SIZE;
4852     }
4853
4854     if (lpFindInfo->flags & LVFI_WRAP)
4855       bWrap = TRUE;
4856
4857     if (lpFindInfo->flags & LVFI_NEARESTXY)
4858     {
4859       ptItem.x = lpFindInfo->pt.x;
4860       ptItem.y = lpFindInfo->pt.y;
4861     }
4862
4863     while (1)
4864     {
4865       while (nItem < nLast)
4866       {
4867         if (lpFindInfo->flags & LVFI_NEARESTXY)
4868         {
4869           nItem = LISTVIEW_GetNearestItem(hwnd, ptItem,
4870                                           lpFindInfo->vkDirection);
4871           if (nItem != -1)
4872           {
4873             /* get position of the new item index */
4874             if (ListView_GetItemPosition(hwnd, nItem, &ptItem) == FALSE)
4875               return -1;
4876           }
4877           else
4878             return -1;
4879         }
4880         else
4881         {
4882           nItem++;
4883         }
4884
4885         lvItem.iItem = nItem;
4886         lvItem.iSubItem = 0;
4887         if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
4888         {
4889           if (lvItem.mask & LVIF_TEXT)
4890           {
4891             if (lpFindInfo->flags & LVFI_PARTIAL)
4892             {
4893               if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL)
4894                 continue;
4895             }
4896             else
4897             {
4898               if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0)
4899                 continue;
4900             }
4901           }
4902
4903           if (lvItem.mask & LVIF_PARAM)
4904           {
4905             if (lpFindInfo->lParam != lvItem.lParam)
4906               continue;
4907           }
4908
4909           return nItem;
4910         }
4911       }
4912
4913       if (bWrap)
4914       {
4915         nItem = -1;
4916         nLast = nStart + 1;
4917         bWrap = FALSE;
4918       }
4919       else
4920       {
4921         return -1;
4922       }
4923     }
4924   }
4925
4926  return -1;
4927 }
4928
4929 /***
4930  * DESCRIPTION:
4931  * Searches for an item with specific characteristics.
4932  *
4933  * PARAMETER(S):
4934  * [I] hwnd : window handle
4935  * [I] nStart : base item index
4936  * [I] lpFindInfo : item information to look for
4937  *
4938  * RETURN:
4939  *   SUCCESS : index of item
4940  *   FAILURE : -1
4941  */
4942 static LRESULT LISTVIEW_FindItemA(HWND hwnd, INT nStart,
4943                                   LPLVFINDINFOA lpFindInfo)
4944 {
4945   BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4946   LVFINDINFOW fiw;
4947   LRESULT res;
4948
4949   memcpy(&fiw, lpFindInfo, sizeof(fiw));
4950   if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4951   res = LISTVIEW_FindItemW(hwnd, nStart, &fiw);
4952   if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4953   return res;
4954 }
4955
4956 /***
4957  * DESCRIPTION:
4958  * Retrieves the background color of the listview control.
4959  *
4960  * PARAMETER(S):
4961  * [I] HWND : window handle
4962  *
4963  * RETURN:
4964  * COLORREF associated with the background.
4965  */
4966 static LRESULT LISTVIEW_GetBkColor(HWND hwnd)
4967 {
4968   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
4969
4970   return infoPtr->clrBk;
4971 }
4972
4973 /***
4974  * DESCRIPTION:
4975  * Retrieves the background image of the listview control.
4976  *
4977  * PARAMETER(S):
4978  * [I] HWND : window handle
4979  * [O] LPLVMKBIMAGE : background image attributes
4980  *
4981  * RETURN:
4982  *   SUCCESS : TRUE
4983  *   FAILURE : FALSE
4984  */
4985 /* static LRESULT LISTVIEW_GetBkImage(HWND hwnd, LPLVBKIMAGE lpBkImage)   */
4986 /* {   */
4987 /*   FIXME (listview, "empty stub!\n"); */
4988 /*   return FALSE;   */
4989 /* }   */
4990
4991 /***
4992  * DESCRIPTION:
4993  * Retrieves the callback mask.
4994  *
4995  * PARAMETER(S):
4996  * [I] HWND : window handle
4997  *
4998  * RETURN:
4999  *  Value of mask
5000  */
5001 static UINT LISTVIEW_GetCallbackMask(HWND hwnd)
5002 {
5003   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5004
5005   return infoPtr->uCallbackMask;
5006 }
5007
5008 /***
5009  * DESCRIPTION:
5010  * Retrieves column attributes.
5011  *
5012  * PARAMETER(S):
5013  * [I] HWND : window handle
5014  * [I] INT :  column index
5015  * [IO] LPLVCOLUMNW : column information
5016  * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5017  *           otherwise it is in fact a LPLVCOLUMNA
5018  *
5019  * RETURN:
5020  *   SUCCESS : TRUE
5021  *   FAILURE : FALSE
5022  */
5023 static LRESULT LISTVIEW_GetColumnT(HWND hwnd, INT nItem, LPLVCOLUMNW lpColumn, BOOL isW)
5024 {
5025   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5026   HDITEMW hdi;
5027   BOOL bResult = FALSE;
5028
5029   if (lpColumn != NULL)
5030   {
5031
5032     /* initialize memory */
5033     ZeroMemory(&hdi, sizeof(hdi));
5034
5035     if (lpColumn->mask & LVCF_FMT)
5036       hdi.mask |= HDI_FORMAT;
5037
5038     if (lpColumn->mask & LVCF_WIDTH)
5039       hdi.mask |= HDI_WIDTH;
5040
5041     if (lpColumn->mask & LVCF_TEXT)
5042     {
5043       hdi.mask |= HDI_TEXT;
5044       hdi.cchTextMax = lpColumn->cchTextMax;
5045       hdi.pszText    = lpColumn->pszText;
5046     }
5047
5048     if (lpColumn->mask & LVCF_IMAGE)
5049       hdi.mask |= HDI_IMAGE;
5050
5051     if (lpColumn->mask & LVCF_ORDER)
5052       hdi.mask |= HDI_ORDER;
5053
5054     if (isW)
5055       bResult = Header_GetItemW(infoPtr->hwndHeader, nItem, &hdi);
5056     else
5057       bResult = Header_GetItemA(infoPtr->hwndHeader, nItem, &hdi);
5058
5059     if (bResult != FALSE)
5060     {
5061       if (lpColumn->mask & LVCF_FMT)
5062       {
5063         lpColumn->fmt = 0;
5064
5065         if (hdi.fmt & HDF_LEFT)
5066           lpColumn->fmt |= LVCFMT_LEFT;
5067         else if (hdi.fmt & HDF_RIGHT)
5068           lpColumn->fmt |= LVCFMT_RIGHT;
5069         else if (hdi.fmt & HDF_CENTER)
5070           lpColumn->fmt |= LVCFMT_CENTER;
5071
5072         if (hdi.fmt & HDF_IMAGE)
5073           lpColumn->fmt |= LVCFMT_COL_HAS_IMAGES;
5074
5075         if (hdi.fmt & HDF_BITMAP_ON_RIGHT)
5076           lpColumn->fmt |= LVCFMT_BITMAP_ON_RIGHT;
5077       }
5078
5079       if (lpColumn->mask & LVCF_WIDTH)
5080         lpColumn->cx = hdi.cxy;
5081
5082       if (lpColumn->mask & LVCF_IMAGE)
5083         lpColumn->iImage = hdi.iImage;
5084
5085       if (lpColumn->mask & LVCF_ORDER)
5086         lpColumn->iOrder = hdi.iOrder;
5087
5088       TRACE("(hwnd=%x, col=%d, lpColumn=%s, isW=%d)\n",
5089             hwnd, nItem, debuglvcolumn_t(lpColumn, isW), isW);
5090
5091     }
5092   }
5093
5094   return bResult;
5095 }
5096
5097
5098 static LRESULT LISTVIEW_GetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
5099 {
5100 /*  LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
5101     INT i;
5102
5103     if (!lpiArray)
5104         return FALSE;
5105
5106     /* little hack */
5107     for (i = 0; i < iCount; i++)
5108         lpiArray[i] = i;
5109
5110     return TRUE;
5111 }
5112
5113 /***
5114  * DESCRIPTION:
5115  * Retrieves the column width.
5116  *
5117  * PARAMETER(S):
5118  * [I] HWND : window handle
5119  * [I] int : column index
5120  *
5121  * RETURN:
5122  *   SUCCESS : column width
5123  *   FAILURE : zero
5124  */
5125 static LRESULT LISTVIEW_GetColumnWidth(HWND hwnd, INT nColumn)
5126 {
5127   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5128   UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5129   INT nColumnWidth = 0;
5130   HDITEMW hdi;
5131
5132   if (uView == LVS_LIST)
5133   {
5134     nColumnWidth = infoPtr->nItemWidth;
5135   }
5136   else if (uView == LVS_REPORT)
5137   {
5138     /* get column width from header */
5139     ZeroMemory(&hdi, sizeof(hdi));
5140     hdi.mask = HDI_WIDTH;
5141     if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi) != FALSE)
5142       nColumnWidth = hdi.cxy;
5143   }
5144
5145   return nColumnWidth;
5146 }
5147
5148 /***
5149  * DESCRIPTION:
5150  * In list or report display mode, retrieves the number of items that can fit
5151  * vertically in the visible area. In icon or small icon display mode,
5152  * retrieves the total number of visible items.
5153  *
5154  * PARAMETER(S):
5155  * [I] HWND : window handle
5156  *
5157  * RETURN:
5158  * Number of fully visible items.
5159  */
5160 static LRESULT LISTVIEW_GetCountPerPage(HWND hwnd)
5161 {
5162   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5163   UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5164   INT nItemCount = 0;
5165
5166   if (uView == LVS_LIST)
5167   {
5168     if (infoPtr->rcList.right > infoPtr->nItemWidth)
5169     {
5170       nItemCount = LISTVIEW_GetCountPerRow(hwnd) *
5171                    LISTVIEW_GetCountPerColumn(hwnd);
5172     }
5173   }
5174   else if (uView == LVS_REPORT)
5175   {
5176     nItemCount = LISTVIEW_GetCountPerColumn(hwnd);
5177   }
5178   else
5179   {
5180     nItemCount = GETITEMCOUNT(infoPtr);
5181   }
5182
5183   return nItemCount;
5184 }
5185
5186 /* LISTVIEW_GetEditControl */
5187
5188 /***
5189  * DESCRIPTION:
5190  * Retrieves the extended listview style.
5191  *
5192  * PARAMETERS:
5193  * [I] HWND  : window handle
5194  *
5195  * RETURN:
5196  *   SUCCESS : previous style
5197  *   FAILURE : 0
5198  */
5199 static LRESULT LISTVIEW_GetExtendedListViewStyle(HWND hwnd)
5200 {
5201     LISTVIEW_INFO *infoPtr;
5202
5203     /* make sure we can get the listview info */
5204     if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
5205         return (0);
5206
5207     return (infoPtr->dwExStyle);
5208 }
5209
5210 /***
5211  * DESCRIPTION:
5212  * Retrieves the handle to the header control.
5213  *
5214  * PARAMETER(S):
5215  * [I] HWND : window handle
5216  *
5217  * RETURN:
5218  * Header handle.
5219  */
5220 static LRESULT LISTVIEW_GetHeader(HWND hwnd)
5221 {
5222   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5223
5224   return infoPtr->hwndHeader;
5225 }
5226
5227 /* LISTVIEW_GetHotCursor */
5228
5229 /***
5230  * DESCRIPTION:
5231  * Returns the time that the mouse cursor must hover over an item
5232  * before it is selected.
5233  *
5234  * PARAMETER(S):
5235  * [I] HWND : window handle
5236  *
5237  * RETURN:
5238  *   Returns the previously set hover time or (DWORD)-1 to indicate that the
5239  *   hover time is set to the default hover time.
5240  */
5241 static LRESULT LISTVIEW_GetHoverTime(HWND hwnd)
5242 {
5243   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5244
5245   return infoPtr->dwHoverTime;
5246 }
5247
5248 /***
5249  * DESCRIPTION:
5250  * Retrieves an image list handle.
5251  *
5252  * PARAMETER(S):
5253  * [I] HWND : window handle
5254  * [I] INT : image list identifier
5255  *
5256  * RETURN:
5257  *   SUCCESS : image list handle
5258  *   FAILURE : NULL
5259  */
5260 static LRESULT LISTVIEW_GetImageList(HWND hwnd, INT nImageList)
5261 {
5262   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5263   HIMAGELIST himl = NULL;
5264
5265   switch (nImageList)
5266   {
5267   case LVSIL_NORMAL:
5268     himl = infoPtr->himlNormal;
5269     break;
5270   case LVSIL_SMALL:
5271     himl = infoPtr->himlSmall;
5272     break;
5273   case LVSIL_STATE:
5274     himl = infoPtr->himlState;
5275     break;
5276   }
5277
5278   return (LRESULT)himl;
5279 }
5280
5281 /* LISTVIEW_GetISearchString */
5282
5283 /***
5284  * DESCRIPTION:
5285  * Retrieves item attributes.
5286  *
5287  * PARAMETER(S):
5288  * [I] hwnd : window handle
5289  * [IO] lpLVItem : item info
5290  * [I] internal : if true then we will use tricks that avoid copies
5291  *               but are not compatible with the regular interface
5292  * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5293  *           if FALSE, the lpLVItem is a LPLVITEMA.
5294  *
5295  * RETURN:
5296  *   SUCCESS : TRUE
5297  *   FAILURE : FALSE
5298  */
5299 static LRESULT LISTVIEW_GetItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL internal, BOOL isW)
5300 {
5301   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5302   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
5303   NMLVDISPINFOW dispInfo;
5304   LISTVIEW_SUBITEM *lpSubItem;
5305   LISTVIEW_ITEM *lpItem;
5306   HDPA hdpaSubItems;
5307   void* null = NULL;
5308   INT* piImage = (INT*)&null;
5309   LPWSTR* ppszText= (LPWSTR*)&null;
5310   LPARAM* plParam = (LPARAM*)&null;
5311
5312   if (internal && !isW)
5313   {
5314     ERR("We can't have internal non-Unicode GetItem!\n");
5315     return FALSE;
5316   }
5317
5318   /* In the following:
5319    * lpLVItem describes the information requested by the user
5320    * lpItem/lpSubItem is what we have
5321    * dispInfo is a structure we use to request the missing
5322    *     information from the application
5323    */
5324
5325   TRACE("(hwnd=%x, lpLVItem=%s, internal=%d, isW=%d)\n",
5326         hwnd, debuglvitem_t(lpLVItem, isW), internal, isW);
5327
5328   if ((lpLVItem == NULL) || (lpLVItem->iItem < 0) ||
5329       (lpLVItem->iItem >= GETITEMCOUNT(infoPtr)))
5330     return FALSE;
5331
5332   ZeroMemory(&dispInfo, sizeof(dispInfo));
5333
5334   if (lStyle & LVS_OWNERDATA)
5335   {
5336     if (lpLVItem->mask & ~LVIF_STATE)
5337     {
5338       memcpy(&dispInfo.item, lpLVItem, sizeof(LVITEMW));
5339       dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5340       memcpy(lpLVItem, &dispInfo.item, sizeof(LVITEMW));
5341       TRACE("   getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5342     }
5343
5344     if ((lpLVItem->mask & LVIF_STATE)&&(lpLVItem->iSubItem == 0))
5345     {
5346       lpLVItem->state = 0;
5347       if (infoPtr->nFocusedItem == lpLVItem->iItem)
5348         lpLVItem->state |= LVIS_FOCUSED;
5349       if (LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5350         lpLVItem->state |= LVIS_SELECTED;
5351     }
5352
5353     return TRUE;
5354   }
5355
5356   hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5357   if (hdpaSubItems == NULL) return FALSE;
5358
5359   if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) == NULL)
5360     return FALSE;
5361
5362   ZeroMemory(&dispInfo.item, sizeof(LVITEMW));
5363   if (lpLVItem->iSubItem == 0)
5364   {
5365     piImage=&lpItem->iImage;
5366     ppszText=&lpItem->pszText;
5367     plParam=&lpItem->lParam;
5368     if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask)
5369     {
5370       dispInfo.item.mask |= LVIF_STATE;
5371       dispInfo.item.stateMask = infoPtr->uCallbackMask;
5372     }
5373   }
5374   else
5375   {
5376     lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
5377     if (lpSubItem != NULL)
5378     {
5379       piImage=&lpSubItem->iImage;
5380       ppszText=&lpSubItem->pszText;
5381     }
5382   }
5383
5384   if ((lpLVItem->mask & LVIF_IMAGE) && (*piImage==I_IMAGECALLBACK))
5385   {
5386     dispInfo.item.mask |= LVIF_IMAGE;
5387   }
5388
5389   if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(*ppszText))
5390   {
5391     dispInfo.item.mask |= LVIF_TEXT;
5392     dispInfo.item.pszText = lpLVItem->pszText;
5393     dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5394     if (dispInfo.item.pszText && lpLVItem->cchTextMax > 0)
5395       *dispInfo.item.pszText = '\0';
5396     if (dispInfo.item.pszText && (*ppszText == NULL))
5397       *dispInfo.item.pszText = '\0';
5398   }
5399
5400   if (dispInfo.item.mask != 0)
5401   {
5402     /* We don't have all the requested info, query the application */
5403     dispInfo.item.iItem = lpLVItem->iItem;
5404     dispInfo.item.iSubItem = lpLVItem->iSubItem;
5405     dispInfo.item.lParam = lpItem->lParam;
5406     dispinfo_notifyT(hwnd, LVN_GETDISPINFOW, &dispInfo, isW);
5407     TRACE("   getdispinfo(2):lpLVItem=%s\n", debuglvitem_t(&dispInfo.item, isW));
5408   }
5409
5410   if (dispInfo.item.mask & LVIF_IMAGE)
5411   {
5412     lpLVItem->iImage = dispInfo.item.iImage;
5413     if ((dispInfo.item.mask & LVIF_DI_SETITEM) && (*piImage==I_IMAGECALLBACK))
5414       *piImage = dispInfo.item.iImage;
5415   }
5416   else if (lpLVItem->mask & LVIF_IMAGE)
5417   {
5418     lpLVItem->iImage = *piImage;
5419   }
5420
5421   if (dispInfo.item.mask & LVIF_PARAM)
5422   {
5423     lpLVItem->lParam = dispInfo.item.lParam;
5424     if (dispInfo.item.mask & LVIF_DI_SETITEM)
5425       *plParam = dispInfo.item.lParam;
5426   }
5427   else if (lpLVItem->mask & LVIF_PARAM)
5428     lpLVItem->lParam = lpItem->lParam;
5429
5430   if (dispInfo.item.mask & LVIF_TEXT)
5431   {
5432     if ((dispInfo.item.mask & LVIF_DI_SETITEM) && *ppszText)
5433       textsetptrT(ppszText, dispInfo.item.pszText, isW);
5434
5435     /* If lpLVItem->pszText==dispInfo.item.pszText a copy is unnecessary, but */
5436     /* some apps give a new pointer in ListView_Notify so we can't be sure.  */
5437     if (lpLVItem->pszText != dispInfo.item.pszText)
5438         textcpynT(lpLVItem->pszText, isW, dispInfo.item.pszText, isW, lpLVItem->cchTextMax);
5439
5440   }
5441   else if (lpLVItem->mask & LVIF_TEXT)
5442   {
5443     if (internal) lpLVItem->pszText = *ppszText;
5444     else textcpynT(lpLVItem->pszText, isW, *ppszText, TRUE, lpLVItem->cchTextMax);
5445   }
5446
5447   if (lpLVItem->iSubItem == 0)
5448   {
5449     if (dispInfo.item.mask & LVIF_STATE)
5450     {
5451       lpLVItem->state = lpItem->state;
5452       lpLVItem->state &= ~dispInfo.item.stateMask;
5453       lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5454
5455       lpLVItem->state &= ~LVIS_SELECTED;
5456       if ((dispInfo.item.stateMask & LVIS_SELECTED) &&
5457           LISTVIEW_IsSelected(hwnd,dispInfo.item.iItem))
5458         lpLVItem->state |= LVIS_SELECTED;
5459     }
5460     else if (lpLVItem->mask & LVIF_STATE)
5461     {
5462       lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5463
5464       lpLVItem->state &= ~LVIS_SELECTED;
5465       if ((lpLVItem->stateMask & LVIS_SELECTED) &&
5466           LISTVIEW_IsSelected(hwnd,lpLVItem->iItem))
5467          lpLVItem->state |= LVIS_SELECTED;
5468     }
5469
5470     if (lpLVItem->mask & LVIF_PARAM)
5471       lpLVItem->lParam = lpItem->lParam;
5472
5473     if (lpLVItem->mask & LVIF_INDENT)
5474       lpLVItem->iIndent = lpItem->iIndent;
5475   }
5476
5477   return TRUE;
5478 }
5479
5480 /* LISTVIEW_GetHotCursor */
5481
5482 /***
5483  * DESCRIPTION:
5484  * Retrieves the index of the hot item.
5485  *
5486  * PARAMETERS:
5487  * [I] HWND  : window handle
5488  *
5489  * RETURN:
5490  *   SUCCESS : hot item index
5491  *   FAILURE : -1 (no hot item)
5492  */
5493 static LRESULT LISTVIEW_GetHotItem(HWND hwnd)
5494 {
5495   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5496
5497   return infoPtr->nHotItem;
5498 }
5499
5500 /* LISTVIEW_GetHoverTime */
5501
5502 /***
5503  * DESCRIPTION:
5504  * Retrieves the number of items in the listview control.
5505  *
5506  * PARAMETER(S):
5507  * [I] HWND : window handle
5508  *
5509  * RETURN:
5510  * Number of items.
5511  */
5512 static LRESULT LISTVIEW_GetItemCount(HWND hwnd)
5513 {
5514   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5515
5516   return GETITEMCOUNT(infoPtr);
5517 }
5518
5519 /***
5520  * DESCRIPTION:
5521  * Retrieves the rectangle enclosing the item icon and text.
5522  *
5523  * PARAMETER(S):
5524  * [I] HWND : window handle
5525  * [I] INT : item index
5526  * [O] LPRECT : coordinate information
5527  *
5528  * RETURN:
5529  *   SUCCESS : TRUE
5530  *   FAILURE : FALSE
5531  */
5532 static BOOL LISTVIEW_GetItemBoundBox(HWND hwnd, INT nItem, LPRECT lpRect)
5533 {
5534   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5535   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
5536   UINT uView = lStyle & LVS_TYPEMASK;
5537   BOOL bResult = FALSE;
5538   HDPA hdpaSubItems;
5539   LISTVIEW_ITEM *lpItem;
5540   INT nCountPerColumn;
5541   INT nRow;
5542
5543   TRACE("(hwnd=%x,nItem=%d,lpRect=%p)\n", hwnd, nItem, lpRect);
5544
5545   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5546       (lpRect != NULL))
5547   {
5548     if (uView == LVS_LIST)
5549     {
5550       bResult = TRUE;
5551       nItem = nItem - ListView_GetTopIndex(hwnd);
5552       nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
5553       if (nItem < 0)
5554       {
5555         nRow = nItem % nCountPerColumn;
5556         if (nRow == 0)
5557         {
5558           lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5559           lpRect->top = 0;
5560         }
5561         else
5562         {
5563           lpRect->left = (nItem / nCountPerColumn -1) * infoPtr->nItemWidth;
5564           lpRect->top = (nRow + nCountPerColumn) * infoPtr->nItemHeight;
5565         }
5566       }
5567       else
5568       {
5569         lpRect->left = nItem / nCountPerColumn * infoPtr->nItemWidth;
5570         lpRect->top = nItem % nCountPerColumn * infoPtr->nItemHeight;
5571       }
5572     }
5573     else if (uView == LVS_REPORT)
5574     {
5575       bResult = TRUE;
5576       lpRect->left = REPORT_MARGINX;
5577       lpRect->top = ((nItem - ListView_GetTopIndex(hwnd)) *
5578                          infoPtr->nItemHeight) + infoPtr->rcList.top;
5579
5580       if (!(lStyle & LVS_NOSCROLL))
5581       {
5582         SCROLLINFO scrollInfo;
5583         /* Adjust position by scrollbar offset */
5584         ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
5585         scrollInfo.cbSize = sizeof(SCROLLINFO);
5586         scrollInfo.fMask = SIF_POS;
5587         GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
5588         lpRect->left -= scrollInfo.nPos;
5589       }
5590     }
5591     else /* either LVS_ICON or LVS_SMALLICON */
5592     {
5593       if ((hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)))
5594       {
5595         if ((lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)))
5596         {
5597           bResult = TRUE;
5598           lpRect->left = lpItem->ptPosition.x;
5599           lpRect->top = lpItem->ptPosition.y;
5600         }
5601       }
5602     }
5603   }
5604   lpRect->right = lpRect->left + infoPtr->nItemWidth;
5605   lpRect->bottom = lpRect->top + infoPtr->nItemHeight;
5606   TRACE("result %s: (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
5607         lpRect->left, lpRect->top, lpRect->right, lpRect->bottom);
5608   return bResult;
5609 }
5610
5611 /***
5612  * DESCRIPTION:
5613  * Retrieves the position (upper-left) of the listview control item.
5614  * Note that for LVS_ICON style, the upper-left is that of the icon
5615  * and not the bounding box.
5616  *
5617  * PARAMETER(S):
5618  * [I] HWND : window handle
5619  * [I] INT : item index
5620  * [O] LPPOINT : coordinate information
5621  *
5622  * RETURN:
5623  *   SUCCESS : TRUE
5624  *   FAILURE : FALSE
5625  */
5626 static BOOL LISTVIEW_GetItemPosition(HWND hwnd, INT nItem, LPPOINT lpptPosition)
5627 {
5628   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
5629   UINT uView = GetWindowLongA(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5630   BOOL bResult = FALSE;
5631   RECT rcBounding;
5632
5633   TRACE("(hwnd=%x, nItem=%d, lpptPosition=%p)\n", hwnd, nItem, lpptPosition);
5634
5635   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) &&
5636       (lpptPosition != NULL))
5637   {
5638     bResult = LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcBounding);
5639     lpptPosition->x = rcBounding.left;
5640     lpptPosition->y = rcBounding.top;
5641     if (uView == LVS_ICON)
5642     {
5643        lpptPosition->y += ICON_TOP_PADDING;
5644        lpptPosition->x += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
5645     }
5646     TRACE("result %s (%ld,%ld)\n", bResult ? "TRUE" : "FALSE",
5647           lpptPosition->x, lpptPosition->y);
5648    }
5649    return bResult;
5650 }
5651
5652 /***
5653  * Update the bounding rectangle around the text under a large icon.
5654  * This depends on whether it has the focus or not.
5655  * On entry the rectangle's top, left and right should be set.
5656  * On return the bottom will also be set and the width may have been
5657  * modified.
5658  *
5659  * This appears to be weird, even in the Microsoft implementation.
5660  */
5661
5662 static void ListView_UpdateLargeItemLabelRect (
5663         HWND hwnd,                    /* The window of the listview */
5664         const LISTVIEW_INFO *infoPtr, /* The listview itself */
5665         int nItem, /* The item for which we are calculating this */
5666         RECT *rect) /* The rectangle to be updated */
5667 {
5668     HDC hdc = GetDC (hwnd);
5669     HFONT hOldFont = SelectObject (hdc, infoPtr->hFont);
5670
5671     if (infoPtr->bFocus && infoPtr->nFocusedItem == nItem)
5672     {
5673         /* We (aim to) display the full text.  In Windows 95 it appears to
5674          * calculate the size assuming the specified font and then it draws
5675          * the text in that region with the specified font except scaled to
5676          * 10 point (or the height of the system font or ...).  Thus if the
5677          * window has 24 point Helvetica the highlit rectangle will be
5678          * taller than the text and if it is 7 point Helvetica then the text
5679          * will be clipped.
5680          * For now we will simply say that it is the correct size to display
5681          * the text in the specified font.
5682          */
5683         LVITEMW lvItem;
5684         lvItem.mask = LVIF_TEXT;
5685         lvItem.iItem = nItem;
5686         lvItem.iSubItem = 0;
5687         /* We will specify INTERNAL and so will receive back a const
5688          * pointer to the text, rather than specifying a buffer to which
5689          * to copy it.
5690          */
5691         LISTVIEW_GetItemW (hwnd, &lvItem, TRUE);
5692         DrawTextW (hdc, lvItem.pszText, -1, rect, DT_CALCRECT |
5693                         DT_NOCLIP | DT_EDITCONTROL | DT_TOP | DT_CENTER |
5694                         DT_WORDBREAK | DT_NOPREFIX);
5695         /* Maintain this DT_* list in line with LISTVIEW_DrawLargeItem */
5696     }
5697     else
5698     {
5699         /* As far as I can see the text region seems to be trying to be
5700          * "tall enough for two lines of text".  Once again (comctl32.dll ver
5701          * 5.81?) it measures this on the basis of the selected font and then
5702          * draws it with the same font except in 10 point size.  This can lead
5703          * to more or less than the two rows appearing.
5704          * Question; are we  supposed to be including DT_EXTERNALLEADING?
5705          * Question; should the width be shrunk to the space required to
5706          * display the two lines?
5707          */
5708         rect->bottom = rect->top + 2 * infoPtr->ntmHeight;
5709     }
5710
5711     SelectObject (hdc, hOldFont);
5712     ReleaseDC (hwnd, hdc);
5713 }
5714
5715 /***
5716  * DESCRIPTION:
5717  * Retrieves the bounding rectangle for a listview control item.
5718  *
5719  * PARAMETER(S):
5720  * [I] HWND : window handle
5721  * [I] INT : item index
5722  * [IO] LPRECT : bounding rectangle coordinates
5723  *     lprc->left specifies the portion of the item for which the bounding
5724  *     rectangle will be retrieved.
5725  *
5726  *     LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5727  *        including the icon and label.
5728  *     LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5729  *     LVIR_LABEL Returns the bounding rectangle of the item text.
5730  *     LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5731  *      rectangles, but excludes columns in report view.
5732  *
5733  * RETURN:
5734  *   SUCCESS : TRUE
5735  *   FAILURE : FALSE
5736  *
5737  * NOTES
5738  *   Note that the bounding rectangle of the label in the LVS_ICON view depends
5739  *   upon whether the window has the focus currently and on whether the item
5740  *   is the one with the focus.  Ensure that the control's record of which
5741  *   item has the focus agrees with the items' records.
5742  */
5743 static LRESULT LISTVIEW_GetItemRect(HWND hwnd, INT nItem, LPRECT lprc)
5744 {
5745   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
5746   UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
5747   BOOL bResult = FALSE;
5748   POINT ptOrigin;
5749   POINT ptItem;
5750   INT nLeftPos;
5751   INT nLabelWidth;
5752   INT nIndent;
5753   LVITEMW lvItem;
5754   RECT rcInternal;
5755
5756   TRACE("(hwnd=%x, nItem=%d, lprc=%p)\n", hwnd, nItem, lprc);
5757
5758   if (uView & LVS_REPORT)
5759   {
5760     ZeroMemory(&lvItem, sizeof(lvItem));
5761     lvItem.mask = LVIF_INDENT;
5762     lvItem.iItem = nItem;
5763     lvItem.iSubItem = 0;
5764     LISTVIEW_GetItemW(hwnd, &lvItem, TRUE);
5765
5766     /* do indent */
5767     if (lvItem.iIndent>0 && infoPtr->iconSize.cx > 0)
5768       nIndent = infoPtr->iconSize.cx * lvItem.iIndent;
5769     else
5770       nIndent = 0;
5771   }
5772   else
5773     nIndent = 0;
5774
5775   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)) && (lprc != NULL))
5776   {
5777       switch(lprc->left)
5778       {
5779       case LVIR_ICON:
5780         if (!LISTVIEW_GetItemPosition(hwnd, nItem, &ptItem)) break;
5781         if (uView == LVS_ICON)
5782         {
5783           if (infoPtr->himlNormal != NULL)
5784           {
5785             if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5786             {
5787               bResult = TRUE;
5788               lprc->left = ptItem.x + ptOrigin.x;
5789               lprc->top = ptItem.y + ptOrigin.y;
5790               lprc->right = lprc->left + infoPtr->iconSize.cx;
5791               lprc->bottom = (lprc->top + infoPtr->iconSize.cy +
5792                               ICON_BOTTOM_PADDING + ICON_TOP_PADDING);
5793             }
5794           }
5795         }
5796         else if (uView == LVS_SMALLICON)
5797         {
5798           if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5799           {
5800             bResult = TRUE;
5801             lprc->left = ptItem.x + ptOrigin.x;
5802             lprc->top = ptItem.y + ptOrigin.y;
5803             lprc->bottom = lprc->top + infoPtr->nItemHeight;
5804
5805             if (infoPtr->himlState != NULL)
5806               lprc->left += infoPtr->iconSize.cx;
5807
5808             if (infoPtr->himlSmall != NULL)
5809               lprc->right = lprc->left + infoPtr->iconSize.cx;
5810             else
5811               lprc->right = lprc->left;
5812           }
5813         }
5814         else
5815         {
5816           bResult = TRUE;
5817           lprc->left = ptItem.x;
5818           if (uView & LVS_REPORT)
5819             lprc->left += nIndent;
5820           lprc->top = ptItem.y;
5821           lprc->bottom = lprc->top + infoPtr->nItemHeight;
5822
5823           if (infoPtr->himlState != NULL)
5824             lprc->left += infoPtr->iconSize.cx;
5825
5826           if (infoPtr->himlSmall != NULL)
5827             lprc->right = lprc->left + infoPtr->iconSize.cx;
5828           else
5829             lprc->right = lprc->left;
5830         }
5831         break;
5832
5833       case LVIR_LABEL:
5834         if (!LISTVIEW_GetItemPosition(hwnd, nItem, &ptItem)) break;
5835         if (uView == LVS_ICON)
5836         {
5837           if (infoPtr->himlNormal != NULL)
5838           {
5839             if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5840             {
5841               bResult = TRUE;
5842               lprc->left = ptItem.x + ptOrigin.x;
5843               lprc->top = (ptItem.y + ptOrigin.y + infoPtr->iconSize.cy +
5844                            ICON_BOTTOM_PADDING);
5845               nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5846               if (infoPtr->iconSpacing.cx - nLabelWidth > 1)
5847               {
5848                 lprc->left += (infoPtr->iconSpacing.cx - nLabelWidth) / 2;
5849                 lprc->right = lprc->left + nLabelWidth;
5850               }
5851               else
5852               {
5853                 lprc->left += 1;
5854                 lprc->right = lprc->left + infoPtr->iconSpacing.cx - 1;
5855                 ListView_UpdateLargeItemLabelRect (hwnd, infoPtr, nItem, lprc);
5856               }
5857               lprc->bottom = lprc->top + infoPtr->ntmHeight + HEIGHT_PADDING;
5858             }
5859           }
5860         }
5861         else if (uView == LVS_SMALLICON)
5862         {
5863           if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5864           {
5865             bResult = TRUE;
5866             nLeftPos = lprc->left = ptItem.x + ptOrigin.x;
5867             lprc->top = ptItem.y + ptOrigin.y;
5868             lprc->bottom = lprc->top + infoPtr->nItemHeight;
5869
5870             if (infoPtr->himlState != NULL)
5871               lprc->left += infoPtr->iconSize.cx;
5872
5873             if (infoPtr->himlSmall != NULL)
5874               lprc->left += infoPtr->iconSize.cx;
5875
5876             nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5877             nLabelWidth += TRAILING_PADDING;
5878             if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5879               lprc->right = lprc->left + nLabelWidth;
5880             else
5881               lprc->right = nLeftPos + infoPtr->nItemWidth;
5882           }
5883         }
5884         else
5885         {
5886           bResult = TRUE;
5887           if (uView == LVS_REPORT)
5888             nLeftPos = lprc->left = ptItem.x + nIndent;
5889           else
5890             nLeftPos = lprc->left = ptItem.x;
5891           lprc->top = ptItem.y;
5892           lprc->bottom = lprc->top + infoPtr->nItemHeight;
5893
5894           if (infoPtr->himlState != NULL)
5895             lprc->left += infoPtr->iconSize.cx;
5896
5897           if (infoPtr->himlSmall != NULL)
5898             lprc->left += infoPtr->iconSize.cx;
5899
5900           if (uView != LVS_REPORT)
5901           {
5902             nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5903             nLabelWidth += TRAILING_PADDING;
5904             if (infoPtr->himlSmall)
5905               nLabelWidth += IMAGE_PADDING;
5906           }
5907           else
5908             nLabelWidth = LISTVIEW_GetColumnWidth(hwnd, 0)-lprc->left;
5909           if (lprc->left + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
5910             lprc->right = lprc->left + nLabelWidth;
5911           else
5912             lprc->right = nLeftPos + infoPtr->nItemWidth;
5913         }
5914         break;
5915
5916       case LVIR_BOUNDS:
5917         if (!LISTVIEW_GetItemBoundBox(hwnd, nItem, &rcInternal)) break;
5918         ptItem.x = rcInternal.left;
5919         ptItem.y = rcInternal.top;
5920         if (uView == LVS_ICON)
5921         {
5922           if (infoPtr->himlNormal != NULL)
5923           {
5924             if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5925             {
5926               RECT label_rect;
5927               INT text_left, text_right, icon_left, text_pos_x;
5928               /* for style LVS_ICON bounds
5929                *            left = min(icon.left, text.left)
5930                *            right = max(icon.right, text.right)
5931                *            top = boundbox.top + NOTHITABLE
5932                *            bottom = text.bottom + 1
5933                */
5934               bResult = TRUE;
5935               icon_left = text_left = ptItem.x;
5936
5937               /* Correct ptItem to icon upper-left */
5938               icon_left += (infoPtr->nItemWidth - infoPtr->iconSize.cx)/2;
5939               ptItem.y += ICON_TOP_PADDING;
5940
5941               /* Compute the label left and right */
5942                nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5943                text_pos_x = infoPtr->iconSpacing.cx - 2*CAPTION_BORDER - nLabelWidth;
5944                if (text_pos_x > 1)
5945                {
5946                  text_left += text_pos_x / 2;
5947                  text_right = text_left + nLabelWidth + 2*CAPTION_BORDER;
5948                }
5949                else
5950                {
5951                  text_left += 1;
5952                  text_right = text_left + infoPtr->iconSpacing.cx - 1;
5953                }
5954
5955               /* Compute rectangle w/o the text height */
5956                lprc->left = min(icon_left, text_left) + ptOrigin.x;
5957                lprc->right = max(icon_left + infoPtr->iconSize.cx,
5958                                 text_right) + ptOrigin.x;
5959                lprc->top = ptItem.y + ptOrigin.y - ICON_TOP_PADDING_HITABLE;
5960                lprc->bottom = lprc->top + ICON_TOP_PADDING_HITABLE
5961                              + infoPtr->iconSize.cy + 1
5962                               + ICON_BOTTOM_PADDING;
5963
5964                CopyRect (&label_rect, lprc);
5965                label_rect.top = lprc->bottom;
5966                ListView_UpdateLargeItemLabelRect (hwnd, infoPtr, nItem, &label_rect);
5967                UnionRect (lprc, lprc, &label_rect);
5968             }
5969           }
5970         }
5971         else if (uView == LVS_SMALLICON)
5972         {
5973           if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
5974           {
5975             bResult = TRUE;
5976             lprc->left = ptItem.x + ptOrigin.x;
5977             lprc->right = lprc->left;
5978             lprc->top = ptItem.y + ptOrigin.y;
5979             lprc->bottom = lprc->top + infoPtr->nItemHeight;
5980             if (infoPtr->himlState != NULL)
5981               lprc->right += infoPtr->iconSize.cx;
5982             if (infoPtr->himlSmall != NULL)
5983               lprc->right += infoPtr->iconSize.cx;
5984
5985             nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
5986             nLabelWidth += TRAILING_PADDING;
5987             if (infoPtr->himlSmall)
5988               nLabelWidth += IMAGE_PADDING;
5989             if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
5990               lprc->right += nLabelWidth;
5991             else
5992               lprc->right = lprc->left + infoPtr->nItemWidth;
5993           }
5994         }
5995         else
5996         {
5997           bResult = TRUE;
5998           lprc->left = ptItem.x;
5999           if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && uView&LVS_REPORT)
6000             lprc->left += nIndent;
6001           lprc->right = lprc->left;
6002           lprc->top = ptItem.y;
6003           lprc->bottom = lprc->top + infoPtr->nItemHeight;
6004
6005           if ((infoPtr->dwExStyle & LVS_EX_FULLROWSELECT) || (uView == LVS_REPORT))
6006           {
6007             RECT br;
6008             int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
6009             Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
6010
6011             lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
6012           }
6013           else
6014           {
6015              if (infoPtr->himlState != NULL)
6016               lprc->right += infoPtr->iconSize.cx;
6017
6018             if (infoPtr->himlSmall != NULL)
6019               lprc->right += infoPtr->iconSize.cx;
6020
6021             nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
6022             nLabelWidth += TRAILING_PADDING;
6023             if (lprc->right + nLabelWidth < lprc->left + infoPtr->nItemWidth)
6024               lprc->right += nLabelWidth;
6025             else
6026               lprc->right = lprc->left + infoPtr->nItemWidth;
6027           }
6028         }
6029         break;
6030
6031       case LVIR_SELECTBOUNDS:
6032         if (!LISTVIEW_GetItemPosition(hwnd, nItem, &ptItem)) break;
6033         if (uView == LVS_ICON)
6034         {
6035           if (infoPtr->himlNormal != NULL)
6036           {
6037             if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
6038             {
6039               bResult = TRUE;
6040               lprc->left = ptItem.x + ptOrigin.x;
6041               lprc->top = ptItem.y + ptOrigin.y;
6042               lprc->right = lprc->left + infoPtr->iconSpacing.cx;
6043               lprc->bottom = lprc->top + infoPtr->iconSpacing.cy;
6044             }
6045           }
6046         }
6047         else if (uView == LVS_SMALLICON)
6048         {
6049           if (LISTVIEW_GetOrigin(hwnd, &ptOrigin) != FALSE)
6050           {
6051             bResult = TRUE;
6052             nLeftPos= lprc->left = ptItem.x + ptOrigin.x;
6053             lprc->top = ptItem.y + ptOrigin.y;
6054             lprc->bottom = lprc->top + infoPtr->nItemHeight;
6055
6056             if (infoPtr->himlState != NULL)
6057               lprc->left += infoPtr->iconSize.cx;
6058
6059             lprc->right = lprc->left;
6060
6061             if (infoPtr->himlSmall != NULL)
6062               lprc->right += infoPtr->iconSize.cx;
6063
6064             nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
6065             nLabelWidth += TRAILING_PADDING;
6066             if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
6067               lprc->right += nLabelWidth;
6068             else
6069               lprc->right = nLeftPos + infoPtr->nItemWidth;
6070           }
6071         }
6072         else
6073         {
6074           bResult = TRUE;
6075           if (!(infoPtr->dwExStyle&LVS_EX_FULLROWSELECT) && (uView&LVS_REPORT))
6076             nLeftPos = lprc->left = ptItem.x + nIndent;
6077           else
6078             nLeftPos = lprc->left = ptItem.x;
6079           lprc->top = ptItem.y;
6080           lprc->bottom = lprc->top + infoPtr->nItemHeight;
6081
6082           if (infoPtr->himlState != NULL)
6083             lprc->left += infoPtr->iconSize.cx;
6084
6085           lprc->right = lprc->left;
6086
6087           if (infoPtr->dwExStyle & LVS_EX_FULLROWSELECT)
6088           {
6089             RECT br;
6090             int nColumnCount = Header_GetItemCount(infoPtr->hwndHeader);
6091             Header_GetItemRect(infoPtr->hwndHeader, nColumnCount-1, &br);
6092
6093             lprc->right = max(lprc->left, br.right - REPORT_MARGINX);
6094           }
6095           else
6096           {
6097             if (infoPtr->himlSmall != NULL)
6098               lprc->right += infoPtr->iconSize.cx;
6099
6100             nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, nItem);
6101             nLabelWidth += TRAILING_PADDING;
6102             if (infoPtr->himlSmall)
6103               nLabelWidth += IMAGE_PADDING;
6104             if (lprc->right + nLabelWidth < nLeftPos + infoPtr->nItemWidth)
6105               lprc->right += nLabelWidth;
6106             else
6107               lprc->right = nLeftPos + infoPtr->nItemWidth;
6108           }
6109         }
6110         break;
6111       }
6112       TRACE("result %s (%d,%d)-(%d,%d)\n",
6113             (bResult) ? "TRUE" : "FALSE",
6114             lprc->left, lprc->top, lprc->right, lprc->bottom);
6115   }
6116
6117   TRACE("result %s (%d,%d)-(%d,%d)\n", bResult ? "TRUE" : "FALSE",
6118         lprc->left, lprc->top, lprc->right, lprc->bottom);
6119
6120   return bResult;
6121 }
6122
6123
6124 static LRESULT LISTVIEW_GetSubItemRect(HWND hwnd, INT nItem, INT nSubItem, INT
6125 flags, LPRECT lprc)
6126 {
6127     UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
6128     INT  count;
6129
6130     TRACE("(hwnd=%x, nItem=%d, nSubItem=%d lprc=%p)\n", hwnd, nItem, nSubItem,
6131             lprc);
6132
6133     if (!(uView & LVS_REPORT))
6134         return FALSE;
6135
6136     if (flags & LVIR_ICON)
6137     {
6138         FIXME("Unimplemented LVIR_ICON\n");
6139         return FALSE;
6140     }
6141     else
6142     {
6143         int top = min(((LISTVIEW_INFO *)GetWindowLongW(hwnd, 0))->nColumnCount,
6144                       nSubItem - 1);
6145
6146         LISTVIEW_GetItemRect(hwnd,nItem,lprc);
6147         for (count = 0; count < top; count++)
6148             lprc->left += LISTVIEW_GetColumnWidth(hwnd,count);
6149
6150         lprc->right = LISTVIEW_GetColumnWidth(hwnd,(nSubItem-1)) +
6151                             lprc->left;
6152     }
6153     return TRUE;
6154 }
6155
6156
6157 /***
6158  * DESCRIPTION:
6159  * Retrieves the width of a label.
6160  *
6161  * PARAMETER(S):
6162  * [I] HWND : window handle
6163  *
6164  * RETURN:
6165  *   SUCCESS : string width (in pixels)
6166  *   FAILURE : zero
6167  */
6168 static INT LISTVIEW_GetLabelWidth(HWND hwnd, INT nItem)
6169 {
6170   WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6171   INT nLabelWidth = 0;
6172   LVITEMW lvItem;
6173
6174   TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
6175
6176   ZeroMemory(&lvItem, sizeof(lvItem));
6177   lvItem.mask = LVIF_TEXT;
6178   lvItem.iItem = nItem;
6179   lvItem.cchTextMax = DISP_TEXT_SIZE;
6180   lvItem.pszText = szDispText;
6181   if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
6182     nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
6183
6184   return nLabelWidth;
6185 }
6186
6187 /***
6188  * DESCRIPTION:
6189  * Retrieves the spacing between listview control items.
6190  *
6191  * PARAMETER(S):
6192  * [I] HWND : window handle
6193  * [I] BOOL : flag for small or large icon
6194  *
6195  * RETURN:
6196  * Horizontal + vertical spacing
6197  */
6198 static LRESULT LISTVIEW_GetItemSpacing(HWND hwnd, BOOL bSmall)
6199 {
6200   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6201   LONG lResult;
6202
6203   if (bSmall == FALSE)
6204   {
6205     lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6206   }
6207   else
6208   {
6209     LONG style = GetWindowLongW(hwnd, GWL_STYLE);
6210     if ((style & LVS_TYPEMASK) == LVS_ICON)
6211       lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6212     else
6213       lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6214   }
6215   return lResult;
6216 }
6217
6218 /***
6219  * DESCRIPTION:
6220  * Retrieves the state of a listview control item.
6221  *
6222  * PARAMETER(S):
6223  * [I] HWND : window handle
6224  * [I] INT : item index
6225  * [I] UINT : state mask
6226  *
6227  * RETURN:
6228  * State specified by the mask.
6229  */
6230 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask)
6231 {
6232   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6233   LVITEMW lvItem;
6234   UINT uState = 0;
6235
6236   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6237   {
6238     ZeroMemory(&lvItem, sizeof(lvItem));
6239     lvItem.iItem = nItem;
6240     lvItem.stateMask = uMask;
6241     lvItem.mask = LVIF_STATE;
6242     if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
6243       uState = lvItem.state;
6244   }
6245
6246   return uState;
6247 }
6248
6249 /***
6250  * DESCRIPTION:
6251  * Retrieves the text of a listview control item or subitem.
6252  *
6253  * PARAMETER(S):
6254  * [I] hwnd : window handle
6255  * [I] nItem : item index
6256  * [IO] lpLVItem : item information
6257  * [I] isW :  TRUE if lpLVItem is Unicode
6258  *
6259  * RETURN:
6260  *   SUCCESS : string length
6261  *   FAILURE : 0
6262  */
6263 static LRESULT LISTVIEW_GetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6264 {
6265   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6266   INT nLength = 0;
6267
6268   if (lpLVItem != NULL)
6269   {
6270     if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6271     {
6272       lpLVItem->mask = LVIF_TEXT;
6273       lpLVItem->iItem = nItem;
6274       if (LISTVIEW_GetItemT(hwnd, lpLVItem, FALSE, isW))
6275         nLength = textlenT(lpLVItem->pszText, isW);
6276     }
6277   }
6278
6279   return nLength;
6280 }
6281
6282 /***
6283  * DESCRIPTION:
6284  * Searches for an item based on properties + relationships.
6285  *
6286  * PARAMETER(S):
6287  * [I] HWND : window handle
6288  * [I] INT : item index
6289  * [I] INT : relationship flag
6290  *
6291  * RETURN:
6292  *   SUCCESS : item index
6293  *   FAILURE : -1
6294  */
6295 static LRESULT LISTVIEW_GetNextItem(HWND hwnd, INT nItem, UINT uFlags)
6296 {
6297   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6298   UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
6299   UINT uMask = 0;
6300   LVFINDINFOW lvFindInfo;
6301   INT nCountPerColumn;
6302   INT i;
6303
6304   if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
6305   {
6306     ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6307
6308     if (uFlags & LVNI_CUT)
6309       uMask |= LVIS_CUT;
6310
6311     if (uFlags & LVNI_DROPHILITED)
6312       uMask |= LVIS_DROPHILITED;
6313
6314     if (uFlags & LVNI_FOCUSED)
6315       uMask |= LVIS_FOCUSED;
6316
6317     if (uFlags & LVNI_SELECTED)
6318       uMask |= LVIS_SELECTED;
6319
6320     if (uFlags & LVNI_ABOVE)
6321     {
6322       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6323       {
6324         while (nItem >= 0)
6325         {
6326           nItem--;
6327           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6328             return nItem;
6329         }
6330       }
6331       else
6332       {
6333         lvFindInfo.flags = LVFI_NEARESTXY;
6334         lvFindInfo.vkDirection = VK_UP;
6335         ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6336         while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6337         {
6338           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6339             return nItem;
6340         }
6341       }
6342     }
6343     else if (uFlags & LVNI_BELOW)
6344     {
6345       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6346       {
6347         while (nItem < GETITEMCOUNT(infoPtr))
6348         {
6349           nItem++;
6350           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6351             return nItem;
6352         }
6353       }
6354       else
6355       {
6356         lvFindInfo.flags = LVFI_NEARESTXY;
6357         lvFindInfo.vkDirection = VK_DOWN;
6358         ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6359         while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6360         {
6361           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6362             return nItem;
6363         }
6364       }
6365     }
6366     else if (uFlags & LVNI_TOLEFT)
6367     {
6368       if (uView == LVS_LIST)
6369       {
6370         nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6371         while (nItem - nCountPerColumn >= 0)
6372         {
6373           nItem -= nCountPerColumn;
6374           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6375             return nItem;
6376         }
6377       }
6378       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6379       {
6380         lvFindInfo.flags = LVFI_NEARESTXY;
6381         lvFindInfo.vkDirection = VK_LEFT;
6382         ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6383         while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6384         {
6385           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6386             return nItem;
6387         }
6388       }
6389     }
6390     else if (uFlags & LVNI_TORIGHT)
6391     {
6392       if (uView == LVS_LIST)
6393       {
6394         nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6395         while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
6396         {
6397           nItem += nCountPerColumn;
6398           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6399             return nItem;
6400         }
6401       }
6402       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6403       {
6404         lvFindInfo.flags = LVFI_NEARESTXY;
6405         lvFindInfo.vkDirection = VK_RIGHT;
6406         ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6407         while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6408         {
6409           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6410             return nItem;
6411         }
6412       }
6413     }
6414     else
6415     {
6416       nItem++;
6417
6418       /* search by index */
6419       for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
6420       {
6421         if ((ListView_GetItemState(hwnd, i, uMask) & uMask) == uMask)
6422           return i;
6423       }
6424     }
6425   }
6426
6427   return -1;
6428 }
6429
6430 /* LISTVIEW_GetNumberOfWorkAreas */
6431
6432 /***
6433  * DESCRIPTION:
6434  * Retrieves the origin coordinates when in icon or small icon display mode.
6435  *
6436  * PARAMETER(S):
6437  * [I] HWND : window handle
6438  * [O] LPPOINT : coordinate information
6439  *
6440  * RETURN:
6441  *   SUCCESS : TRUE
6442  *   FAILURE : FALSE
6443  */
6444 static LRESULT LISTVIEW_GetOrigin(HWND hwnd, LPPOINT lpptOrigin)
6445 {
6446   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6447   UINT uView = lStyle & LVS_TYPEMASK;
6448   BOOL bResult = FALSE;
6449
6450   TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd, lpptOrigin);
6451
6452   if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6453   {
6454     SCROLLINFO scrollInfo;
6455     ZeroMemory(lpptOrigin, sizeof(POINT));
6456     ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
6457     scrollInfo.cbSize = sizeof(SCROLLINFO);
6458
6459     if (lStyle & WS_HSCROLL)
6460     {
6461       scrollInfo.fMask = SIF_POS;
6462       if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
6463         lpptOrigin->x = -scrollInfo.nPos;
6464     }
6465
6466     if (lStyle & WS_VSCROLL)
6467     {
6468       scrollInfo.fMask = SIF_POS;
6469       if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
6470         lpptOrigin->y = -scrollInfo.nPos;
6471     }
6472
6473     bResult = TRUE;
6474
6475     TRACE("(hwnd=%x, pt=(%ld,%ld))\n", hwnd, lpptOrigin->x, lpptOrigin->y);
6476
6477   }
6478
6479   return bResult;
6480 }
6481
6482 /***
6483  * DESCRIPTION:
6484  * Retrieves the number of items that are marked as selected.
6485  *
6486  * PARAMETER(S):
6487  * [I] HWND : window handle
6488  *
6489  * RETURN:
6490  * Number of items selected.
6491  */
6492 static LRESULT LISTVIEW_GetSelectedCount(HWND hwnd)
6493 {
6494 /* REDO THIS */
6495   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6496   INT nSelectedCount = 0;
6497   INT i;
6498
6499   for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
6500   {
6501     if (ListView_GetItemState(hwnd, i, LVIS_SELECTED) & LVIS_SELECTED)
6502       nSelectedCount++;
6503   }
6504
6505   return nSelectedCount;
6506 }
6507
6508 /***
6509  * DESCRIPTION:
6510  * Retrieves item index that marks the start of a multiple selection.
6511  *
6512  * PARAMETER(S):
6513  * [I] HWND : window handle
6514  *
6515  * RETURN:
6516  * Index number or -1 if there is no selection mark.
6517  */
6518 static LRESULT LISTVIEW_GetSelectionMark(HWND hwnd)
6519 {
6520   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6521
6522   return infoPtr->nSelectionMark;
6523 }
6524
6525
6526 /***
6527  * DESCRIPTION:
6528  * Retrieves the width of a string.
6529  *
6530  * PARAMETER(S):
6531  * [I] hwnd : window handle
6532  * [I] lpszText : text string to process
6533  * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6534  *
6535  * RETURN:
6536  *   SUCCESS : string width (in pixels)
6537  *   FAILURE : zero
6538  */
6539 static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW)
6540 {
6541   if (is_textT(lpszText, isW))
6542   {
6543     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6544     HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6545     HDC hdc = GetDC(hwnd);
6546     HFONT hOldFont = SelectObject(hdc, hFont);
6547     SIZE stringSize;
6548     ZeroMemory(&stringSize, sizeof(SIZE));
6549     if (isW)
6550       GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6551     else
6552       GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6553     SelectObject(hdc, hOldFont);
6554     ReleaseDC(hwnd, hdc);
6555     return stringSize.cx;
6556   }
6557   return 0;
6558 }
6559
6560 /***
6561  * DESCRIPTION:
6562  * Retrieves the text backgound color.
6563  *
6564  * PARAMETER(S):
6565  * [I] HWND : window handle
6566  *
6567  * RETURN:
6568  * COLORREF associated with the the background.
6569  */
6570 static LRESULT LISTVIEW_GetTextBkColor(HWND hwnd)
6571 {
6572   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6573
6574   return infoPtr->clrTextBk;
6575 }
6576
6577 /***
6578  * DESCRIPTION:
6579  * Retrieves the text color.
6580  *
6581  * PARAMETER(S):
6582  * [I] HWND : window handle
6583  *
6584  * RETURN:
6585  * COLORREF associated with the text.
6586  */
6587 static LRESULT LISTVIEW_GetTextColor(HWND hwnd)
6588 {
6589   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6590
6591   return infoPtr->clrText;
6592 }
6593
6594 /***
6595  * DESCRIPTION:
6596  * Determines item if a hit or closest if not
6597  *
6598  * PARAMETER(S):
6599  * [I] HWND : window handle
6600  * [IO] LPLV_INTHIT : hit test information
6601  * [I] subitem : fill out iSubItem.
6602  *
6603  * RETURN:
6604  *   SUCCESS : item index of hit
6605  *   FAILURE : -1
6606  */
6607 static INT LISTVIEW_SuperHitTestItem(HWND hwnd, LPLV_INTHIT lpInt, BOOL subitem)
6608 {
6609   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6610   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6611   UINT uView = lStyle & LVS_TYPEMASK;
6612   INT i,j,topindex,bottomindex;
6613   RECT rcItem,rcSubItem;
6614   DWORD xterm, yterm, dist;
6615
6616   TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpInt->ht.pt.x, lpInt->ht.pt.y);
6617
6618   topindex = LISTVIEW_GetTopIndex(hwnd);
6619   if (uView == LVS_REPORT)
6620   {
6621     bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;
6622     bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
6623   }
6624   else
6625   {
6626     bottomindex = GETITEMCOUNT(infoPtr);
6627   }
6628
6629   lpInt->distance = 0x7fffffff;
6630   lpInt->iDistItem = -1;
6631
6632   for (i = topindex; i < bottomindex; i++)
6633   {
6634     rcItem.left = LVIR_BOUNDS;
6635     if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6636     {
6637       if (PtInRect(&rcItem, lpInt->ht.pt))
6638       {
6639         rcSubItem = rcItem;
6640         rcItem.left = LVIR_ICON;
6641         if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6642         {
6643           if (PtInRect(&rcItem, lpInt->ht.pt))
6644           {
6645             lpInt->ht.flags = LVHT_ONITEMICON;
6646             lpInt->ht.iItem = i;
6647             goto set_subitem;
6648           }
6649         }
6650
6651         rcItem.left = LVIR_LABEL;
6652         if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6653         {
6654           if (PtInRect(&rcItem, lpInt->ht.pt))
6655           {
6656             lpInt->ht.flags = LVHT_ONITEMLABEL;
6657             lpInt->ht.iItem = i;
6658             goto set_subitem;
6659           }
6660         }
6661
6662         lpInt->ht.flags = LVHT_ONITEMSTATEICON;
6663         lpInt->ht.iItem = i;
6664        set_subitem:
6665         if (subitem)
6666         {
6667           lpInt->ht.iSubItem = 0;
6668           rcSubItem.right = rcSubItem.left;
6669           for (j = 0; j < infoPtr->nColumnCount; j++)
6670           {
6671             rcSubItem.left = rcSubItem.right;
6672             rcSubItem.right += LISTVIEW_GetColumnWidth(hwnd, j);
6673             if (PtInRect(&rcSubItem, lpInt->ht.pt))
6674             {
6675               lpInt->ht.iSubItem = j;
6676               break;
6677             }
6678           }
6679         }
6680         return i;
6681       }
6682       else
6683       {
6684         /*
6685          * Now compute distance from point to center of boundary
6686          * box. Since we are only interested in the relative
6687          * distance, we can skip the nasty square root operation
6688          */
6689         xterm = rcItem.left + (rcItem.right - rcItem.left)/2 - lpInt->ht.pt.x;
6690         yterm = rcItem.top + (rcItem.bottom - rcItem.top)/2 - lpInt->ht.pt.y;
6691         dist = xterm * xterm + yterm * yterm;
6692         if (dist < lpInt->distance)
6693         {
6694           lpInt->distance = dist;
6695           lpInt->iDistItem = i;
6696         }
6697       }
6698     }
6699   }
6700
6701   lpInt->ht.flags = LVHT_NOWHERE;
6702   TRACE("no hit, closest item %d, distance %ld\n", lpInt->iDistItem, lpInt->distance);
6703
6704   return -1;
6705 }
6706
6707  /***
6708   * DESCRIPTION:
6709   * Determines which section of the item was selected (if any).
6710   *
6711   * PARAMETER(S):
6712   * [I] HWND : window handle
6713   * [IO] LPLVHITTESTINFO : hit test information
6714   * [I] subitem : fill out iSubItem.
6715   *
6716   * RETURN:
6717   *   SUCCESS : item index
6718   *   FAILURE : -1
6719   */
6720 static INT LISTVIEW_HitTestItem(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo, BOOL subitem)
6721 {
6722   INT ret;
6723   LV_INTHIT lv_inthit;
6724
6725   TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
6726         lpHitTestInfo->pt.y);
6727
6728   memcpy(&lv_inthit, lpHitTestInfo, sizeof(LVHITTESTINFO));
6729   ret = LISTVIEW_SuperHitTestItem(hwnd, &lv_inthit, subitem);
6730   memcpy(lpHitTestInfo, &lv_inthit, sizeof(LVHITTESTINFO));
6731   return ret;
6732 }
6733
6734 /***
6735  * DESCRIPTION:
6736  * Determines which listview item is located at the specified position.
6737  *
6738  * PARAMETER(S):
6739  * [I] HWND : window handle
6740  * [IO} LPLVHITTESTINFO : hit test information
6741  *
6742  * RETURN:
6743  *   SUCCESS : item index
6744  *   FAILURE : -1
6745  */
6746 static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
6747 {
6748   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6749   INT nItem = -1;
6750
6751   lpHitTestInfo->flags = 0;
6752
6753   if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6754     lpHitTestInfo->flags = LVHT_TOLEFT;
6755   else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6756     lpHitTestInfo->flags = LVHT_TORIGHT;
6757   if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6758     lpHitTestInfo->flags |= LVHT_ABOVE;
6759   else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6760     lpHitTestInfo->flags |= LVHT_BELOW;
6761
6762   if (lpHitTestInfo->flags == 0)
6763   {
6764     /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for
6765      * an app might pass only a structure with space up to iItem!
6766      * (MS Office 97 does that for instance in the file open dialog)
6767      */
6768     nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, FALSE);
6769   }
6770
6771   return nItem;
6772 }
6773
6774 /***
6775  * DESCRIPTION:
6776  * Determines which listview subitem is located at the specified position.
6777  *
6778  * PARAMETER(S):
6779  * [I] HWND : window handle
6780  * [IO} LPLVHITTESTINFO : hit test information
6781  *
6782  * RETURN:
6783  *   SUCCESS : item index
6784  *   FAILURE : -1
6785  */
6786 static LRESULT LISTVIEW_SubItemHitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
6787 {
6788   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6789   INT nItem = -1;
6790
6791   lpHitTestInfo->flags = 0;
6792
6793   if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6794     lpHitTestInfo->flags = LVHT_TOLEFT;
6795   else if (infoPtr->rcList.right < lpHitTestInfo->pt.x)
6796     lpHitTestInfo->flags = LVHT_TORIGHT;
6797   if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6798     lpHitTestInfo->flags |= LVHT_ABOVE;
6799   else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y)
6800     lpHitTestInfo->flags |= LVHT_BELOW;
6801
6802   if (lpHitTestInfo->flags == 0)
6803     nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, TRUE);
6804
6805   return nItem;
6806 }
6807
6808 /***
6809  * DESCRIPTION:
6810  * Inserts a new column.
6811  *
6812  * PARAMETER(S):
6813  * [I] HWND : window handle
6814  * [I] INT : column index
6815  * [I] LPLVCOLUMNW : column information
6816  *
6817  * RETURN:
6818  *   SUCCESS : new column index
6819  *   FAILURE : -1
6820  */
6821 static LRESULT LISTVIEW_InsertColumnT(HWND hwnd, INT nColumn,
6822                                       LPLVCOLUMNW lpColumn, BOOL isW)
6823 {
6824   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6825   INT nNewColumn = -1;
6826   HDITEMW hdi;
6827
6828   TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn, lpColumn);
6829
6830   if (lpColumn != NULL)
6831   {
6832     /* initialize memory */
6833     ZeroMemory(&hdi, sizeof(hdi));
6834
6835     if (lpColumn->mask & LVCF_FMT)
6836     {
6837       /* format member is valid */
6838       hdi.mask |= HDI_FORMAT;
6839
6840       /* set text alignment (leftmost column must be left-aligned) */
6841       if (nColumn == 0)
6842       {
6843         hdi.fmt |= HDF_LEFT;
6844       }
6845       else
6846       {
6847         if (lpColumn->fmt & LVCFMT_LEFT)
6848         {
6849           hdi.fmt |= HDF_LEFT;
6850         }
6851         else if (lpColumn->fmt & LVCFMT_RIGHT)
6852         {
6853           hdi.fmt |= HDF_RIGHT;
6854         }
6855         else if (lpColumn->fmt & LVCFMT_CENTER)
6856         {
6857           hdi.fmt |= HDF_CENTER;
6858         }
6859       }
6860
6861       if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6862       {
6863         hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6864         /* ??? */
6865       }
6866
6867       if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6868       {
6869         /* ??? */
6870       }
6871
6872       if (lpColumn->fmt & LVCFMT_IMAGE)
6873       {
6874         hdi.fmt |= HDF_IMAGE;
6875         hdi.iImage = I_IMAGECALLBACK;
6876       }
6877     }
6878
6879     if (lpColumn->mask & LVCF_WIDTH)
6880     {
6881       hdi.mask |= HDI_WIDTH;
6882       if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6883       {
6884         /* make it fill the remainder of the controls width */
6885         HDITEMW hdit;
6886         RECT rcHeader;
6887         INT item_index;
6888
6889         ZeroMemory(&hdit, sizeof(hdit));
6890
6891         /* get the width of every item except the current one */
6892         hdit.mask = HDI_WIDTH;
6893         hdi.cxy = 0;
6894
6895         for(item_index = 0; item_index < (nColumn - 1); item_index++) {
6896           Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit));
6897           hdi.cxy+=hdit.cxy;
6898         }
6899
6900         /* retrieve the layout of the header */
6901         GetClientRect(hwnd, &rcHeader);
6902 /*        GetWindowRect(infoPtr->hwndHeader, &rcHeader);*/
6903         TRACE("start cxy=%d left=%d right=%d\n", hdi.cxy, rcHeader.left, rcHeader.right);
6904
6905         hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
6906       }
6907       else
6908         hdi.cxy = lpColumn->cx;
6909     }
6910
6911     if (lpColumn->mask & LVCF_TEXT)
6912     {
6913       hdi.mask |= HDI_TEXT | HDI_FORMAT;
6914       hdi.pszText = lpColumn->pszText;
6915       hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6916       hdi.fmt |= HDF_STRING;
6917     }
6918
6919     if (lpColumn->mask & LVCF_IMAGE)
6920     {
6921       hdi.mask |= HDI_IMAGE;
6922       hdi.iImage = lpColumn->iImage;
6923     }
6924
6925     if (lpColumn->mask & LVCF_ORDER)
6926     {
6927       hdi.mask |= HDI_ORDER;
6928       hdi.iOrder = lpColumn->iOrder;
6929     }
6930
6931     /* insert item in header control */
6932     nNewColumn = SendMessageW(infoPtr->hwndHeader, HDM_INSERTITEMT(isW),
6933                              (WPARAM)nColumn, (LPARAM)&hdi);
6934
6935     /* Need to reset the item width when inserting a new column */
6936     infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6937
6938     LISTVIEW_UpdateScroll(hwnd);
6939     InvalidateRect(hwnd, NULL, FALSE);
6940   }
6941
6942   return nNewColumn;
6943 }
6944
6945 /* LISTVIEW_InsertCompare:  callback routine for comparing pszText members of the LV_ITEMS
6946    in a LISTVIEW on insert.  Passed to DPA_Sort in LISTVIEW_InsertItem.
6947    This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6948    and not during the processing of a LVM_SORTITEMS message. Applications should provide
6949    their own sort proc. when sending LVM_SORTITEMS.
6950 */
6951 /* Platform SDK:
6952     (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6953         if:
6954           LVS_SORTXXX must be specified,
6955           LVS_OWNERDRAW is not set,
6956           <item>.pszText is not LPSTR_TEXTCALLBACK.
6957
6958     (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6959     are sorted based on item text..."
6960 */
6961 static INT WINAPI LISTVIEW_InsertCompare(  LPVOID first, LPVOID second,  LPARAM lParam)
6962 {
6963   LONG lStyle = GetWindowLongW((HWND) lParam, GWL_STYLE);
6964   LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6965   LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6966   INT  cmpv = lstrcmpW( lv_first->pszText, lv_second->pszText );
6967   /* if we're sorting descending, negate the return value */
6968   return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6969 }
6970
6971 /***
6972  * nESCRIPTION:
6973  * Inserts a new item in the listview control.
6974  *
6975  * PARAMETER(S):
6976  * [I] HWND : window handle
6977  * [I] LPLVITEMW : item information
6978  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6979  *
6980  * RETURN:
6981  *   SUCCESS : new item index
6982  *   FAILURE : -1
6983  */
6984 static LRESULT LISTVIEW_InsertItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
6985 {
6986   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6987   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6988   UINT uView = lStyle & LVS_TYPEMASK;
6989   INT nItem = -1;
6990   HDPA hdpaSubItems;
6991   INT nItemWidth = 0;
6992   LISTVIEW_ITEM *lpItem = NULL;
6993
6994   TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n",
6995         hwnd, debuglvitem_t(lpLVItem, isW), isW);
6996
6997   if (lStyle & LVS_OWNERDATA)
6998   {
6999     nItem = infoPtr->hdpaItems->nItemCount;
7000     infoPtr->hdpaItems->nItemCount ++;
7001     return nItem;
7002   }
7003
7004   if (lpLVItem != NULL)
7005   {
7006     /* make sure it's not a subitem; cannot insert a subitem */
7007     if (lpLVItem->iSubItem == 0)
7008     {
7009       if ( (lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
7010       {
7011         ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
7012         if (LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW))
7013         {
7014           /* insert item in listview control data structure */
7015           if ( (hdpaSubItems = DPA_Create(8)) )
7016           {
7017             if ( (nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem)) != -1)
7018             {
7019               if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
7020                       && !(lStyle & LVS_OWNERDRAWFIXED)
7021                       && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText) )
7022               {
7023                 /* Insert the item in the proper sort order based on the pszText
7024                   member. See comments for LISTVIEW_InsertCompare() for greater detail */
7025                   nItem = DPA_InsertPtr( infoPtr->hdpaItems,
7026                           GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems );
7027                   DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, hwnd );
7028                   nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
7029               }
7030               else
7031               {
7032                 nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem,
7033                                     hdpaSubItems);
7034               }
7035               if (nItem != -1)
7036               {
7037                 NMLISTVIEW nmlv;
7038
7039                 LISTVIEW_ShiftIndices(hwnd,nItem,1);
7040
7041                 /* manage item focus */
7042                 if (lpLVItem->mask & LVIF_STATE)
7043                 {
7044                   lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
7045                   if (lpLVItem->stateMask & LVIS_SELECTED)
7046                     LISTVIEW_SetSelection(hwnd, nItem);
7047                   else if (lpLVItem->stateMask & LVIS_FOCUSED)
7048                     LISTVIEW_SetItemFocus(hwnd, nItem);
7049                 }
7050
7051                 /* send LVN_INSERTITEM notification */
7052                 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7053                 nmlv.iItem = nItem;
7054                 nmlv.lParam = lpItem->lParam;
7055                 listview_notify(hwnd, LVN_INSERTITEM, &nmlv);
7056
7057                 if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
7058                 {
7059                   nItemWidth = LISTVIEW_CalculateWidth(hwnd, lpLVItem->iItem);
7060                   if (nItemWidth > infoPtr->nItemWidth)
7061                     infoPtr->nItemWidth = nItemWidth;
7062                 }
7063
7064                 /* align items (set position of each item) */
7065                 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
7066                 {
7067                   if (lStyle & LVS_ALIGNLEFT)
7068                     LISTVIEW_AlignLeft(hwnd);
7069                   else
7070                     LISTVIEW_AlignTop(hwnd);
7071                 }
7072
7073                 LISTVIEW_UpdateScroll(hwnd);
7074                 /* refresh client area */
7075                 InvalidateRect(hwnd, NULL, FALSE);
7076               }
7077             }
7078           }
7079         }
7080       }
7081     }
7082   }
7083
7084   /* free memory if unsuccessful */
7085   if ((nItem == -1) && (lpItem != NULL))
7086     COMCTL32_Free(lpItem);
7087
7088   return nItem;
7089 }
7090
7091 /***
7092  * DESCRIPTION:
7093  * Redraws a range of items.
7094  *
7095  * PARAMETER(S):
7096  * [I] HWND : window handle
7097  * [I] INT : first item
7098  * [I] INT : last item
7099  *
7100  * RETURN:
7101  *   SUCCESS : TRUE
7102  *   FAILURE : FALSE
7103  */
7104 static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast)
7105 {
7106   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7107   BOOL bResult = FALSE;
7108   RECT rcItem;
7109   INT i;
7110
7111   if (nFirst <= nLast)
7112   {
7113     if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
7114     {
7115       if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
7116       {
7117         for (i = nFirst; i <= nLast; i++)
7118         {
7119           rcItem.left = LVIR_BOUNDS;
7120           LISTVIEW_GetItemRect(hwnd, i, &rcItem);
7121           InvalidateRect(hwnd, &rcItem, TRUE);
7122         }
7123       }
7124     }
7125   }
7126
7127   return bResult;
7128 }
7129
7130 /***
7131  * DESCRIPTION:
7132  * Scroll the content of a listview.
7133  *
7134  * PARAMETER(S):
7135  * [I] HWND : window handle
7136  * [I] INT : horizontal scroll amount in pixels
7137  * [I] INT : vertical scroll amount in pixels
7138  *
7139  * RETURN:
7140  *   SUCCESS : TRUE
7141  *   FAILURE : FALSE
7142  *
7143  * COMMENTS:
7144  *  If the control is in report mode (LVS_REPORT) the control can
7145  *  be scrolled only in line increments. "dy" will be rounded to the
7146  *  nearest number of pixels that are a whole line. Ex: if line height
7147  *  is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
7148  *  is passed the the scroll will be 0.  (per MSDN 7/2002)
7149  *
7150  *  For:  (per experimentaion with native control and CSpy ListView)
7151  *     LVS_ICON       dy=1 = 1 pixel  (vertical only)
7152  *                    dx ignored
7153  *     LVS_SMALLICON  dy=1 = 1 pixel  (vertical only)
7154  *                    dx ignored
7155  *     LVS_LIST       dx=1 = 1 column (horizontal only)
7156  *                           but will only scroll 1 column per message
7157  *                           no matter what the value.
7158  *                    dy must be 0 or FALSE returned.
7159  *     LVS_REPORT     dx=1 = 1 pixel
7160  *                    dy=  see above
7161  *
7162  */
7163 static LRESULT LISTVIEW_Scroll(HWND hwnd, INT dx, INT dy)
7164 {
7165   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7166   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7167   UINT uView =  lStyle & LVS_TYPEMASK;
7168   INT rows, mode, i;
7169
7170   if (uView == LVS_REPORT)
7171   {
7172       rows = (abs(dy) + infoPtr->nItemHeight/2) / infoPtr->nItemHeight;
7173       if (rows != 0)
7174       {
7175           mode = (dy>0) ? SB_INTERNAL_DOWN : SB_INTERNAL_UP;
7176           for ( i=0; i<rows; i++)
7177               LISTVIEW_VScroll(hwnd, mode, 0, hwnd);
7178       }
7179
7180       if (dx != 0)
7181       {
7182           mode = (dx>0) ? SB_INTERNAL_RIGHT : SB_INTERNAL_LEFT;
7183           for ( i=0; i<abs(dx); i++)
7184               LISTVIEW_HScroll(hwnd, mode, 0, hwnd);
7185       }
7186       return TRUE;
7187   }
7188   else if (uView == LVS_ICON)
7189   {
7190       INT mode, i;
7191
7192       mode = (dy>0) ? SB_INTERNAL_DOWN : SB_INTERNAL_UP;
7193       for(i=0; i<abs(dy); i++)
7194           LISTVIEW_VScroll(hwnd, mode, 0, hwnd);
7195       return TRUE;
7196   }
7197   else if (uView == LVS_SMALLICON)
7198   {
7199       INT mode, i;
7200
7201       mode = (dy>0) ? SB_INTERNAL_DOWN : SB_INTERNAL_UP;
7202       for(i=0; i<abs(dy); i++)
7203           LISTVIEW_VScroll(hwnd, mode, 0, hwnd);
7204       return TRUE;
7205   }
7206   else if (uView == LVS_LIST)
7207   {
7208       if (dy != 0) return FALSE;
7209       if (dx == 0) return TRUE;
7210       mode = (dx>0) ? SB_INTERNAL_RIGHT : SB_INTERNAL_LEFT;
7211       LISTVIEW_HScroll(hwnd, mode, 0, hwnd);
7212       return TRUE;
7213   }
7214   return FALSE;
7215 }
7216
7217 /***
7218  * DESCRIPTION:
7219  * Sets the background color.
7220  *
7221  * PARAMETER(S):
7222  * [I] HWND : window handle
7223  * [I] COLORREF : background color
7224  *
7225  * RETURN:
7226  *   SUCCESS : TRUE
7227  *   FAILURE : FALSE
7228  */
7229 static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk)
7230 {
7231   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7232
7233   if(infoPtr->clrBk!=clrBk){
7234     infoPtr->clrBk = clrBk;
7235     InvalidateRect(hwnd, NULL, TRUE);
7236   }
7237
7238   return TRUE;
7239 }
7240
7241 /* LISTVIEW_SetBkImage */
7242
7243 /***
7244  * DESCRIPTION:
7245  * Sets the callback mask. This mask will be used when the parent
7246  * window stores state information (some or all).
7247  *
7248  * PARAMETER(S):
7249  * [I] HWND : window handle
7250  * [I] UINT : state mask
7251  *
7252  * RETURN:
7253  *   SUCCESS : TRUE
7254  *   FAILURE : FALSE
7255  */
7256 static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask)
7257 {
7258   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7259
7260   infoPtr->uCallbackMask = uMask;
7261
7262   return TRUE;
7263 }
7264
7265 /***
7266  * DESCRIPTION:
7267  * Sets the attributes of a header item.
7268  *
7269  * PARAMETER(S):
7270  * [I] HWND : window handle
7271  * [I] INT : column index
7272  * [I] LPLVCOLUMNW : column attributes
7273  * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW,
7274  *          otherwise it is in fact a LPLVCOLUMNA
7275  *
7276  * RETURN:
7277  *   SUCCESS : TRUE
7278  *   FAILURE : FALSE
7279  */
7280 static LRESULT LISTVIEW_SetColumnT(HWND hwnd, INT nColumn,
7281                                    LPLVCOLUMNW lpColumn, BOOL isW)
7282 {
7283   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7284   BOOL bResult = FALSE;
7285   HDITEMW hdi, hdiget;
7286
7287   if ((lpColumn != NULL) && (nColumn >= 0) &&
7288       (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
7289   {
7290     /* initialize memory */
7291     ZeroMemory(&hdi, sizeof(hdi));
7292
7293     if (lpColumn->mask & LVCF_FMT)
7294     {
7295       /* format member is valid */
7296       hdi.mask |= HDI_FORMAT;
7297
7298       /* get current format first */
7299       hdiget.mask = HDI_FORMAT;
7300       if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
7301               /* preserve HDF_STRING if present */
7302               hdi.fmt = hdiget.fmt & HDF_STRING;
7303
7304       /* set text alignment (leftmost column must be left-aligned) */
7305       if (nColumn == 0)
7306       {
7307         hdi.fmt |= HDF_LEFT;
7308       }
7309       else
7310       {
7311         if (lpColumn->fmt & LVCFMT_LEFT)
7312           hdi.fmt |= HDF_LEFT;
7313         else if (lpColumn->fmt & LVCFMT_RIGHT)
7314           hdi.fmt |= HDF_RIGHT;
7315         else if (lpColumn->fmt & LVCFMT_CENTER)
7316           hdi.fmt |= HDF_CENTER;
7317       }
7318
7319       if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
7320         hdi.fmt |= HDF_BITMAP_ON_RIGHT;
7321
7322       if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
7323         hdi.fmt |= HDF_IMAGE;
7324
7325       if (lpColumn->fmt & LVCFMT_IMAGE)
7326       {
7327         hdi.fmt |= HDF_IMAGE;
7328         hdi.iImage = I_IMAGECALLBACK;
7329       }
7330     }
7331
7332     if (lpColumn->mask & LVCF_WIDTH)
7333     {
7334       hdi.mask |= HDI_WIDTH;
7335       hdi.cxy = lpColumn->cx;
7336     }
7337
7338     if (lpColumn->mask & LVCF_TEXT)
7339     {
7340       hdi.mask |= HDI_TEXT | HDI_FORMAT;
7341       hdi.pszText = lpColumn->pszText;
7342       hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
7343       hdi.fmt |= HDF_STRING;
7344     }
7345
7346     if (lpColumn->mask & LVCF_IMAGE)
7347     {
7348       hdi.mask |= HDI_IMAGE;
7349       hdi.iImage = lpColumn->iImage;
7350     }
7351
7352     if (lpColumn->mask & LVCF_ORDER)
7353     {
7354       hdi.mask |= HDI_ORDER;
7355       hdi.iOrder = lpColumn->iOrder;
7356     }
7357
7358     /* set header item attributes */
7359     if (isW)
7360       bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7361     else
7362       bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
7363   }
7364
7365   return bResult;
7366 }
7367
7368 /***
7369  * DESCRIPTION:
7370  * Sets the column order array
7371  *
7372  * PARAMETERS:
7373  * [I] HWND : window handle
7374  * [I] INT : number of elements in column order array
7375  * [I] INT : pointer to column order array
7376  *
7377  * RETURN:
7378  *   SUCCESS : TRUE
7379  *   FAILURE : FALSE
7380  */
7381 static LRESULT LISTVIEW_SetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
7382 {
7383   /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
7384
7385   FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7386
7387   if (!lpiArray)
7388     return FALSE;
7389
7390   return TRUE;
7391 }
7392
7393
7394 /***
7395  * DESCRIPTION:
7396  * Sets the width of a column
7397  *
7398  * PARAMETERS:
7399  * [I] HWND : window handle
7400  * [I] INT : column index
7401  * [I] INT : column width
7402  *
7403  * RETURN:
7404  *   SUCCESS : TRUE
7405  *   FAILURE : FALSE
7406  */
7407 static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx)
7408 {
7409     LISTVIEW_INFO *infoPtr;
7410     HDITEMW hdi;
7411     LRESULT lret;
7412     LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7413     UINT uView = lStyle & LVS_TYPEMASK;
7414     HDC hdc;
7415     HFONT header_font;
7416     HFONT old_font;
7417     SIZE size;
7418     WCHAR text_buffer[DISP_TEXT_SIZE];
7419     INT header_item_count;
7420     INT item_index;
7421     INT nLabelWidth;
7422     RECT rcHeader;
7423     LVITEMW lvItem;
7424     WCHAR szDispText[DISP_TEXT_SIZE];
7425
7426     /* make sure we can get the listview info */
7427     if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
7428       return (FALSE);
7429
7430     if (!infoPtr->hwndHeader) /* make sure we have a header */
7431       return (FALSE);
7432
7433     /* set column width only if in report or list mode */
7434     if ((uView != LVS_REPORT) && (uView != LVS_LIST))
7435       return (FALSE);
7436
7437     TRACE("(hwnd=%x, iCol=%d, cx=%d\n", hwnd, iCol, cx);
7438
7439     /* take care of invalid cx values */
7440     if((uView == LVS_REPORT) && (cx < -2))
7441       cx = LVSCW_AUTOSIZE;
7442     else if (uView == LVS_LIST && (cx < 1))
7443       return FALSE;
7444
7445     /* resize all columns if in LVS_LIST mode */
7446     if(uView == LVS_LIST) {
7447       infoPtr->nItemWidth = cx;
7448       InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7449       return TRUE;
7450     }
7451
7452     /* autosize based on listview items width */
7453     if(cx == LVSCW_AUTOSIZE)
7454     {
7455       /* set the width of the column to the width of the widest item */
7456       if (iCol == 0 || uView == LVS_LIST)
7457       {
7458         cx = 0;
7459         for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7460         {
7461           nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, item_index);
7462           cx = (nLabelWidth>cx)?nLabelWidth:cx;
7463         }
7464         if (infoPtr->himlSmall)
7465           cx += infoPtr->iconSize.cx + IMAGE_PADDING;
7466       }
7467       else
7468       {
7469         ZeroMemory(&lvItem, sizeof(lvItem));
7470         lvItem.iSubItem = iCol;
7471         lvItem.mask = LVIF_TEXT;
7472         lvItem.cchTextMax = DISP_TEXT_SIZE;
7473         lvItem.pszText = szDispText;
7474         *lvItem.pszText = '\0';
7475         cx = 0;
7476         for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7477         {
7478           lvItem.iItem = item_index;
7479           LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, TRUE);
7480           nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
7481           cx = (nLabelWidth>cx)?nLabelWidth:cx;
7482         }
7483       }
7484       cx += TRAILING_PADDING;
7485     } /* autosize based on listview header width */
7486     else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7487     {
7488       header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
7489
7490       /* if iCol is the last column make it fill the remainder of the controls width */
7491       if(iCol == (header_item_count - 1)) {
7492         /* get the width of every item except the current one */
7493         hdi.mask = HDI_WIDTH;
7494         cx = 0;
7495
7496         for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
7497           Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
7498           cx+=hdi.cxy;
7499         }
7500
7501         /* retrieve the layout of the header */
7502         GetWindowRect(infoPtr->hwndHeader, &rcHeader);
7503
7504         cx = (rcHeader.right - rcHeader.left) - cx;
7505       }
7506       else
7507       {
7508         /* Despite what the MS docs say, if this is not the last
7509            column, then MS resizes the column to the width of the
7510            largest text string in the column, including headers
7511            and items. This is different from LVSCW_AUTOSIZE in that
7512            LVSCW_AUTOSIZE ignores the header string length.
7513            */
7514
7515         /* retrieve header font */
7516         header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
7517
7518         /* retrieve header text */
7519         hdi.mask = HDI_TEXT;
7520         hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
7521         hdi.pszText = text_buffer;
7522
7523         Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
7524
7525         /* determine the width of the text in the header */
7526         hdc = GetDC(hwnd);
7527         old_font = SelectObject(hdc, header_font); /* select the font into hdc */
7528
7529         GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
7530
7531         SelectObject(hdc, old_font); /* restore the old font */
7532         ReleaseDC(hwnd, hdc);
7533
7534         ZeroMemory(&lvItem, sizeof(lvItem));
7535         lvItem.iSubItem = iCol;
7536         lvItem.mask = LVIF_TEXT;
7537         lvItem.cchTextMax = DISP_TEXT_SIZE;
7538         lvItem.pszText = szDispText;
7539         *lvItem.pszText = '\0';
7540         cx = size.cx;
7541         for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7542         {
7543           lvItem.iItem = item_index;
7544           LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, TRUE);
7545           nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
7546           nLabelWidth += TRAILING_PADDING;
7547           /* While it is possible for subitems to have icons, even MS messes
7548              up the positioning, so I suspect no applications actually use
7549              them. */
7550           if (item_index == 0 && infoPtr->himlSmall)
7551             nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
7552           cx = (nLabelWidth>cx)?nLabelWidth:cx;
7553         }
7554       }
7555   }
7556
7557   /* call header to update the column change */
7558   hdi.mask = HDI_WIDTH;
7559
7560   hdi.cxy = cx;
7561   lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
7562
7563   InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7564
7565   return lret;
7566 }
7567
7568 /***
7569  * DESCRIPTION:
7570  * Sets the extended listview style.
7571  *
7572  * PARAMETERS:
7573  * [I] HWND  : window handle
7574  * [I] DWORD : mask
7575  * [I] DWORD : style
7576  *
7577  * RETURN:
7578  *   SUCCESS : previous style
7579  *   FAILURE : 0
7580  */
7581 static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle)
7582 {
7583   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7584   DWORD dwOldStyle = infoPtr->dwExStyle;
7585
7586   /* set new style */
7587   if (dwMask)
7588     infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
7589   else
7590     infoPtr->dwExStyle = dwStyle;
7591
7592   return dwOldStyle;
7593 }
7594
7595 /* LISTVIEW_SetHotCursor */
7596
7597 /***
7598  * DESCRIPTION:
7599  * Sets the hot item index.
7600  *
7601  * PARAMETERS:
7602  * [I] HWND  : window handle
7603  * [I] INT   : index
7604  *
7605  * RETURN:
7606  *   SUCCESS : previous hot item index
7607  *   FAILURE : -1 (no hot item)
7608  */
7609 static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex)
7610 {
7611   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7612   INT iOldIndex = infoPtr->nHotItem;
7613
7614   /* set new style */
7615   infoPtr->nHotItem = iIndex;
7616
7617   return iOldIndex;
7618 }
7619
7620 /***
7621  * DESCRIPTION:
7622  * Sets the amount of time the cursor must hover over an item before it is selected.
7623  *
7624  * PARAMETER(S):
7625  * [I] HWND : window handle
7626  * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
7627  *
7628  * RETURN:
7629  * Returns the previous hover time
7630  */
7631 static LRESULT LISTVIEW_SetHoverTime(HWND hwnd, DWORD dwHoverTime)
7632 {
7633   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7634   DWORD oldHoverTime = infoPtr->dwHoverTime;
7635
7636   infoPtr->dwHoverTime = dwHoverTime;
7637
7638   return oldHoverTime;
7639 }
7640
7641 /***
7642  * DESCRIPTION:
7643  * Sets spacing for icons of LVS_ICON style.
7644  *
7645  * PARAMETER(S):
7646  * [I] HWND : window handle
7647  * [I] DWORD : MAKELONG(cx, cy)
7648  *
7649  * RETURN:
7650  *   MAKELONG(oldcx, oldcy)
7651  */
7652 static LRESULT LISTVIEW_SetIconSpacing(HWND hwnd, DWORD spacing)
7653 {
7654   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7655   INT cy = HIWORD(spacing);
7656   INT cx = LOWORD(spacing);
7657   DWORD oldspacing;
7658   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7659   UINT uView = lStyle & LVS_TYPEMASK;
7660
7661   oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7662   if (cx == -1) /* set to default */
7663     cx = GetSystemMetrics(SM_CXICONSPACING);
7664   if (cy == -1) /* set to default */
7665     cy = GetSystemMetrics(SM_CYICONSPACING);
7666
7667   if (cx)
7668     infoPtr->iconSpacing.cx = cx;
7669   else
7670   {  /* if 0 then compute width */
7671     if (uView == LVS_ICON)
7672        FIXME("width computation not yet done\n");
7673        /*
7674         * Should scan each item and determine max width of
7675         * icon or label, then make that the width
7676         */
7677      else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7678        infoPtr->iconSpacing.cx = LISTVIEW_GetItemWidth(hwnd);
7679   }
7680   if (cy)
7681       infoPtr->iconSpacing.cy = cy;
7682   else
7683   {  /* if 0 then compute height */
7684     if (uView == LVS_ICON)
7685        infoPtr->iconSpacing.cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight
7686                                   + ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_OFFSET;
7687         /* FIXME.  I don't think so; I think it is based on twice the ntmHeight */
7688     else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7689        infoPtr->iconSpacing.cy = LISTVIEW_GetItemHeight(hwnd);
7690   }
7691
7692   TRACE("old=(%d,%d), new=(%ld,%ld), iconSize=(%ld,%ld), ntmH=%d\n",
7693         LOWORD(oldspacing), HIWORD(oldspacing),
7694         infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy,
7695         infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7696         infoPtr->ntmHeight);
7697
7698   /* these depend on the iconSpacing */
7699   infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
7700   infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7701
7702   return oldspacing;
7703 }
7704
7705 /***
7706  * DESCRIPTION:
7707  * Sets image lists.
7708  *
7709  * PARAMETER(S):
7710  * [I] HWND : window handle
7711  * [I] INT : image list type
7712  * [I] HIMAGELIST : image list handle
7713  *
7714  * RETURN:
7715  *   SUCCESS : old image list
7716  *   FAILURE : NULL
7717  */
7718 static HIMAGELIST LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
7719 {
7720   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7721   HIMAGELIST himlOld = 0;
7722   INT oldHeight;
7723   UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
7724
7725   switch (nType)
7726   {
7727   case LVSIL_NORMAL:
7728     himlOld = infoPtr->himlNormal;
7729     infoPtr->himlNormal = himl;
7730     if(himl && (LVS_ICON == uView))
7731     {
7732         INT cx, cy;
7733         ImageList_GetIconSize(himl, &cx, &cy);
7734         TRACE("icon old size=(%ld,%ld), new size=(%d,%d)\n",
7735               infoPtr->iconSize.cx, infoPtr->iconSize.cy, cx, cy);
7736         infoPtr->iconSize.cx = cx;
7737         infoPtr->iconSize.cy = cy;
7738         LISTVIEW_SetIconSpacing(hwnd,0);
7739     }
7740     break;
7741
7742   case LVSIL_SMALL:
7743     himlOld = infoPtr->himlSmall;
7744     infoPtr->himlSmall = himl;
7745     break;
7746
7747   case LVSIL_STATE:
7748     himlOld = infoPtr->himlState;
7749     infoPtr->himlState = himl;
7750     ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7751     break;
7752   }
7753
7754   oldHeight = infoPtr->nItemHeight;
7755   infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7756   if (infoPtr->nItemHeight != oldHeight)
7757     LISTVIEW_UpdateScroll(hwnd);
7758
7759   return himlOld;
7760 }
7761
7762 /***
7763  * DESCRIPTION:
7764  * Preallocates memory (does *not* set the actual count of items !)
7765  *
7766  * PARAMETER(S):
7767  * [I] HWND : window handle
7768  * [I] INT   : item count (projected number of items to allocate)
7769  * [I] DWORD : update flags
7770  *
7771  * RETURN:
7772  *   SUCCESS : TRUE
7773  *   FAILURE : FALSE
7774  */
7775 static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags)
7776 {
7777   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7778
7779   TRACE("(hwnd=%x, nItems=%d, dwFlags=%lx)\n", hwnd, nItems, dwFlags);
7780
7781   if (GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA)
7782   {
7783       int precount,topvisible;
7784
7785       TRACE("LVS_OWNERDATA is set!\n");
7786       if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
7787         FIXME("flags %s %s not implemented\n",
7788               (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
7789               : "",
7790               (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
7791
7792       /*
7793        * Internally remove all the selections.
7794        */
7795       do
7796       {
7797         LISTVIEW_SELECTION *selection;
7798         selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
7799         if (selection)
7800             LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,
7801                                           selection->upper);
7802       }
7803       while (infoPtr->hdpaSelectionRanges->nItemCount>0);
7804
7805       precount = infoPtr->hdpaItems->nItemCount;
7806       topvisible = ListView_GetTopIndex(hwnd) +
7807                    LISTVIEW_GetCountPerColumn(hwnd) + 1;
7808
7809       infoPtr->hdpaItems->nItemCount = nItems;
7810
7811       infoPtr->nItemWidth = max(LISTVIEW_GetItemWidth(hwnd),
7812                                 DEFAULT_COLUMN_WIDTH);
7813
7814       LISTVIEW_UpdateSize(hwnd);
7815       LISTVIEW_UpdateScroll(hwnd);
7816
7817       if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible)
7818         InvalidateRect(hwnd, NULL, TRUE);
7819   }
7820   else
7821   {
7822     /* According to MSDN for non-LVS_OWNERDATA this is just
7823      * a performance issue. The control allocates its internal
7824      * data structures for the number of items specified. It
7825      * cuts down on the number of memory allocations. Therefore
7826      * we will just issue a WARN here
7827      */
7828      WARN("for non-ownerdata performance option not implemented.\n");
7829   }
7830
7831   return TRUE;
7832 }
7833
7834 /***
7835  * DESCRIPTION:
7836  * Sets the position of an item.
7837  *
7838  * PARAMETER(S):
7839  * [I] HWND : window handle
7840  * [I] INT : item index
7841  * [I] LONG : x coordinate
7842  * [I] LONG : y coordinate
7843  *
7844  * RETURN:
7845  *   SUCCESS : TRUE
7846  *   FAILURE : FALSE
7847  */
7848 static BOOL LISTVIEW_SetItemPosition(HWND hwnd, INT nItem,
7849                                      LONG nPosX, LONG nPosY)
7850 {
7851   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7852   UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7853   UINT uView = lStyle & LVS_TYPEMASK;
7854   LISTVIEW_ITEM *lpItem;
7855   HDPA hdpaSubItems;
7856   BOOL bResult = FALSE;
7857
7858   TRACE("(hwnd=%x, nItem=%d, X=%ld, Y=%ld)\n", hwnd, nItem, nPosX, nPosY);
7859
7860   if (lStyle & LVS_OWNERDATA)
7861     return FALSE;
7862
7863   if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
7864   {
7865     if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
7866     {
7867       if ( (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)) )
7868       {
7869         if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
7870         {
7871           POINT orig;
7872           bResult = TRUE;
7873           orig = lpItem->ptPosition;
7874           if ((nPosX == -1) && (nPosY == -1))
7875           {
7876             /* This point value seems to be an undocumented feature. The
7877              * best guess is that it means either at the origin, or at
7878              * the true beginning of the list. I will assume the origin.
7879              */
7880             POINT pt1;
7881             if (!LISTVIEW_GetOrigin(hwnd, &pt1))
7882             {
7883               pt1.x = 0;
7884               pt1.y = 0;
7885             }
7886             nPosX = pt1.x;
7887             nPosY = pt1.y;
7888             if (uView == LVS_ICON)
7889             {
7890               nPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7891               nPosY += ICON_TOP_PADDING;
7892             }
7893             TRACE("requested special (-1,-1), set to origin (%ld,%ld)\n",
7894                   nPosX, nPosY);
7895           }
7896
7897           lpItem->ptPosition.x = nPosX;
7898           lpItem->ptPosition.y = nPosY;
7899           if (uView == LVS_ICON)
7900           {
7901             lpItem->ptPosition.y -= ICON_TOP_PADDING;
7902               lpItem->ptPosition.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7903               if ((lpItem->ptPosition.y < 0) || (lpItem->ptPosition.x < 0))
7904               {
7905                   FIXME("failed orig (%ld,%ld), intent (%ld,%ld), is (%ld, %ld), setting neg to 0\n",
7906                         orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7907
7908                   /*
7909                   if (lpItem->ptPosition.x < 0) lpItem->ptPosition.x = 0;
7910                   if (lpItem->ptPosition.y < 0) lpItem->ptPosition.y = 0;
7911                   */
7912               }
7913               else
7914               {
7915                   TRACE("orig (%ld,%ld), intent (%ld,%ld), is (%ld,%ld)\n",
7916                         orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7917               }
7918           }
7919         }
7920       }
7921     }
7922   }
7923
7924   return bResult;
7925 }
7926
7927 /***
7928  * DESCRIPTION:
7929  * Sets the state of one or many items.
7930  *
7931  * PARAMETER(S):
7932  * [I] HWND : window handle
7933  * [I]INT : item index
7934  * [I] LPLVITEM : item or subitem info
7935  *
7936  * RETURN:
7937  *   SUCCESS : TRUE
7938  *   FAILURE : FALSE
7939  */
7940 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem)
7941 {
7942   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7943   BOOL bResult = TRUE;
7944   LVITEMW lvItem;
7945
7946   TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s)\n",
7947         hwnd, nItem, debuglvitem_t(lpLVItem, TRUE));
7948
7949   ZeroMemory(&lvItem, sizeof(lvItem));
7950   lvItem.mask = LVIF_STATE;
7951   lvItem.state = lpLVItem->state;
7952   lvItem.stateMask = lpLVItem->stateMask ;
7953   lvItem.iItem = nItem;
7954
7955   if (nItem == -1)
7956   {
7957     /* apply to all items */
7958     for (lvItem.iItem = 0; lvItem.iItem < GETITEMCOUNT(infoPtr); lvItem.iItem++)
7959       if (!LISTVIEW_SetItemT(hwnd, &lvItem, TRUE)) bResult = FALSE;
7960   }
7961   else
7962     bResult = LISTVIEW_SetItemT(hwnd, &lvItem, TRUE);
7963
7964   return bResult;
7965 }
7966
7967 /***
7968  * DESCRIPTION:
7969  * Sets the text of an item or subitem.
7970  *
7971  * PARAMETER(S):
7972  * [I] hwnd : window handle
7973  * [I] nItem : item index
7974  * [I] lpLVItem : item or subitem info
7975  * [I] isW : TRUE if input is Unicode
7976  *
7977  * RETURN:
7978  *   SUCCESS : TRUE
7979  *   FAILURE : FALSE
7980  */
7981 static BOOL LISTVIEW_SetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
7982 {
7983   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7984   BOOL bResult = FALSE;
7985   LVITEMW lvItem;
7986
7987   TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s, isW=%d)\n",
7988         hwnd, nItem, debuglvitem_t(lpLVItem, isW), isW);
7989
7990   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7991   {
7992     ZeroMemory(&lvItem, sizeof(LVITEMW));
7993     lvItem.mask = LVIF_TEXT;
7994     lvItem.pszText = lpLVItem->pszText;
7995     lvItem.iItem = nItem;
7996     lvItem.iSubItem = lpLVItem->iSubItem;
7997     if(isW) bResult = ListView_SetItemW(hwnd, &lvItem);
7998     else    bResult = ListView_SetItemA(hwnd, &lvItem);
7999   }
8000
8001   return bResult;
8002 }
8003
8004 /***
8005  * DESCRIPTION:
8006  * Set item index that marks the start of a multiple selection.
8007  *
8008  * PARAMETER(S):
8009  * [I] HWND : window handle
8010  * [I] INT  : index
8011  *
8012  * RETURN:
8013  * Index number or -1 if there is no selection mark.
8014  */
8015 static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex)
8016 {
8017   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8018   INT nOldIndex = infoPtr->nSelectionMark;
8019
8020   TRACE("(hwnd=%x, nIndex=%d)\n", hwnd, nIndex);
8021
8022   infoPtr->nSelectionMark = nIndex;
8023
8024   return nOldIndex;
8025 }
8026
8027 /***
8028  * DESCRIPTION:
8029  * Sets the text background color.
8030  *
8031  * PARAMETER(S):
8032  * [I] HWND : window handle
8033  * [I] COLORREF : text background color
8034  *
8035  * RETURN:
8036  *   SUCCESS : TRUE
8037  *   FAILURE : FALSE
8038  */
8039 static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk)
8040 {
8041   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8042
8043   TRACE("(hwnd=%x, clrTextBk=%lx)\n", hwnd, clrTextBk);
8044
8045   infoPtr->clrTextBk = clrTextBk;
8046   InvalidateRect(hwnd, NULL, TRUE);
8047
8048   return TRUE;
8049 }
8050
8051 /***
8052  * DESCRIPTION:
8053  * Sets the text foreground color.
8054  *
8055  * PARAMETER(S):
8056  * [I] HWND : window handle
8057  * [I] COLORREF : text color
8058  *
8059  * RETURN:
8060  *   SUCCESS : TRUE
8061  *   FAILURE : FALSE
8062  */
8063 static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText)
8064 {
8065   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8066
8067   TRACE("(hwnd=%x, clrText=%lx)\n", hwnd, clrText);
8068
8069   infoPtr->clrText = clrText;
8070   InvalidateRect(hwnd, NULL, TRUE);
8071
8072   return TRUE;
8073 }
8074
8075 /* LISTVIEW_SetToolTips */
8076 /* LISTVIEW_SetUnicodeFormat */
8077 /* LISTVIEW_SetWorkAreas */
8078
8079 /***
8080  * DESCRIPTION:
8081  * Callback internally used by LISTVIEW_SortItems()
8082  *
8083  * PARAMETER(S):
8084  * [I] LPVOID : first LISTVIEW_ITEM to compare
8085  * [I] LPVOID : second LISTVIEW_ITEM to compare
8086  * [I] LPARAM : HWND of control
8087  *
8088  * RETURN:
8089  *   if first comes before second : negative
8090  *   if first comes after second : positive
8091  *   if first and second are equivalent : zero
8092  */
8093 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
8094 {
8095   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
8096   LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
8097   LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
8098
8099   /* Forward the call to the client defined callback */
8100   return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
8101 }
8102
8103 /***
8104  * DESCRIPTION:
8105  * Sorts the listview items.
8106  *
8107  * PARAMETER(S):
8108  * [I] HWND : window handle
8109  * [I] WPARAM : application-defined value
8110  * [I] LPARAM : pointer to comparision callback
8111  *
8112  * RETURN:
8113  *   SUCCESS : TRUE
8114  *   FAILURE : FALSE
8115  */
8116 static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
8117 {
8118     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8119     UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
8120     HDPA hdpaSubItems=NULL;
8121     LISTVIEW_ITEM *pLVItem=NULL;
8122     LPVOID selectionMarkItem;
8123     int nCount, i;
8124
8125     TRACE("(hwnd=%x, pfnCompare=%p, lParamSort=%lx)\n", hwnd, pfnCompare, lParamSort);
8126
8127     if (lStyle & LVS_OWNERDATA) return FALSE;
8128
8129     if (!infoPtr || !infoPtr->hdpaItems) return FALSE;
8130
8131     nCount = GETITEMCOUNT(infoPtr);
8132     /* if there are 0 or 1 items, there is no need to sort */
8133     if (nCount < 2)
8134         return TRUE;
8135
8136     infoPtr->pfnCompare = pfnCompare;
8137     infoPtr->lParamSort = lParamSort;
8138     DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, hwnd);
8139
8140     /* Adjust selections and indices so that they are the way they should
8141      * be after the sort (otherwise, the list items move around, but
8142      * whatever is at the item's previous original position will be
8143      * selected instead)
8144      */
8145     selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
8146     for (i=0; i < nCount; i++)
8147     {
8148        hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
8149        pLVItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
8150
8151        if (pLVItem->state & LVIS_SELECTED)
8152           LISTVIEW_AddSelectionRange(hwnd, i, i);
8153        else
8154           LISTVIEW_RemoveSelectionRange(hwnd, i, i);
8155        if (pLVItem->state & LVIS_FOCUSED)
8156           infoPtr->nFocusedItem=i;
8157     }
8158     if (selectionMarkItem != NULL)
8159        infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
8160     /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
8161
8162     /* align the items */
8163     LISTVIEW_AlignTop(hwnd);
8164
8165     /* refresh the display */
8166     InvalidateRect(hwnd, NULL, TRUE);
8167
8168     return TRUE;
8169 }
8170
8171 /* LISTVIEW_SubItemHitTest */
8172
8173 /***
8174  * DESCRIPTION:
8175  * Updates an items or rearranges the listview control.
8176  *
8177  * PARAMETER(S):
8178  * [I] HWND : window handle
8179  * [I] INT : item index
8180  *
8181  * RETURN:
8182  *   SUCCESS : TRUE
8183  *   FAILURE : FALSE
8184  */
8185 static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
8186 {
8187   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8188   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
8189   BOOL bResult = FALSE;
8190   RECT rc;
8191
8192   TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
8193
8194   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8195   {
8196     bResult = TRUE;
8197
8198     /* rearrange with default alignment style */
8199     if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
8200         ((lStyle & LVS_TYPEMASK)  == LVS_SMALLICON)))
8201     {
8202       ListView_Arrange(hwnd, 0);
8203     }
8204     else
8205     {
8206       /* get item bounding rectangle */
8207       ListView_GetItemRect(hwnd, nItem, &rc, LVIR_BOUNDS);
8208       InvalidateRect(hwnd, &rc, TRUE);
8209     }
8210   }
8211
8212   return bResult;
8213 }
8214
8215 /***
8216  * DESCRIPTION:
8217  * Creates the listview control.
8218  *
8219  * PARAMETER(S):
8220  * [I] HWND : window handle
8221  *
8222  * RETURN:
8223  * Zero
8224  */
8225 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
8226 {
8227   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8228   UINT uView = lpcs->style & LVS_TYPEMASK;
8229   LOGFONTW logFont;
8230
8231   TRACE("(hwnd=%x, lpcs=%p)\n", hwnd, lpcs);
8232
8233   /* initialize info pointer */
8234   ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
8235
8236   /* determine the type of structures to use */
8237   infoPtr->notifyFormat = SendMessageW(GetParent(hwnd), WM_NOTIFYFORMAT,
8238                                        (WPARAM)hwnd, (LPARAM)NF_QUERY);
8239
8240   /* initialize color information  */
8241   infoPtr->clrBk = comctl32_color.clrWindow;
8242   infoPtr->clrText = comctl32_color.clrWindowText;
8243   infoPtr->clrTextBk = CLR_DEFAULT;
8244
8245   /* set default values */
8246   infoPtr->hwndSelf = hwnd;
8247   infoPtr->uCallbackMask = 0;
8248   infoPtr->nFocusedItem = -1;
8249   infoPtr->nSelectionMark = -1;
8250   infoPtr->nHotItem = -1;
8251   infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
8252   infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
8253   ZeroMemory(&infoPtr->rcList, sizeof(RECT));
8254   infoPtr->hwndEdit = 0;
8255   infoPtr->Editing = FALSE;
8256   infoPtr->pedititem = NULL;
8257   infoPtr->nEditLabelItem = -1;
8258   infoPtr->bIsDrawing = FALSE;
8259
8260   /* get default font (icon title) */
8261   SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
8262   infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
8263   infoPtr->hFont = infoPtr->hDefaultFont;
8264   LISTVIEW_SaveTextMetrics(hwnd);
8265
8266   /* create header */
8267   infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL,
8268     WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
8269     0, 0, 0, 0, hwnd, (HMENU)0,
8270     lpcs->hInstance, NULL);
8271
8272   /* set header unicode format */
8273   SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT,(WPARAM)TRUE,(LPARAM)NULL);
8274
8275   /* set header font */
8276   SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont,
8277                (LPARAM)TRUE);
8278
8279   if (uView == LVS_ICON)
8280   {
8281     infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8282     infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8283   }
8284   else if (uView == LVS_REPORT)
8285   {
8286     if (!(LVS_NOCOLUMNHEADER & lpcs->style))
8287     {
8288       ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8289     }
8290     else
8291     {
8292       /* set HDS_HIDDEN flag to hide the header bar */
8293       SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
8294                     GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
8295     }
8296
8297
8298     infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8299     infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8300   }
8301   else
8302   {
8303     infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8304     infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8305   }
8306
8307   /* display unsupported listview window styles */
8308   LISTVIEW_UnsupportedStyles(lpcs->style);
8309
8310   /* allocate memory for the data structure */
8311   infoPtr->hdpaItems = DPA_Create(10);
8312
8313   /* allocate memory for the selection ranges */
8314   infoPtr->hdpaSelectionRanges = DPA_Create(10);
8315
8316   /* initialize size of items */
8317   infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8318   infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8319
8320   /* initialize the hover time to -1(indicating the default system hover time) */
8321   infoPtr->dwHoverTime = -1;
8322
8323   return 0;
8324 }
8325
8326 /***
8327  * DESCRIPTION:
8328  * Erases the background of the listview control.
8329  *
8330  * PARAMETER(S):
8331  * [I] HWND : window handle
8332  * [I] WPARAM : device context handle
8333  * [I] LPARAM : not used
8334  *
8335  * RETURN:
8336  *   SUCCESS : TRUE
8337  *   FAILURE : FALSE
8338  */
8339 static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam,
8340                                         LPARAM lParam)
8341 {
8342   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8343   BOOL bResult;
8344
8345   TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
8346
8347   if (infoPtr->clrBk == CLR_NONE)
8348   {
8349     bResult = SendMessageW(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam);
8350   }
8351   else
8352   {
8353     RECT rc;
8354     HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
8355     GetClientRect(hwnd, &rc);
8356     FillRect((HDC)wParam, &rc, hBrush);
8357     DeleteObject(hBrush);
8358     bResult = TRUE;
8359   }
8360
8361   return bResult;
8362 }
8363
8364
8365 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc)
8366 {
8367   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8368
8369   TRACE("(hwnd=%x, hdc=%x, rc=%p)\n", hwnd, hdc, rc);
8370
8371   if (infoPtr->clrBk != CLR_NONE)
8372   {
8373     HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
8374     FillRect(hdc, rc, hBrush);
8375     DeleteObject(hBrush);
8376   }
8377 }
8378
8379 /***
8380  * DESCRIPTION:
8381  * Retrieves the listview control font.
8382  *
8383  * PARAMETER(S):
8384  * [I] HWND : window handle
8385  *
8386  * RETURN:
8387  * Font handle.
8388  */
8389 static LRESULT LISTVIEW_GetFont(HWND hwnd)
8390 {
8391   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8392
8393   TRACE("(hwnd=%x)\n", hwnd);
8394
8395   return infoPtr->hFont;
8396 }
8397
8398 /***
8399  * DESCRIPTION:
8400  * Performs vertical scrolling.
8401  *
8402  * PARAMETER(S):
8403  * [I] HWND : window handle
8404  * [I] INT : scroll code
8405  * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8406  *             or SB_THUMBTRACK.
8407  * [I] HWND : scrollbar control window handle
8408  *
8409  * RETURN:
8410  * Zero
8411  *
8412  * NOTES:
8413  *   SB_LINEUP/SB_LINEDOWN:
8414  *        for LVS_ICON, LVS_SMALLICON is 37 by experiment
8415  *        for LVS_REPORT is 1 line --> infoPtr->nItemHeight
8416  *        for LVS_LIST cannot occur ??? (implemented as LVS_REPORT)
8417  *
8418  */
8419 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
8420                                 HWND hScrollWnd)
8421 {
8422   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8423   UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8424   SCROLLINFO scrollInfo;
8425   BOOL is_an_icon;
8426
8427   TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8428         hwnd, nScrollCode, nCurrentPos, hScrollWnd);
8429
8430   SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8431
8432   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8433   scrollInfo.cbSize = sizeof(SCROLLINFO);
8434   scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8435
8436   is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
8437
8438   if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8439   {
8440     INT nOldScrollPos = scrollInfo.nPos;
8441     switch (nScrollCode)
8442     {
8443     case SB_INTERNAL_UP:
8444       if (scrollInfo.nPos > scrollInfo.nMin)
8445           scrollInfo.nPos--;
8446       break;
8447
8448     case SB_INTERNAL_DOWN:
8449       if (scrollInfo.nPos < scrollInfo.nMax)
8450           scrollInfo.nPos++;
8451       break;
8452
8453     case SB_LINEUP:
8454       if (scrollInfo.nPos > scrollInfo.nMin)
8455           scrollInfo.nPos -= (is_an_icon) ?
8456               LISTVIEW_SCROLL_ICON_LINE_SIZE : infoPtr->nItemHeight;
8457       break;
8458
8459     case SB_LINEDOWN:
8460       if (scrollInfo.nPos < scrollInfo.nMax)
8461           scrollInfo.nPos += (is_an_icon) ?
8462               LISTVIEW_SCROLL_ICON_LINE_SIZE : infoPtr->nItemHeight;
8463       break;
8464
8465     case SB_PAGEUP:
8466       if (scrollInfo.nPos > scrollInfo.nMin)
8467       {
8468         if (scrollInfo.nPos >= scrollInfo.nPage)
8469           scrollInfo.nPos -= scrollInfo.nPage;
8470         else
8471           scrollInfo.nPos = scrollInfo.nMin;
8472       }
8473       break;
8474
8475     case SB_PAGEDOWN:
8476       if (scrollInfo.nPos < scrollInfo.nMax)
8477       {
8478         if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8479           scrollInfo.nPos += scrollInfo.nPage;
8480         else
8481           scrollInfo.nPos = scrollInfo.nMax;
8482       }
8483       break;
8484
8485     case SB_THUMBPOSITION:
8486     case SB_THUMBTRACK:
8487         scrollInfo.nPos = nCurrentPos;
8488         if (scrollInfo.nPos > scrollInfo.nMax)
8489             scrollInfo.nPos=scrollInfo.nMax;
8490
8491         if (scrollInfo.nPos < scrollInfo.nMin)
8492             scrollInfo.nPos=scrollInfo.nMin;
8493
8494       break;
8495     }
8496
8497     if (nOldScrollPos != scrollInfo.nPos)
8498     {
8499       scrollInfo.fMask = SIF_POS;
8500       SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
8501
8502       /* Get real position value, the value we set might have been changed
8503        * by SetScrollInfo (especially if we went too far.
8504        */
8505       scrollInfo.fMask = SIF_POS;
8506       GetScrollInfo(hwnd, SB_VERT, &scrollInfo);
8507
8508       /* only if the scroll position really changed, do we update screen */
8509       if (nOldScrollPos != scrollInfo.nPos)
8510       {
8511         if (IsWindowVisible(infoPtr->hwndHeader))
8512         {
8513             RECT rListview, rcHeader, rDest;
8514             GetClientRect(hwnd, &rListview);
8515             GetWindowRect(infoPtr->hwndHeader, &rcHeader);
8516             MapWindowPoints((HWND) NULL, hwnd, (LPPOINT) &rcHeader, 2);
8517             SubtractRect(&rDest, &rListview, &rcHeader);
8518             InvalidateRect(hwnd, &rDest, TRUE);
8519         }
8520         else
8521             InvalidateRect(hwnd, NULL, TRUE);
8522       }
8523     }
8524   }
8525
8526   return 0;
8527 }
8528
8529 /***
8530  * DESCRIPTION:
8531  * Performs horizontal scrolling.
8532  *
8533  * PARAMETER(S):
8534  * [I] HWND : window handle
8535  * [I] INT : scroll code
8536  * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION
8537  *             or SB_THUMBTRACK.
8538  * [I] HWND : scrollbar control window handle
8539  *
8540  * RETURN:
8541  * Zero
8542  *
8543  * NOTES:
8544  *   SB_LINELEFT/SB_LINERIGHT:
8545  *        for LVS_ICON, LVS_SMALLICON  ??? (implemented as 1 pixel)
8546  *        for LVS_REPORT is 1 pixel
8547  *        for LVS_LIST  is 1 column --> which is a 1 because the
8548  *                                      scroll is based on columns not pixels
8549  *
8550  */
8551 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
8552                                 HWND hScrollWnd)
8553 {
8554   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8555   UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8556   SCROLLINFO scrollInfo;
8557   BOOL is_a_list;
8558
8559   TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n",
8560         hwnd, nScrollCode, nCurrentPos, hScrollWnd);
8561
8562   SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8563
8564   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8565   scrollInfo.cbSize = sizeof(SCROLLINFO);
8566   scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8567
8568   is_a_list = (uView == LVS_LIST);
8569
8570   if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
8571   {
8572     INT nOldScrollPos = scrollInfo.nPos;
8573
8574     switch (nScrollCode)
8575     {
8576     case SB_INTERNAL_LEFT:
8577       if (scrollInfo.nPos > scrollInfo.nMin)
8578           scrollInfo.nPos--;
8579       break;
8580
8581     case SB_INTERNAL_RIGHT:
8582       if (scrollInfo.nPos < scrollInfo.nMax)
8583           scrollInfo.nPos++;
8584       break;
8585
8586     case SB_LINELEFT:
8587       if (scrollInfo.nPos > scrollInfo.nMin)
8588           scrollInfo.nPos--;
8589       break;
8590
8591     case SB_LINERIGHT:
8592       if (scrollInfo.nPos < scrollInfo.nMax)
8593           scrollInfo.nPos++;
8594       break;
8595
8596     case SB_PAGELEFT:
8597       if (scrollInfo.nPos > scrollInfo.nMin)
8598       {
8599         if (scrollInfo.nPos >= scrollInfo.nPage)
8600           scrollInfo.nPos -= scrollInfo.nPage;
8601         else
8602           scrollInfo.nPos = scrollInfo.nMin;
8603       }
8604       break;
8605
8606     case SB_PAGERIGHT:
8607       if (scrollInfo.nPos < scrollInfo.nMax)
8608       {
8609         if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8610           scrollInfo.nPos += scrollInfo.nPage;
8611         else
8612           scrollInfo.nPos = scrollInfo.nMax;
8613       }
8614       break;
8615
8616     case SB_THUMBPOSITION:
8617     case SB_THUMBTRACK:
8618         scrollInfo.nPos = nCurrentPos;
8619
8620         if (scrollInfo.nPos > scrollInfo.nMax)
8621             scrollInfo.nPos=scrollInfo.nMax;
8622
8623         if (scrollInfo.nPos < scrollInfo.nMin)
8624             scrollInfo.nPos=scrollInfo.nMin;
8625       break;
8626     }
8627
8628     if (nOldScrollPos != scrollInfo.nPos)
8629     {
8630       UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8631       scrollInfo.fMask = SIF_POS;
8632       SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
8633
8634       /* Get real position value, the value we set might have been changed
8635        * by SetScrollInfo (especially if we went too far.
8636        */
8637       scrollInfo.fMask = SIF_POS;
8638       GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
8639       if(uView == LVS_REPORT)
8640       {
8641           LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
8642       }
8643
8644       /* only if the scroll position really changed, do we update screen */
8645       if (nOldScrollPos != scrollInfo.nPos)
8646           InvalidateRect(hwnd, NULL, TRUE);
8647     }
8648   }
8649
8650   return 0;
8651 }
8652
8653 static LRESULT LISTVIEW_MouseWheel(HWND hwnd, INT wheelDelta)
8654 {
8655     UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8656     INT gcWheelDelta = 0;
8657     UINT pulScrollLines = 3;
8658     SCROLLINFO scrollInfo;
8659
8660     TRACE("(hwnd=%x, wheelDelta=%d)\n", hwnd, wheelDelta);
8661
8662     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8663     gcWheelDelta -= wheelDelta;
8664
8665     ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8666     scrollInfo.cbSize = sizeof(SCROLLINFO);
8667     scrollInfo.fMask = SIF_POS | SIF_RANGE;
8668
8669     switch(uView)
8670     {
8671     case LVS_ICON:
8672     case LVS_SMALLICON:
8673        /*
8674         *  listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8675         *  should be fixed in the future.
8676         */
8677         if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8678             LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION,
8679                              scrollInfo.nPos + (gcWheelDelta < 0) ?
8680                              LISTVIEW_SCROLL_ICON_LINE_SIZE :
8681                              -LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8682         break;
8683
8684     case LVS_REPORT:
8685         if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8686         {
8687             if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8688             {
8689                 int cLineScroll = min(LISTVIEW_GetCountPerColumn(hwnd), pulScrollLines);
8690                 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8691                 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
8692             }
8693         }
8694         break;
8695
8696     case LVS_LIST:
8697         LISTVIEW_HScroll(hwnd, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8698         break;
8699     }
8700     return 0;
8701 }
8702
8703 /***
8704  * DESCRIPTION:
8705  * ???
8706  *
8707  * PARAMETER(S):
8708  * [I] HWND : window handle
8709  * [I] INT : virtual key
8710  * [I] LONG : key data
8711  *
8712  * RETURN:
8713  * Zero
8714  */
8715 static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData)
8716 {
8717   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8718   UINT uView =  GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8719   INT nItem = -1;
8720   NMLVKEYDOWN nmKeyDown;
8721
8722   TRACE("(hwnd=%x, nVirtualKey=%d, lKeyData=%ld)\n", hwnd, nVirtualKey, lKeyData);
8723
8724   /* send LVN_KEYDOWN notification */
8725   nmKeyDown.wVKey = nVirtualKey;
8726   nmKeyDown.flags = 0;
8727   notify(hwnd, LVN_KEYDOWN, &nmKeyDown.hdr);
8728
8729   switch (nVirtualKey)
8730   {
8731   case VK_RETURN:
8732     if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
8733     {
8734       hdr_notify(hwnd, NM_RETURN);        /* NM_RETURN notification */
8735       hdr_notify(hwnd, LVN_ITEMACTIVATE); /* LVN_ITEMACTIVATE notification */
8736     }
8737     break;
8738
8739   case VK_HOME:
8740     if (GETITEMCOUNT(infoPtr) > 0)
8741       nItem = 0;
8742     break;
8743
8744   case VK_END:
8745     if (GETITEMCOUNT(infoPtr) > 0)
8746       nItem = GETITEMCOUNT(infoPtr) - 1;
8747     break;
8748
8749   case VK_LEFT:
8750     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT);
8751     break;
8752
8753   case VK_UP:
8754     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE);
8755     break;
8756
8757   case VK_RIGHT:
8758     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT);
8759     break;
8760
8761   case VK_DOWN:
8762     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW);
8763     break;
8764
8765   case VK_PRIOR:
8766     if (uView == LVS_REPORT)
8767       nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd);
8768     else
8769       nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd)
8770                                     * LISTVIEW_GetCountPerRow(hwnd);
8771     if(nItem < 0) nItem = 0;
8772     break;
8773
8774   case VK_NEXT:
8775     if (uView == LVS_REPORT)
8776       nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd);
8777     else
8778       nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd)
8779                                     * LISTVIEW_GetCountPerRow(hwnd);
8780     if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
8781     break;
8782   }
8783
8784   if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
8785   {
8786     if (LISTVIEW_KeySelection(hwnd, nItem))
8787       UpdateWindow(hwnd); /* update client area */
8788   }
8789
8790   return 0;
8791 }
8792
8793 /***
8794  * DESCRIPTION:
8795  * Kills the focus.
8796  *
8797  * PARAMETER(S):
8798  * [I] HWND : window handle
8799  *
8800  * RETURN:
8801  * Zero
8802  */
8803 static LRESULT LISTVIEW_KillFocus(HWND hwnd)
8804 {
8805   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
8806   UINT uView =  GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8807   INT i,nTop,nBottom;
8808
8809   TRACE("(hwnd=%x)\n", hwnd);
8810
8811   /* send NM_KILLFOCUS notification */
8812   hdr_notify(hwnd, NM_KILLFOCUS);
8813
8814   /* set window focus flag */
8815   infoPtr->bFocus = FALSE;
8816
8817   /* NEED drawing optimization ; redraw the selected items */
8818   if (uView & LVS_REPORT)
8819   {
8820     nTop = LISTVIEW_GetTopIndex(hwnd);
8821     nBottom = nTop +
8822               LISTVIEW_GetCountPerColumn(hwnd) + 1;
8823   }
8824   else
8825   {
8826     nTop = 0;
8827     nBottom = GETITEMCOUNT(infoPtr);
8828   }
8829   for (i = nTop; i<nBottom; i++)
8830   {
8831     if (LISTVIEW_IsSelected(hwnd,i))
8832     {
8833       RECT rcItem;
8834       rcItem.left = LVIR_BOUNDS;
8835       LISTVIEW_GetItemRect(hwnd, i, &rcItem);
8836       InvalidateRect(hwnd, &rcItem, FALSE);
8837     }
8838   }
8839
8840   return 0;
8841 }
8842
8843 /***
8844  * DESCRIPTION:
8845  * Processes double click messages (left mouse button).
8846  *
8847  * PARAMETER(S):
8848  * [I] HWND : window handle
8849  * [I] WORD : key flag
8850  * [I] WORD : x coordinate
8851  * [I] WORD : y coordinate
8852  *
8853  * RETURN:
8854  * Zero
8855  */
8856 static LRESULT LISTVIEW_LButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
8857                                       WORD wPosY)
8858 {
8859   LVHITTESTINFO htInfo;
8860   NMLISTVIEW nmlv;
8861
8862   TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8863
8864   htInfo.pt.x = wPosX;
8865   htInfo.pt.y = wPosY;
8866
8867   /* send NM_DBLCLK notification */
8868   ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8869   if (LISTVIEW_HitTestItem(hwnd, &htInfo, TRUE) != -1)
8870   {
8871     nmlv.iItem = htInfo.iItem;
8872     nmlv.iSubItem = htInfo.iSubItem;
8873   }
8874   else
8875   {
8876     nmlv.iItem = -1;
8877     nmlv.iSubItem = 0;
8878   }
8879   nmlv.ptAction.x = wPosX;
8880   nmlv.ptAction.y = wPosY;
8881   listview_notify(hwnd, NM_DBLCLK, &nmlv);
8882
8883
8884   /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8885   if(nmlv.iItem != -1)
8886     hdr_notify(hwnd, LVN_ITEMACTIVATE);
8887
8888   return 0;
8889 }
8890
8891 /***
8892  * DESCRIPTION:
8893  * Processes mouse down messages (left mouse button).
8894  *
8895  * PARAMETER(S):
8896  * [I] HWND : window handle
8897  * [I] WORD : key flag
8898  * [I] WORD : x coordinate
8899  * [I] WORD : y coordinate
8900  *
8901  * RETURN:
8902  * Zero
8903  */
8904 static LRESULT LISTVIEW_LButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
8905                                     WORD wPosY)
8906 {
8907   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8908   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
8909   static BOOL bGroupSelect = TRUE;
8910   POINT ptPosition;
8911   INT nItem;
8912
8913   TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8914
8915   /* send NM_RELEASEDCAPTURE notification */
8916   hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8917
8918   if (infoPtr->bFocus == FALSE)
8919     SetFocus(hwnd);
8920
8921   /* set left button down flag */
8922   infoPtr->bLButtonDown = TRUE;
8923
8924   ptPosition.x = wPosX;
8925   ptPosition.y = wPosY;
8926   nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8927   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8928   {
8929     if (lStyle & LVS_SINGLESEL)
8930     {
8931       if ((LISTVIEW_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED)
8932           && infoPtr->nEditLabelItem == -1)
8933           infoPtr->nEditLabelItem = nItem;
8934       else
8935         LISTVIEW_SetSelection(hwnd, nItem);
8936     }
8937     else
8938     {
8939       if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8940       {
8941         if (bGroupSelect)
8942           LISTVIEW_AddGroupSelection(hwnd, nItem);
8943         else
8944           LISTVIEW_AddSelection(hwnd, nItem);
8945       }
8946       else if (wKey & MK_CONTROL)
8947       {
8948         bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem);
8949       }
8950       else  if (wKey & MK_SHIFT)
8951       {
8952         LISTVIEW_SetGroupSelection(hwnd, nItem);
8953       }
8954       else
8955       {
8956         BOOL was_selected =
8957             (LISTVIEW_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED);
8958
8959         /* set selection (clears other pre-existing selections) */
8960         LISTVIEW_SetSelection(hwnd, nItem);
8961
8962         if (was_selected && infoPtr->nEditLabelItem == -1)
8963           infoPtr->nEditLabelItem = nItem;
8964       }
8965     }
8966   }
8967   else
8968   {
8969     /* remove all selections */
8970     LISTVIEW_RemoveAllSelections(hwnd);
8971   }
8972
8973   /* redraw if we could have possibly selected something */
8974   if(!GETITEMCOUNT(infoPtr)) InvalidateRect(hwnd, NULL, TRUE);
8975
8976   return 0;
8977 }
8978
8979 /***
8980  * DESCRIPTION:
8981  * Processes mouse up messages (left mouse button).
8982  *
8983  * PARAMETER(S):
8984  * [I] HWND : window handle
8985  * [I] WORD : key flag
8986  * [I] WORD : x coordinate
8987  * [I] WORD : y coordinate
8988  *
8989  * RETURN:
8990  * Zero
8991  */
8992 static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
8993                                   WORD wPosY)
8994 {
8995   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8996
8997   TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8998
8999   if (infoPtr->bLButtonDown != FALSE)
9000   {
9001     LVHITTESTINFO lvHitTestInfo;
9002     NMLISTVIEW nmlv;
9003
9004     lvHitTestInfo.pt.x = wPosX;
9005     lvHitTestInfo.pt.y = wPosY;
9006
9007   /* send NM_CLICK notification */
9008     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
9009     if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
9010     {
9011         nmlv.iItem = lvHitTestInfo.iItem;
9012         nmlv.iSubItem = lvHitTestInfo.iSubItem;
9013     }
9014     else
9015     {
9016         nmlv.iItem = -1;
9017         nmlv.iSubItem = 0;
9018     }
9019     nmlv.ptAction.x = wPosX;
9020     nmlv.ptAction.y = wPosY;
9021     listview_notify(hwnd, NM_CLICK, &nmlv);
9022
9023     /* set left button flag */
9024     infoPtr->bLButtonDown = FALSE;
9025
9026     if(infoPtr->nEditLabelItem != -1)
9027     {
9028       if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && lvHitTestInfo.flags & LVHT_ONITEMLABEL)
9029         LISTVIEW_EditLabelT(hwnd, lvHitTestInfo.iItem, TRUE);
9030       infoPtr->nEditLabelItem = -1;
9031     }
9032   }
9033
9034   return 0;
9035 }
9036
9037 /***
9038  * DESCRIPTION:
9039  * Creates the listview control (called before WM_CREATE).
9040  *
9041  * PARAMETER(S):
9042  * [I] HWND : window handle
9043  * [I] WPARAM : unhandled
9044  * [I] LPARAM : widow creation info
9045  *
9046  * RETURN:
9047  * Zero
9048  */
9049 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
9050 {
9051   LISTVIEW_INFO *infoPtr;
9052
9053   TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
9054
9055   /* allocate memory for info structure */
9056   infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
9057   if (infoPtr == NULL)
9058   {
9059     ERR("could not allocate info memory!\n");
9060     return 0;
9061   }
9062
9063   SetWindowLongW(hwnd, 0, (LONG)infoPtr);
9064   if ((LISTVIEW_INFO *)GetWindowLongW(hwnd, 0) != infoPtr)
9065   {
9066     ERR("pointer assignment error!\n");
9067     return 0;
9068   }
9069
9070   return DefWindowProcW(hwnd, WM_NCCREATE, wParam, lParam);
9071 }
9072
9073 /***
9074  * DESCRIPTION:
9075  * Destroys the listview control (called after WM_DESTROY).
9076  *
9077  * PARAMETER(S):
9078  * [I] HWND : window handle
9079  *
9080  * RETURN:
9081  * Zero
9082  */
9083 static LRESULT LISTVIEW_NCDestroy(HWND hwnd)
9084 {
9085   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9086   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9087
9088   TRACE("(hwnd=%x)\n", hwnd);
9089
9090   /* delete all items */
9091   LISTVIEW_DeleteAllItems(hwnd);
9092
9093   /* destroy data structure */
9094   DPA_Destroy(infoPtr->hdpaItems);
9095   DPA_Destroy(infoPtr->hdpaSelectionRanges);
9096
9097   /* destroy image lists */
9098   if (!(lStyle & LVS_SHAREIMAGELISTS))
9099   {
9100 #if 0
9101       /* FIXME: If the caller does a ImageList_Destroy and then we
9102        *        do this code the area will be freed twice. Currently
9103        *        this generates an "err:heap:HEAP_ValidateInUseArena
9104        *        Heap xxxxxxxx: in-use arena yyyyyyyy next block
9105        *        has PREV_FREE flag" sometimes.
9106        *
9107        *        We will leak the memory till we figure out how to fix
9108        */
9109       if (infoPtr->himlNormal)
9110           ImageList_Destroy(infoPtr->himlNormal);
9111       if (infoPtr->himlSmall)
9112           ImageList_Destroy(infoPtr->himlSmall);
9113       if (infoPtr->himlState)
9114           ImageList_Destroy(infoPtr->himlState);
9115 #endif
9116   }
9117
9118   /* destroy font */
9119   infoPtr->hFont = (HFONT)0;
9120   if (infoPtr->hDefaultFont)
9121   {
9122     DeleteObject(infoPtr->hDefaultFont);
9123   }
9124
9125   /* free listview info pointer*/
9126   COMCTL32_Free(infoPtr);
9127
9128   SetWindowLongW(hwnd, 0, 0);
9129   return 0;
9130 }
9131
9132 /***
9133  * DESCRIPTION:
9134  * Handles notifications from children.
9135  *
9136  * PARAMETER(S):
9137  * [I] HWND : window handle
9138  * [I] INT : control identifier
9139  * [I] LPNMHDR : notification information
9140  *
9141  * RETURN:
9142  * Zero
9143  */
9144 static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh)
9145 {
9146   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9147
9148   TRACE("(hwnd=%x, nCtrlId=%d, lpnmh=%p)\n", hwnd, nCtrlId, lpnmh);
9149
9150   if (lpnmh->hwndFrom == infoPtr->hwndHeader)
9151   {
9152     /* handle notification from header control */
9153     if (lpnmh->code == HDN_ENDTRACKW)
9154     {
9155       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9156       InvalidateRect(hwnd, NULL, TRUE);
9157     }
9158     else if(lpnmh->code ==  HDN_ITEMCLICKW || lpnmh->code ==  HDN_ITEMCLICKA)
9159     {
9160         /* Handle sorting by Header Column */
9161         NMLISTVIEW nmlv;
9162
9163         ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
9164         nmlv.iItem = -1;
9165         nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
9166         listview_notify(hwnd, LVN_COLUMNCLICK, &nmlv);
9167     }
9168     else if(lpnmh->code == NM_RELEASEDCAPTURE)
9169     {
9170       /* Idealy this should be done in HDN_ENDTRACKA
9171        * but since SetItemBounds in Header.c is called after
9172        * the notification is sent, it is neccessary to handle the
9173        * update of the scroll bar here (Header.c works fine as it is,
9174        * no need to disturb it)
9175        */
9176       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9177       LISTVIEW_UpdateScroll(hwnd);
9178       InvalidateRect(hwnd, NULL, TRUE);
9179     }
9180
9181   }
9182
9183   return 0;
9184 }
9185
9186 /***
9187  * DESCRIPTION:
9188  * Determines the type of structure to use.
9189  *
9190  * PARAMETER(S):
9191  * [I] HWND : window handle of the sender
9192  * [I] HWND : listview window handle
9193  * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
9194  *
9195  * RETURN:
9196  * Zero
9197  */
9198 static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand)
9199 {
9200   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9201
9202   TRACE("(hwndFrom=%x, hwnd=%x, nCommand=%d)\n", hwndFrom, hwnd, nCommand);
9203
9204   if (nCommand == NF_REQUERY)
9205     infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT,
9206                                          (WPARAM)hwnd, (LPARAM)NF_QUERY);
9207   return 0;
9208 }
9209
9210 /***
9211  * DESCRIPTION:
9212  * Paints/Repaints the listview control.
9213  *
9214  * PARAMETER(S):
9215  * [I] HWND : window handle
9216  * [I] HDC : device context handle
9217  *
9218  * RETURN:
9219  * Zero
9220  */
9221 static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc)
9222 {
9223   PAINTSTRUCT ps;
9224
9225   TRACE("(hwnd=%x, hdc=%x)\n", hwnd, hdc);
9226
9227   if (hdc == 0)
9228   {
9229     hdc = BeginPaint(hwnd, &ps);
9230     LISTVIEW_Refresh(hwnd, hdc);
9231     EndPaint(hwnd, &ps);
9232   }
9233   else
9234   {
9235     LISTVIEW_Refresh(hwnd, hdc);
9236   }
9237
9238   return 0;
9239 }
9240
9241 /***
9242  * DESCRIPTION:
9243  * Processes double click messages (right mouse button).
9244  *
9245  * PARAMETER(S):
9246  * [I] HWND : window handle
9247  * [I] WORD : key flag
9248  * [I] WORD : x coordinate
9249  * [I] WORD : y coordinate
9250  *
9251  * RETURN:
9252  * Zero
9253  */
9254 static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX,
9255                                       WORD wPosY)
9256 {
9257   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
9258
9259   /* send NM_RELEASEDCAPTURE notification */
9260   hdr_notify(hwnd, NM_RELEASEDCAPTURE);
9261
9262   /* send NM_RDBLCLK notification */
9263   hdr_notify(hwnd, NM_RDBLCLK);
9264
9265   return 0;
9266 }
9267
9268 /***
9269  * DESCRIPTION:
9270  * Processes mouse down messages (right mouse button).
9271  *
9272  * PARAMETER(S):
9273  * [I] HWND : window handle
9274  * [I] WORD : key flag
9275  * [I] WORD : x coordinate
9276  * [I] WORD : y coordinate
9277  *
9278  * RETURN:
9279  * Zero
9280  */
9281 static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX,
9282                                     WORD wPosY)
9283 {
9284   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9285   POINT ptPosition;
9286   INT nItem;
9287   NMLISTVIEW nmlv;
9288   LVHITTESTINFO lvHitTestInfo;
9289
9290   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
9291
9292   /* send NM_RELEASEDCAPTURE notification */
9293   hdr_notify(hwnd, NM_RELEASEDCAPTURE);
9294
9295   /* make sure the listview control window has the focus */
9296   if (infoPtr->bFocus == FALSE)
9297     SetFocus(hwnd);
9298
9299   /* set right button down flag */
9300   infoPtr->bRButtonDown = TRUE;
9301
9302   /* determine the index of the selected item */
9303   ptPosition.x = wPosX;
9304   ptPosition.y = wPosY;
9305   nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
9306   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
9307   {
9308     LISTVIEW_SetItemFocus(hwnd,nItem);
9309     if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9310         !LISTVIEW_IsSelected(hwnd,nItem))
9311       LISTVIEW_SetSelection(hwnd, nItem);
9312   }
9313   else
9314   {
9315     LISTVIEW_RemoveAllSelections(hwnd);
9316   }
9317
9318   lvHitTestInfo.pt.x = wPosX;
9319   lvHitTestInfo.pt.y = wPosY;
9320
9321   /* Send NM_RClICK notification */
9322   ZeroMemory(&nmlv, sizeof(nmlv));
9323   if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
9324   {
9325     nmlv.iItem = lvHitTestInfo.iItem;
9326     nmlv.iSubItem = lvHitTestInfo.iSubItem;
9327   }
9328   else
9329   {
9330     nmlv.iItem = -1;
9331     nmlv.iSubItem = 0;
9332   }
9333   nmlv.ptAction.x = wPosX;
9334   nmlv.ptAction.y = wPosY;
9335   listview_notify(hwnd, NM_RCLICK, &nmlv);
9336
9337   return 0;
9338 }
9339
9340 /***
9341  * DESCRIPTION:
9342  * Processes mouse up messages (right mouse button).
9343  *
9344  * PARAMETER(S):
9345  * [I] HWND : window handle
9346  * [I] WORD : key flag
9347  * [I] WORD : x coordinate
9348  * [I] WORD : y coordinate
9349  *
9350  * RETURN:
9351  * Zero
9352  */
9353 static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX,
9354                                   WORD wPosY)
9355 {
9356   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9357
9358   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
9359
9360   if (infoPtr->bRButtonDown)
9361   {
9362     POINT pt;
9363
9364     pt.x = wPosX;
9365     pt.y = wPosY;
9366
9367     /* set button flag */
9368     infoPtr->bRButtonDown = FALSE;
9369
9370     /* Change to screen coordinate for WM_CONTEXTMENU */
9371     ClientToScreen(hwnd, &pt);
9372
9373     /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9374     SendMessageW( hwnd, WM_CONTEXTMENU, (WPARAM) hwnd, MAKELPARAM(pt.x, pt.y));
9375   }
9376
9377   return 0;
9378 }
9379
9380 /***
9381  * DESCRIPTION:
9382  * Sets the focus.
9383  *
9384  * PARAMETER(S):
9385  * [I] HWND : window handle
9386  * [I] HWND : window handle of previously focused window
9387  *
9388  * RETURN:
9389  * Zero
9390  */
9391 static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus)
9392 {
9393   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9394
9395   TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
9396
9397   /* send NM_SETFOCUS notification */
9398   hdr_notify(hwnd, NM_SETFOCUS);
9399
9400   /* set window focus flag */
9401   infoPtr->bFocus = TRUE;
9402
9403   UpdateWindow(hwnd);
9404
9405   return 0;
9406 }
9407
9408 /***
9409  * DESCRIPTION:
9410  * Sets the font.
9411  *
9412  * PARAMETER(S):
9413  * [I] HWND : window handle
9414  * [I] HFONT : font handle
9415  * [I] WORD : redraw flag
9416  *
9417  * RETURN:
9418  * Zero
9419  */
9420 static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw)
9421 {
9422   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9423   UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
9424
9425   TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
9426
9427   infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9428   LISTVIEW_SaveTextMetrics(hwnd);
9429
9430   if (uView == LVS_REPORT)
9431   {
9432     /* set header font */
9433     SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont,
9434                    MAKELPARAM(fRedraw, 0));
9435   }
9436
9437   /* invalidate listview control client area */
9438   InvalidateRect(hwnd, NULL, TRUE);
9439
9440   if (fRedraw != FALSE)
9441     UpdateWindow(hwnd);
9442
9443   return 0;
9444 }
9445
9446 /***
9447  * DESCRIPTION:
9448  * Message handling for WM_SETREDRAW.
9449  * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9450  *
9451  * PARAMETER(S):
9452  * [I] HWND   : window handle
9453  * [I] bRedraw: state of redraw flag
9454  *
9455  * RETURN:
9456  * DefWinProc return value
9457  */
9458 static LRESULT LISTVIEW_SetRedraw(HWND hwnd, BOOL bRedraw)
9459 {
9460     LRESULT lResult = DefWindowProcW(hwnd, WM_SETREDRAW, bRedraw, 0);
9461     if(bRedraw)
9462         RedrawWindow(hwnd, NULL, 0,
9463             RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
9464     return lResult;
9465 }
9466
9467 /***
9468  * DESCRIPTION:
9469  * Resizes the listview control. This function processes WM_SIZE
9470  * messages.  At this time, the width and height are not used.
9471  *
9472  * PARAMETER(S):
9473  * [I] HWND : window handle
9474  * [I] WORD : new width
9475  * [I] WORD : new height
9476  *
9477  * RETURN:
9478  * Zero
9479  */
9480 static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height)
9481 {
9482   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9483   UINT uView = lStyle & LVS_TYPEMASK;
9484
9485   TRACE("(hwnd=%x, width=%d, height=%d)\n", hwnd, Width, Height);
9486
9487   if (LISTVIEW_UpdateSize(hwnd))
9488   {
9489     if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
9490     {
9491         if (lStyle & LVS_ALIGNLEFT)
9492             LISTVIEW_AlignLeft(hwnd);
9493         else
9494             LISTVIEW_AlignTop(hwnd);
9495     }
9496
9497     LISTVIEW_UpdateScroll(hwnd);
9498
9499     /* invalidate client area + erase background */
9500     InvalidateRect(hwnd, NULL, TRUE);
9501   }
9502
9503   return 0;
9504 }
9505
9506 /***
9507  * DESCRIPTION:
9508  * Sets the size information.
9509  *
9510  * PARAMETER(S):
9511  * [I] HWND : window handle
9512  *
9513  * RETURN:
9514  * Zero if no size change
9515  * 1 of size changed
9516  */
9517 static BOOL LISTVIEW_UpdateSize(HWND hwnd)
9518 {
9519   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9520   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9521   UINT uView = lStyle & LVS_TYPEMASK;
9522   RECT rcList;
9523   RECT rcOld;
9524
9525   TRACE("(hwnd=%x)\n", hwnd);
9526
9527   GetClientRect(hwnd, &rcList);
9528   CopyRect(&rcOld,&(infoPtr->rcList));
9529   infoPtr->rcList.left = 0;
9530   infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
9531   infoPtr->rcList.top = 0;
9532   infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
9533
9534   if (uView == LVS_LIST)
9535   {
9536     /* Apparently the "LIST" style is supposed to have the same
9537      * number of items in a column even if there is no scroll bar.
9538      * Since if a scroll bar already exists then the bottom is already
9539      * reduced, only reduce if the scroll bar does not currently exist.
9540      * The "2" is there to mimic the native control. I think it may be
9541      * related to either padding or edges.  (GLA 7/2002)
9542      */
9543     if (!(lStyle & WS_HSCROLL))
9544     {
9545       INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
9546       if (infoPtr->rcList.bottom > nHScrollHeight)
9547         infoPtr->rcList.bottom -= (nHScrollHeight + 2);
9548     }
9549     else
9550     {
9551       if (infoPtr->rcList.bottom > 2)
9552         infoPtr->rcList.bottom -= 2;
9553     }
9554   }
9555   else if (uView == LVS_REPORT)
9556   {
9557     HDLAYOUT hl;
9558     WINDOWPOS wp;
9559
9560     hl.prc = &rcList;
9561     hl.pwpos = &wp;
9562     Header_Layout(infoPtr->hwndHeader, &hl);
9563
9564     SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9565
9566     if (!(LVS_NOCOLUMNHEADER & lStyle))
9567       infoPtr->rcList.top = max(wp.cy, 0);
9568   }
9569   return (EqualRect(&rcOld,&(infoPtr->rcList)));
9570 }
9571
9572 /***
9573  * DESCRIPTION:
9574  * Processes WM_STYLECHANGED messages.
9575  *
9576  * PARAMETER(S):
9577  * [I] HWND : window handle
9578  * [I] WPARAM : window style type (normal or extended)
9579  * [I] LPSTYLESTRUCT : window style information
9580  *
9581  * RETURN:
9582  * Zero
9583  */
9584 static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType,
9585                                  LPSTYLESTRUCT lpss)
9586 {
9587   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9588   UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9589   UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9590   RECT rcList = infoPtr->rcList;
9591
9592   TRACE("(hwnd=%x, styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
9593         hwnd, wStyleType, lpss->styleOld, lpss->styleNew);
9594
9595   if (wStyleType == GWL_STYLE)
9596   {
9597     if (uOldView == LVS_REPORT)
9598       ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9599
9600     if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9601         ((lpss->styleNew & WS_HSCROLL) == 0))
9602        ShowScrollBar(hwnd, SB_HORZ, FALSE);
9603
9604     if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9605         ((lpss->styleNew & WS_VSCROLL) == 0))
9606        ShowScrollBar(hwnd, SB_VERT, FALSE);
9607
9608     /* If switching modes, then start with no scroll bars and then
9609      * decide.
9610      */
9611     if (uNewView != uOldView)
9612         ShowScrollBar(hwnd, SB_BOTH, FALSE);
9613
9614     if (uNewView == LVS_ICON)
9615     {
9616       INT oldcx, oldcy;
9617
9618       /* First readjust the iconSize and if necessary the iconSpacing */
9619       oldcx = infoPtr->iconSize.cx;
9620       oldcy = infoPtr->iconSize.cy;
9621       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
9622       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
9623       if (infoPtr->himlNormal != NULL)
9624       {
9625           INT cx, cy;
9626           ImageList_GetIconSize(infoPtr->himlNormal, &cx, &cy);
9627           infoPtr->iconSize.cx = cx;
9628           infoPtr->iconSize.cy = cy;
9629       }
9630       if ((infoPtr->iconSize.cx != oldcx) || (infoPtr->iconSize.cy != oldcy))
9631       {
9632           TRACE("icon old size=(%d,%d), new size=(%ld,%ld)\n",
9633                 oldcx, oldcy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9634           LISTVIEW_SetIconSpacing(hwnd,0);
9635       }
9636
9637       /* Now update the full item width and height */
9638       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9639       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9640       if (lpss->styleNew & LVS_ALIGNLEFT)
9641         LISTVIEW_AlignLeft(hwnd);
9642       else
9643         LISTVIEW_AlignTop(hwnd);
9644     }
9645     else if (uNewView == LVS_REPORT)
9646     {
9647       HDLAYOUT hl;
9648       WINDOWPOS wp;
9649
9650       hl.prc = &rcList;
9651       hl.pwpos = &wp;
9652       Header_Layout(infoPtr->hwndHeader, &hl);
9653       SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy,
9654                    wp.flags);
9655       if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
9656         ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9657
9658       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9659       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9660       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9661       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9662     }
9663     else if (uNewView == LVS_LIST)
9664     {
9665       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9666       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9667       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9668       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9669     }
9670     else
9671     {
9672       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9673       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9674       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9675       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9676       if (lpss->styleNew & LVS_ALIGNLEFT)
9677         LISTVIEW_AlignLeft(hwnd);
9678       else
9679         LISTVIEW_AlignTop(hwnd);
9680     }
9681
9682     /* update the size of the client area */
9683     LISTVIEW_UpdateSize(hwnd);
9684
9685     /* add scrollbars if needed */
9686     LISTVIEW_UpdateScroll(hwnd);
9687
9688     /* invalidate client area + erase background */
9689     InvalidateRect(hwnd, NULL, TRUE);
9690
9691     /* print the list of unsupported window styles */
9692     LISTVIEW_UnsupportedStyles(lpss->styleNew);
9693   }
9694
9695   /* If they change the view and we have an active edit control
9696      we will need to kill the control since the redraw will
9697      misplace the edit control.
9698    */
9699   if (infoPtr->Editing &&
9700         ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
9701         ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
9702   {
9703      SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9704   }
9705
9706   return 0;
9707 }
9708
9709 /***
9710  * DESCRIPTION:
9711  * Window procedure of the listview control.
9712  *
9713  */
9714 static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
9715                                    LPARAM lParam)
9716 {
9717   TRACE("(hwnd=%x uMsg=%x wParam=%x lParam=%lx)\n", hwnd, uMsg, wParam, lParam);
9718   if (!GetWindowLongW(hwnd, 0) && (uMsg != WM_NCCREATE))
9719     return DefWindowProcW( hwnd, uMsg, wParam, lParam );
9720   switch (uMsg)
9721   {
9722   case LVM_APPROXIMATEVIEWRECT:
9723     return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam,
9724                                         LOWORD(lParam), HIWORD(lParam));
9725   case LVM_ARRANGE:
9726     return LISTVIEW_Arrange(hwnd, (INT)wParam);
9727
9728 /* case LVM_CREATEDRAGIMAGE: */
9729
9730   case LVM_DELETEALLITEMS:
9731     return LISTVIEW_DeleteAllItems(hwnd);
9732
9733   case LVM_DELETECOLUMN:
9734     return LISTVIEW_DeleteColumn(hwnd, (INT)wParam);
9735
9736   case LVM_DELETEITEM:
9737     return LISTVIEW_DeleteItem(hwnd, (INT)wParam);
9738
9739   case LVM_EDITLABELW:
9740     return LISTVIEW_EditLabelT(hwnd, (INT)wParam, TRUE);
9741
9742   case LVM_EDITLABELA:
9743     return LISTVIEW_EditLabelT(hwnd, (INT)wParam, FALSE);
9744
9745   case LVM_ENSUREVISIBLE:
9746     return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
9747
9748   case LVM_FINDITEMW:
9749     return LISTVIEW_FindItemW(hwnd, (INT)wParam, (LPLVFINDINFOW)lParam);
9750
9751   case LVM_FINDITEMA:
9752     return LISTVIEW_FindItemA(hwnd, (INT)wParam, (LPLVFINDINFOA)lParam);
9753
9754   case LVM_GETBKCOLOR:
9755     return LISTVIEW_GetBkColor(hwnd);
9756
9757 /*      case LVM_GETBKIMAGE: */
9758
9759   case LVM_GETCALLBACKMASK:
9760     return LISTVIEW_GetCallbackMask(hwnd);
9761
9762   case LVM_GETCOLUMNA:
9763     return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9764
9765   case LVM_GETCOLUMNW:
9766     return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9767
9768   case LVM_GETCOLUMNORDERARRAY:
9769     return LISTVIEW_GetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9770
9771   case LVM_GETCOLUMNWIDTH:
9772     return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam);
9773
9774   case LVM_GETCOUNTPERPAGE:
9775     return LISTVIEW_GetCountPerPage(hwnd);
9776
9777   case LVM_GETEDITCONTROL:
9778     return LISTVIEW_GetEditControl(hwnd);
9779
9780   case LVM_GETEXTENDEDLISTVIEWSTYLE:
9781     return LISTVIEW_GetExtendedListViewStyle(hwnd);
9782
9783   case LVM_GETHEADER:
9784     return LISTVIEW_GetHeader(hwnd);
9785
9786   case LVM_GETHOTCURSOR:
9787     FIXME("LVM_GETHOTCURSOR: unimplemented\n");
9788     return FALSE;
9789
9790   case LVM_GETHOTITEM:
9791     return LISTVIEW_GetHotItem(hwnd);
9792
9793   case LVM_GETHOVERTIME:
9794     return LISTVIEW_GetHoverTime(hwnd);
9795
9796   case LVM_GETIMAGELIST:
9797     return LISTVIEW_GetImageList(hwnd, (INT)wParam);
9798
9799   case LVM_GETISEARCHSTRINGA:
9800   case LVM_GETISEARCHSTRINGW:
9801     FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9802     return FALSE;
9803
9804   case LVM_GETITEMA:
9805     return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, FALSE);
9806
9807   case LVM_GETITEMW:
9808     return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, TRUE);
9809
9810   case LVM_GETITEMCOUNT:
9811     return LISTVIEW_GetItemCount(hwnd);
9812
9813   case LVM_GETITEMPOSITION:
9814     return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam);
9815
9816   case LVM_GETITEMRECT:
9817     return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam);
9818
9819   case LVM_GETITEMSPACING:
9820     return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam);
9821
9822   case LVM_GETITEMSTATE:
9823     return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam);
9824
9825   case LVM_GETITEMTEXTA:
9826     return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9827
9828   case LVM_GETITEMTEXTW:
9829     return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9830
9831   case LVM_GETNEXTITEM:
9832     return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
9833
9834   case LVM_GETNUMBEROFWORKAREAS:
9835     FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9836     return 1;
9837
9838   case LVM_GETORIGIN:
9839     return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam);
9840
9841   case LVM_GETSELECTEDCOUNT:
9842     return LISTVIEW_GetSelectedCount(hwnd);
9843
9844   case LVM_GETSELECTIONMARK:
9845     return LISTVIEW_GetSelectionMark(hwnd);
9846
9847   case LVM_GETSTRINGWIDTHA:
9848     return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, FALSE);
9849
9850   case LVM_GETSTRINGWIDTHW:
9851     return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, TRUE);
9852
9853   case LVM_GETSUBITEMRECT:
9854     return LISTVIEW_GetSubItemRect(hwnd, (UINT)wParam, ((LPRECT)lParam)->top,
9855                                    ((LPRECT)lParam)->left, (LPRECT)lParam);
9856
9857   case LVM_GETTEXTBKCOLOR:
9858     return LISTVIEW_GetTextBkColor(hwnd);
9859
9860   case LVM_GETTEXTCOLOR:
9861     return LISTVIEW_GetTextColor(hwnd);
9862
9863   case LVM_GETTOOLTIPS:
9864     FIXME("LVM_GETTOOLTIPS: unimplemented\n");
9865     return FALSE;
9866
9867   case LVM_GETTOPINDEX:
9868     return LISTVIEW_GetTopIndex(hwnd);
9869
9870   /*case LVM_GETUNICODEFORMAT:
9871     FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9872     return FALSE;*/
9873
9874   case LVM_GETVIEWRECT:
9875     return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
9876
9877   case LVM_GETWORKAREAS:
9878     FIXME("LVM_GETWORKAREAS: unimplemented\n");
9879     return FALSE;
9880
9881   case LVM_HITTEST:
9882     return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
9883
9884   case LVM_INSERTCOLUMNA:
9885     return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9886
9887   case LVM_INSERTCOLUMNW:
9888     return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9889
9890   case LVM_INSERTITEMA:
9891     return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9892
9893   case LVM_INSERTITEMW:
9894     return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9895
9896   case LVM_REDRAWITEMS:
9897     return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam);
9898
9899   case LVM_SCROLL:
9900     return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam);
9901
9902   case LVM_SETBKCOLOR:
9903     return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam);
9904
9905 /*      case LVM_SETBKIMAGE: */
9906
9907   case LVM_SETCALLBACKMASK:
9908     return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam);
9909
9910   case LVM_SETCOLUMNA:
9911     return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9912
9913   case LVM_SETCOLUMNW:
9914     return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9915
9916   case LVM_SETCOLUMNORDERARRAY:
9917     return LISTVIEW_SetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9918
9919   case LVM_SETCOLUMNWIDTH:
9920     return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, SLOWORD(lParam));
9921
9922   case LVM_SETEXTENDEDLISTVIEWSTYLE:
9923     return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam);
9924
9925 /*      case LVM_SETHOTCURSOR: */
9926
9927   case LVM_SETHOTITEM:
9928     return LISTVIEW_SetHotItem(hwnd, (INT)wParam);
9929
9930   case LVM_SETHOVERTIME:
9931     return LISTVIEW_SetHoverTime(hwnd, (DWORD)wParam);
9932
9933   case LVM_SETICONSPACING:
9934     return LISTVIEW_SetIconSpacing(hwnd, (DWORD)lParam);
9935
9936   case LVM_SETIMAGELIST:
9937     return (LRESULT)LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
9938
9939   case LVM_SETITEMA:
9940     return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9941
9942   case LVM_SETITEMW:
9943     return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9944
9945   case LVM_SETITEMCOUNT:
9946     return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam);
9947
9948   case LVM_SETITEMPOSITION:
9949     return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam),
9950                                     (INT)HIWORD(lParam));
9951
9952   case LVM_SETITEMPOSITION32:
9953     return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, ((POINT*)lParam)->x,
9954                                     ((POINT*)lParam)->y);
9955
9956   case LVM_SETITEMSTATE:
9957     return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMW)lParam);
9958
9959   case LVM_SETITEMTEXTA:
9960     return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9961
9962   case LVM_SETITEMTEXTW:
9963     return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9964
9965   case LVM_SETSELECTIONMARK:
9966     return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam);
9967
9968   case LVM_SETTEXTBKCOLOR:
9969     return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam);
9970
9971   case LVM_SETTEXTCOLOR:
9972     return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam);
9973
9974 /*      case LVM_SETTOOLTIPS: */
9975 /*      case LVM_SETUNICODEFORMAT: */
9976 /*      case LVM_SETWORKAREAS: */
9977
9978   case LVM_SORTITEMS:
9979     return LISTVIEW_SortItems(hwnd, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9980
9981   case LVM_SUBITEMHITTEST:
9982     return LISTVIEW_SubItemHitTest(hwnd, (LPLVHITTESTINFO)lParam);
9983
9984   case LVM_UPDATE:
9985     return LISTVIEW_Update(hwnd, (INT)wParam);
9986
9987   case WM_CHAR:
9988     return LISTVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
9989
9990   case WM_COMMAND:
9991     return LISTVIEW_Command(hwnd, wParam, lParam);
9992
9993   case WM_CREATE:
9994     return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9995
9996   case WM_ERASEBKGND:
9997     return LISTVIEW_EraseBackground(hwnd, wParam, lParam);
9998
9999   case WM_GETDLGCODE:
10000     return DLGC_WANTCHARS | DLGC_WANTARROWS;
10001
10002   case WM_GETFONT:
10003     return LISTVIEW_GetFont(hwnd);
10004
10005   case WM_HSCROLL:
10006     if (SLOWORD(wParam) < 0) return 0; /* validate not internal codes */
10007     return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam),
10008                             (INT)HIWORD(wParam), (HWND)lParam);
10009
10010   case WM_KEYDOWN:
10011     return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam);
10012
10013   case WM_KILLFOCUS:
10014     return LISTVIEW_KillFocus(hwnd);
10015
10016   case WM_LBUTTONDBLCLK:
10017     return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
10018                                 HIWORD(lParam));
10019
10020   case WM_LBUTTONDOWN:
10021     return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
10022                                 HIWORD(lParam));
10023   case WM_LBUTTONUP:
10024     return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
10025                               HIWORD(lParam));
10026   case WM_MOUSEMOVE:
10027     return LISTVIEW_MouseMove (hwnd, wParam, lParam);
10028
10029   case WM_MOUSEHOVER:
10030     return LISTVIEW_MouseHover(hwnd, wParam, lParam);
10031
10032   case WM_NCCREATE:
10033     return LISTVIEW_NCCreate(hwnd, wParam, lParam);
10034
10035   case WM_NCDESTROY:
10036     return LISTVIEW_NCDestroy(hwnd);
10037
10038   case WM_NOTIFY:
10039     return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam);
10040
10041   case WM_NOTIFYFORMAT:
10042     return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam);
10043
10044   case WM_PAINT:
10045     return LISTVIEW_Paint(hwnd, (HDC)wParam);
10046
10047   case WM_RBUTTONDBLCLK:
10048     return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam),
10049                                   HIWORD(lParam));
10050
10051   case WM_RBUTTONDOWN:
10052     return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam),
10053                                 HIWORD(lParam));
10054
10055   case WM_RBUTTONUP:
10056     return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam),
10057                               HIWORD(lParam));
10058
10059   case WM_SETFOCUS:
10060     return LISTVIEW_SetFocus(hwnd, (HWND)wParam);
10061
10062   case WM_SETFONT:
10063     return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam);
10064
10065   case WM_SETREDRAW:
10066     return LISTVIEW_SetRedraw(hwnd, (BOOL)wParam);
10067
10068   case WM_SIZE:
10069     return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
10070
10071   case WM_STYLECHANGED:
10072     return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam);
10073
10074   case WM_SYSCOLORCHANGE:
10075     COMCTL32_RefreshSysColors();
10076     return 0;
10077
10078 /*      case WM_TIMER: */
10079
10080   case WM_VSCROLL:
10081     if (SLOWORD(wParam) < 0) return 0; /* validate not internal codes */
10082     return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam),
10083                             (INT)HIWORD(wParam), (HWND)lParam);
10084
10085   case WM_MOUSEWHEEL:
10086       if (wParam & (MK_SHIFT | MK_CONTROL))
10087           return DefWindowProcW( hwnd, uMsg, wParam, lParam );
10088       return LISTVIEW_MouseWheel(hwnd, (short int)HIWORD(wParam));
10089
10090   case WM_WINDOWPOSCHANGED:
10091       if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE)) {
10092           SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
10093                        SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
10094           LISTVIEW_UpdateSize(hwnd);
10095           LISTVIEW_UpdateScroll(hwnd);
10096       }
10097       return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10098
10099 /*      case WM_WININICHANGE: */
10100
10101   default:
10102     if ((uMsg >= WM_USER) && (uMsg < WM_APP))
10103     {
10104       ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam,
10105           lParam);
10106     }
10107
10108     /* call default window procedure */
10109     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10110   }
10111
10112   return 0;
10113 }
10114
10115 /***
10116  * DESCRIPTION:
10117  * Registers the window class.
10118  *
10119  * PARAMETER(S):
10120  * None
10121  *
10122  * RETURN:
10123  * None
10124  */
10125 VOID LISTVIEW_Register(void)
10126 {
10127   WNDCLASSW wndClass;
10128
10129     ZeroMemory(&wndClass, sizeof(WNDCLASSW));
10130     wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
10131     wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
10132     wndClass.cbClsExtra = 0;
10133     wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
10134     wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
10135     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
10136     wndClass.lpszClassName = WC_LISTVIEWW;
10137     RegisterClassW(&wndClass);
10138 }
10139
10140 /***
10141  * DESCRIPTION:
10142  * Unregisters the window class.
10143  *
10144  * PARAMETER(S):
10145  * None
10146  *
10147  * RETURN:
10148  * None
10149  */
10150 VOID LISTVIEW_Unregister(void)
10151 {
10152     UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
10153 }
10154
10155 /***
10156  * DESCRIPTION:
10157  * Handle any WM_COMMAND messages
10158  *
10159  * PARAMETER(S):
10160  *
10161  * RETURN:
10162  */
10163 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam)
10164 {
10165     switch (HIWORD(wParam))
10166     {
10167         case EN_UPDATE:
10168         {
10169             /*
10170              * Adjust the edit window size
10171              */
10172             WCHAR buffer[1024];
10173             LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
10174             HDC           hdc      = GetDC(infoPtr->hwndEdit);
10175             HFONT         hFont, hOldFont = 0;
10176             RECT          rect;
10177             SIZE          sz;
10178             int           len;
10179
10180             len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
10181             GetWindowRect(infoPtr->hwndEdit, &rect);
10182
10183             /* Select font to get the right dimension of the string */
10184             hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
10185             if(hFont != 0)
10186             {
10187                 hOldFont = SelectObject(hdc, hFont);
10188             }
10189
10190             if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
10191             {
10192                 TEXTMETRICW textMetric;
10193
10194                 /* Add Extra spacing for the next character */
10195                 GetTextMetricsW(hdc, &textMetric);
10196                 sz.cx += (textMetric.tmMaxCharWidth * 2);
10197
10198                 SetWindowPos (
10199                     infoPtr->hwndEdit,
10200                     HWND_TOP,
10201                     0,
10202                     0,
10203                     sz.cx,
10204                     rect.bottom - rect.top,
10205                     SWP_DRAWFRAME|SWP_NOMOVE);
10206             }
10207             if(hFont != 0)
10208                 SelectObject(hdc, hOldFont);
10209
10210             ReleaseDC(hwnd, hdc);
10211
10212             break;
10213         }
10214
10215         default:
10216           return SendMessageW (GetParent (hwnd), WM_COMMAND, wParam, lParam);
10217     }
10218
10219     return 0;
10220 }
10221
10222
10223 /***
10224  * DESCRIPTION:
10225  * Subclassed edit control windproc function
10226  *
10227  * PARAMETER(S):
10228  *
10229  * RETURN:
10230  */
10231 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg,
10232         WPARAM wParam, LPARAM lParam, BOOL isW)
10233 {
10234     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
10235     EDITLABEL_ITEM *einfo = infoPtr->pedititem;
10236     static BOOL bIgnoreKillFocus = FALSE;
10237     BOOL cancel = FALSE;
10238
10239     TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
10240           hwnd, uMsg, wParam, lParam, isW);
10241
10242     switch (uMsg)
10243     {
10244         case WM_GETDLGCODE:
10245           return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
10246
10247         case WM_KILLFOCUS:
10248             if(bIgnoreKillFocus) return TRUE;
10249             break;
10250
10251         case WM_DESTROY:
10252         {
10253             WNDPROC editProc = einfo->EditWndProc;
10254             SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
10255             COMCTL32_Free(einfo);
10256             infoPtr->pedititem = NULL;
10257             return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
10258         }
10259
10260         case WM_KEYDOWN:
10261             if (VK_ESCAPE == (INT)wParam)
10262             {
10263                 cancel = TRUE;
10264                 break;
10265             }
10266             else if (VK_RETURN == (INT)wParam)
10267                 break;
10268
10269         default:
10270             return CallWindowProcT(einfo->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
10271     }
10272
10273     if (einfo->EditLblCb)
10274     {
10275         LPWSTR buffer = NULL;
10276
10277         if (!cancel)
10278         {
10279             DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
10280
10281             if (len)
10282             {
10283                 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
10284                 {
10285                     if (isW) GetWindowTextW(hwnd, buffer, len+1);
10286                     else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
10287                 }
10288             }
10289         }
10290         /* Processing LVN_ENDLABELEDIT message could kill the focus       */
10291         /* eg. Using a messagebox                                         */
10292         bIgnoreKillFocus = TRUE;
10293         einfo->EditLblCb(GetParent(hwnd), buffer, einfo->param);
10294
10295         if (buffer) COMCTL32_Free(buffer);
10296
10297         einfo->EditLblCb = NULL;
10298         bIgnoreKillFocus = FALSE;
10299     }
10300
10301     SendMessageW(hwnd, WM_CLOSE, 0, 0);
10302     return TRUE;
10303 }
10304
10305 /***
10306  * DESCRIPTION:
10307  * Subclassed edit control windproc function
10308  *
10309  * PARAMETER(S):
10310  *
10311  * RETURN:
10312  */
10313 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10314 {
10315     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
10316 }
10317
10318 /***
10319  * DESCRIPTION:
10320  * Subclassed edit control windproc function
10321  *
10322  * PARAMETER(S):
10323  *
10324  * RETURN:
10325  */
10326 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10327 {
10328     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
10329 }
10330
10331 /***
10332  * DESCRIPTION:
10333  * Creates a subclassed edit cotrol
10334  *
10335  * PARAMETER(S):
10336  *
10337  * RETURN:
10338  */
10339 HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y,
10340         INT width, INT height, HWND parent, HINSTANCE hinst,
10341         EditlblCallbackW EditLblCb, DWORD param, BOOL isW)
10342 {
10343     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(parent, 0);
10344     WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
10345     HWND hedit;
10346     SIZE sz;
10347     HDC hdc;
10348     HDC hOldFont=0;
10349     TEXTMETRICW textMetric;
10350
10351     TRACE("(text=%s, ..., isW=%d)\n", debugstr_t(text, isW), isW);
10352
10353     if (NULL == (infoPtr->pedititem = COMCTL32_Alloc(sizeof(EDITLABEL_ITEM))))
10354         return 0;
10355
10356     style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
10357     hdc = GetDC(parent);
10358
10359     /* Select the font to get appropriate metric dimensions */
10360     if(infoPtr->hFont != 0)
10361         hOldFont = SelectObject(hdc, infoPtr->hFont);
10362
10363     /*Get String Lenght in pixels */
10364     GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
10365
10366     /*Add Extra spacing for the next character */
10367     GetTextMetricsW(hdc, &textMetric);
10368     sz.cx += (textMetric.tmMaxCharWidth * 2);
10369
10370     if(infoPtr->hFont != 0)
10371         SelectObject(hdc, hOldFont);
10372
10373     ReleaseDC(parent, hdc);
10374     if (isW)
10375         hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
10376     else
10377         hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
10378
10379     if (!hedit)
10380     {
10381         COMCTL32_Free(infoPtr->pedititem);
10382         return 0;
10383     }
10384
10385     infoPtr->pedititem->param = param;
10386     infoPtr->pedititem->EditLblCb = EditLblCb;
10387     infoPtr->pedititem->EditWndProc = (WNDPROC)
10388         (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
10389                SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
10390
10391     SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);
10392
10393     return hedit;
10394 }