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