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