Spelling stuff.
[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         LISTVIEW_GetItemRect(hwnd,nItem,lprc);
6085         count = 0;
6086         while (count<(nSubItem-1))
6087         {
6088             lprc->left += LISTVIEW_GetColumnWidth(hwnd,count);
6089             count ++;
6090         }
6091
6092         lprc->right = LISTVIEW_GetColumnWidth(hwnd,(nSubItem-1)) +
6093                             lprc->left;
6094
6095     }
6096     return TRUE;    
6097 }
6098
6099
6100 /***
6101  * DESCRIPTION:
6102  * Retrieves the width of a label.
6103  * 
6104  * PARAMETER(S):
6105  * [I] HWND : window handle
6106  * 
6107  * RETURN:
6108  *   SUCCESS : string width (in pixels)
6109  *   FAILURE : zero
6110  */
6111 static INT LISTVIEW_GetLabelWidth(HWND hwnd, INT nItem)
6112 {
6113   WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6114   INT nLabelWidth = 0;
6115   LVITEMW lvItem;
6116
6117   TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
6118
6119   ZeroMemory(&lvItem, sizeof(lvItem));
6120   lvItem.mask = LVIF_TEXT;
6121   lvItem.iItem = nItem;
6122   lvItem.cchTextMax = DISP_TEXT_SIZE;
6123   lvItem.pszText = szDispText;
6124   if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
6125     nLabelWidth = ListView_GetStringWidthW(hwnd, lvItem.pszText); 
6126     
6127   return nLabelWidth;
6128 }
6129
6130 /***
6131  * DESCRIPTION:
6132  * Retrieves the spacing between listview control items.
6133  * 
6134  * PARAMETER(S):
6135  * [I] HWND : window handle
6136  * [I] BOOL : flag for small or large icon 
6137  *
6138  * RETURN:
6139  * Horizontal + vertical spacing
6140  */
6141 static LRESULT LISTVIEW_GetItemSpacing(HWND hwnd, BOOL bSmall)
6142 {
6143   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6144   LONG lResult;
6145
6146   if (bSmall == FALSE)
6147   {
6148     lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6149   }
6150   else
6151   {
6152     LONG style = GetWindowLongW(hwnd, GWL_STYLE);
6153     if ((style & LVS_TYPEMASK) == LVS_ICON)
6154       lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6155     else
6156       lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6157   }
6158   return lResult;
6159 }
6160
6161 /***
6162  * DESCRIPTION:
6163  * Retrieves the state of a listview control item.
6164  * 
6165  * PARAMETER(S):
6166  * [I] HWND : window handle
6167  * [I] INT : item index
6168  * [I] UINT : state mask
6169  * 
6170  * RETURN:
6171  * State specified by the mask.
6172  */
6173 static LRESULT LISTVIEW_GetItemState(HWND hwnd, INT nItem, UINT uMask)
6174 {
6175   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6176   LVITEMW lvItem;
6177   UINT uState = 0;
6178
6179   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6180   {
6181     ZeroMemory(&lvItem, sizeof(lvItem));
6182     lvItem.iItem = nItem;
6183     lvItem.stateMask = uMask;
6184     lvItem.mask = LVIF_STATE;
6185     if (LISTVIEW_GetItemW(hwnd, &lvItem, TRUE))
6186       uState = lvItem.state;
6187   }
6188
6189   return uState;
6190 }
6191
6192 /***
6193  * DESCRIPTION:
6194  * Retrieves the text of a listview control item or subitem. 
6195  * 
6196  * PARAMETER(S):
6197  * [I] hwnd : window handle
6198  * [I] nItem : item index
6199  * [IO] lpLVItem : item information
6200  * [I] isW :  TRUE if lpLVItem is Unicode
6201  * 
6202  * RETURN:
6203  *   SUCCESS : string length
6204  *   FAILURE : 0
6205  */
6206 static LRESULT LISTVIEW_GetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6207 {
6208   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6209   INT nLength = 0;
6210   
6211   if (lpLVItem != NULL)
6212   {
6213     if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
6214     {
6215       lpLVItem->mask = LVIF_TEXT;
6216       lpLVItem->iItem = nItem;
6217       if (LISTVIEW_GetItemT(hwnd, lpLVItem, FALSE, isW))
6218         nLength = textlenT(lpLVItem->pszText, isW);
6219     }
6220   }
6221
6222   return nLength;
6223 }
6224
6225 /***
6226  * DESCRIPTION:
6227  * Searches for an item based on properties + relationships.
6228  * 
6229  * PARAMETER(S):
6230  * [I] HWND : window handle
6231  * [I] INT : item index
6232  * [I] INT : relationship flag
6233  * 
6234  * RETURN:
6235  *   SUCCESS : item index
6236  *   FAILURE : -1
6237  */
6238 static LRESULT LISTVIEW_GetNextItem(HWND hwnd, INT nItem, UINT uFlags)
6239 {
6240   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6241   UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
6242   UINT uMask = 0;
6243   LVFINDINFOW lvFindInfo;
6244   INT nCountPerColumn;
6245   INT i;
6246   
6247   if ((nItem >= -1) && (nItem < GETITEMCOUNT(infoPtr)))
6248   { 
6249     ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6250
6251     if (uFlags & LVNI_CUT)
6252       uMask |= LVIS_CUT;
6253     
6254     if (uFlags & LVNI_DROPHILITED)
6255       uMask |= LVIS_DROPHILITED;
6256           
6257     if (uFlags & LVNI_FOCUSED)
6258       uMask |= LVIS_FOCUSED;
6259
6260     if (uFlags & LVNI_SELECTED)
6261       uMask |= LVIS_SELECTED;
6262
6263     if (uFlags & LVNI_ABOVE)
6264     {
6265       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6266       {
6267         while (nItem >= 0)
6268         {
6269           nItem--;
6270           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6271             return nItem;
6272         }
6273       }
6274       else
6275       {
6276         lvFindInfo.flags = LVFI_NEARESTXY;
6277         lvFindInfo.vkDirection = VK_UP;
6278         ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6279         while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6280         {
6281           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6282             return nItem;
6283         }
6284       }
6285     }
6286     else if (uFlags & LVNI_BELOW)
6287     {
6288       if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6289       {
6290         while (nItem < GETITEMCOUNT(infoPtr))
6291         {
6292           nItem++;
6293           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6294             return nItem;
6295         }
6296       }
6297       else
6298       {
6299         lvFindInfo.flags = LVFI_NEARESTXY;
6300         lvFindInfo.vkDirection = VK_DOWN;
6301         ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6302         while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6303         {
6304           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6305             return nItem;
6306         }
6307       }
6308     }
6309     else if (uFlags & LVNI_TOLEFT)
6310     {
6311       if (uView == LVS_LIST)
6312       {
6313         nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6314         while (nItem - nCountPerColumn >= 0)
6315         {
6316           nItem -= nCountPerColumn;
6317           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6318             return nItem;
6319         }
6320       }
6321       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6322       {
6323         lvFindInfo.flags = LVFI_NEARESTXY;
6324         lvFindInfo.vkDirection = VK_LEFT;
6325         ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6326         while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6327         {
6328           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6329             return nItem;
6330         }
6331       }
6332     }
6333     else if (uFlags & LVNI_TORIGHT)
6334     {
6335       if (uView == LVS_LIST)
6336       {
6337         nCountPerColumn = LISTVIEW_GetCountPerColumn(hwnd);
6338         while (nItem + nCountPerColumn < GETITEMCOUNT(infoPtr))
6339         {
6340           nItem += nCountPerColumn;
6341           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6342             return nItem;
6343         }
6344       }
6345       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6346       {
6347         lvFindInfo.flags = LVFI_NEARESTXY;
6348         lvFindInfo.vkDirection = VK_RIGHT;
6349         ListView_GetItemPosition(hwnd, nItem, &lvFindInfo.pt);
6350         while ((nItem = ListView_FindItemW(hwnd, nItem, &lvFindInfo)) != -1)
6351         {
6352           if ((ListView_GetItemState(hwnd, nItem, uMask) & uMask) == uMask)
6353             return nItem;
6354         }
6355       }
6356     }
6357     else
6358     {
6359       nItem++;
6360
6361       /* search by index */
6362       for (i = nItem; i < GETITEMCOUNT(infoPtr); i++)
6363       {
6364         if ((ListView_GetItemState(hwnd, i, uMask) & uMask) == uMask)
6365           return i;
6366       }
6367     }
6368   }
6369
6370   return -1;
6371 }
6372
6373 /* LISTVIEW_GetNumberOfWorkAreas */
6374
6375 /***
6376  * DESCRIPTION:
6377  * Retrieves the origin coordinates when in icon or small icon display mode.
6378  * 
6379  * PARAMETER(S):
6380  * [I] HWND : window handle
6381  * [O] LPPOINT : coordinate information
6382  * 
6383  * RETURN:
6384  *   SUCCESS : TRUE
6385  *   FAILURE : FALSE
6386  */
6387 static LRESULT LISTVIEW_GetOrigin(HWND hwnd, LPPOINT lpptOrigin)
6388 {
6389   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6390   UINT uView = lStyle & LVS_TYPEMASK;
6391   BOOL bResult = FALSE;
6392   
6393   TRACE("(hwnd=%x, lpptOrigin=%p)\n", hwnd, lpptOrigin);
6394
6395   if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6396   {
6397     SCROLLINFO scrollInfo;
6398     ZeroMemory(lpptOrigin, sizeof(POINT));
6399     ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
6400     scrollInfo.cbSize = sizeof(SCROLLINFO);
6401
6402     if (lStyle & WS_HSCROLL)
6403     {
6404       scrollInfo.fMask = SIF_POS;
6405       if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE) 
6406         lpptOrigin->x = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE; 
6407     }
6408
6409     if (lStyle & WS_VSCROLL)
6410     {
6411       scrollInfo.fMask = SIF_POS;
6412       if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
6413         lpptOrigin->y = -scrollInfo.nPos * LISTVIEW_SCROLL_DIV_SIZE;
6414     }
6415       
6416     bResult = TRUE;
6417   }
6418   
6419   return bResult;
6420 }
6421
6422 /***
6423  * DESCRIPTION:
6424  * Retrieves the number of items that are marked as selected.
6425  * 
6426  * PARAMETER(S):
6427  * [I] HWND : window handle
6428  * 
6429  * RETURN:
6430  * Number of items selected.
6431  */
6432 static LRESULT LISTVIEW_GetSelectedCount(HWND hwnd)
6433 {
6434 /* REDO THIS */
6435   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6436   INT nSelectedCount = 0;
6437   INT i;
6438
6439   for (i = 0; i < GETITEMCOUNT(infoPtr); i++)
6440   {
6441     if (ListView_GetItemState(hwnd, i, LVIS_SELECTED) & LVIS_SELECTED)
6442       nSelectedCount++;
6443   }
6444   
6445   return nSelectedCount;
6446 }
6447
6448 /***
6449  * DESCRIPTION:
6450  * Retrieves item index that marks the start of a multiple selection.
6451  * 
6452  * PARAMETER(S):
6453  * [I] HWND : window handle
6454  * 
6455  * RETURN:
6456  * Index number or -1 if there is no selection mark.
6457  */
6458 static LRESULT LISTVIEW_GetSelectionMark(HWND hwnd)
6459 {
6460   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6461
6462   return infoPtr->nSelectionMark;
6463 }
6464
6465
6466 /***
6467  * DESCRIPTION:
6468  * Retrieves the width of a string.
6469  * 
6470  * PARAMETER(S):
6471  * [I] hwnd : window handle
6472  * [I] lpszText : text string to process
6473  * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6474  * 
6475  * RETURN:
6476  *   SUCCESS : string width (in pixels)
6477  *   FAILURE : zero
6478  */
6479 static LRESULT LISTVIEW_GetStringWidthT(HWND hwnd, LPCWSTR lpszText, BOOL isW)
6480 {
6481   if (is_textT(lpszText, isW))
6482   {
6483     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6484     HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6485     HDC hdc = GetDC(hwnd);
6486     HFONT hOldFont = SelectObject(hdc, hFont);
6487     SIZE stringSize;
6488     ZeroMemory(&stringSize, sizeof(SIZE));
6489     if (isW)
6490       GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6491     else
6492       GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6493     SelectObject(hdc, hOldFont);
6494     ReleaseDC(hwnd, hdc);
6495     return stringSize.cx;
6496   }
6497   return 0;
6498 }
6499
6500 /***
6501  * DESCRIPTION:
6502  * Retrieves the text backgound color.
6503  * 
6504  * PARAMETER(S):
6505  * [I] HWND : window handle
6506  * 
6507  * RETURN:
6508  * COLORREF associated with the the background.
6509  */
6510 static LRESULT LISTVIEW_GetTextBkColor(HWND hwnd)
6511 {
6512   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6513
6514   return infoPtr->clrTextBk;
6515 }
6516
6517 /***
6518  * DESCRIPTION:
6519  * Retrieves the text color.
6520  * 
6521  * PARAMETER(S):
6522  * [I] HWND : window handle
6523  * 
6524  * RETURN:
6525  * COLORREF associated with the text.
6526  */
6527 static LRESULT LISTVIEW_GetTextColor(HWND hwnd)
6528 {
6529   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
6530
6531   return infoPtr->clrText;
6532 }
6533
6534 /***
6535  * DESCRIPTION:
6536  * Determines item if a hit or closest if not
6537  * 
6538  * PARAMETER(S):
6539  * [I] HWND : window handle
6540  * [IO] LPLV_INTHIT : hit test information
6541  * [I] subitem : fill out iSubItem.
6542  *
6543  * RETURN:
6544  *   SUCCESS : item index of hit
6545  *   FAILURE : -1
6546  */
6547 static INT LISTVIEW_SuperHitTestItem(HWND hwnd, LPLV_INTHIT lpInt, BOOL subitem)
6548 {
6549   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6550   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6551   UINT uView = lStyle & LVS_TYPEMASK;
6552   INT i,topindex,bottomindex;
6553   RECT rcItem;
6554   DWORD xterm, yterm, dist;
6555
6556   TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpInt->ht.pt.x, lpInt->ht.pt.y);
6557
6558   topindex = ListView_GetTopIndex(hwnd);
6559   if (uView == LVS_REPORT)
6560   {
6561     bottomindex = topindex + LISTVIEW_GetCountPerColumn(hwnd) + 1;  
6562     bottomindex = min(bottomindex,GETITEMCOUNT(infoPtr));
6563   }
6564   else
6565   {
6566     bottomindex = GETITEMCOUNT(infoPtr);
6567   }
6568
6569   lpInt->distance = 0x7fffffff;
6570   lpInt->iDistItem = -1;
6571
6572   for (i = topindex; i < bottomindex; i++)
6573   {
6574     rcItem.left = LVIR_BOUNDS;
6575     if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6576     {
6577       if (PtInRect(&rcItem, lpInt->ht.pt))
6578       {
6579         rcItem.left = LVIR_ICON;
6580         if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6581         {
6582           if (PtInRect(&rcItem, lpInt->ht.pt))
6583           {
6584             lpInt->ht.flags = LVHT_ONITEMICON;
6585             lpInt->ht.iItem = i;
6586             if (subitem) lpInt->ht.iSubItem = 0;
6587             return i;
6588           }
6589         }
6590       
6591         rcItem.left = LVIR_LABEL;
6592         if (LISTVIEW_GetItemRect(hwnd, i, &rcItem))
6593         {
6594           if (PtInRect(&rcItem, lpInt->ht.pt))
6595           {
6596             lpInt->ht.flags = LVHT_ONITEMLABEL;
6597             lpInt->ht.iItem = i;
6598             if (subitem) lpInt->ht.iSubItem = 0;
6599             return i;
6600           }
6601         }
6602         
6603         lpInt->ht.flags = LVHT_ONITEMSTATEICON;
6604         lpInt->ht.iItem = i;
6605         if (subitem) lpInt->ht.iSubItem = 0;
6606         return i;
6607       }
6608       else
6609       {
6610         /*
6611          * Now compute distance from point to center of boundary
6612          * box. Since we are only interested in the relative
6613          * distance, we can skip the nasty square root operation
6614          */
6615         xterm = rcItem.left + (rcItem.right - rcItem.left)/2 - lpInt->ht.pt.x;
6616         yterm = rcItem.top + (rcItem.bottom - rcItem.top)/2 - lpInt->ht.pt.y;
6617         dist = xterm * xterm + yterm * yterm;
6618         if (dist < lpInt->distance) 
6619         {
6620           lpInt->distance = dist;
6621           lpInt->iDistItem = i;
6622         }
6623       }
6624     }
6625   }
6626      
6627   lpInt->ht.flags = LVHT_NOWHERE;
6628   TRACE("no hit, closest item %d, distance %ld\n", lpInt->iDistItem, lpInt->distance);
6629
6630   return -1;
6631 }
6632
6633  /***
6634   * DESCRIPTION:
6635   * Determines which section of the item was selected (if any).
6636   *
6637   * PARAMETER(S):
6638   * [I] HWND : window handle
6639   * [IO] LPLVHITTESTINFO : hit test information
6640   * [I] subitem : fill out iSubItem.
6641   *
6642   * RETURN:
6643   *   SUCCESS : item index
6644   *   FAILURE : -1
6645   */
6646 static INT LISTVIEW_HitTestItem(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo, BOOL subitem)
6647 {
6648   INT ret;
6649   LV_INTHIT lv_inthit;
6650
6651   TRACE("(hwnd=%x, x=%ld, y=%ld)\n", hwnd, lpHitTestInfo->pt.x,
6652         lpHitTestInfo->pt.y);
6653
6654   memcpy(&lv_inthit, lpHitTestInfo, sizeof(LVHITTESTINFO));
6655   ret = LISTVIEW_SuperHitTestItem(hwnd, &lv_inthit, subitem);
6656   memcpy(lpHitTestInfo, &lv_inthit, sizeof(LVHITTESTINFO));
6657   return ret;
6658 }
6659
6660 /***
6661  * DESCRIPTION:
6662  * Determines which listview item is located at the specified position.
6663  * 
6664  * PARAMETER(S):
6665  * [I] HWND : window handle
6666  * [IO} LPLVHITTESTINFO : hit test information
6667  *
6668  * RETURN:
6669  *   SUCCESS : item index
6670  *   FAILURE : -1
6671  */
6672 static LRESULT LISTVIEW_HitTest(HWND hwnd, LPLVHITTESTINFO lpHitTestInfo)
6673 {
6674   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6675   INT nItem = -1;
6676
6677   lpHitTestInfo->flags = 0;
6678
6679   if (infoPtr->rcList.left > lpHitTestInfo->pt.x)
6680     lpHitTestInfo->flags = LVHT_TOLEFT;
6681   else if (infoPtr->rcList.right < lpHitTestInfo->pt.x) 
6682     lpHitTestInfo->flags = LVHT_TORIGHT;
6683   if (infoPtr->rcList.top > lpHitTestInfo->pt.y)
6684     lpHitTestInfo->flags |= LVHT_ABOVE;
6685   else if (infoPtr->rcList.bottom < lpHitTestInfo->pt.y) 
6686     lpHitTestInfo->flags |= LVHT_BELOW;
6687
6688   if (lpHitTestInfo->flags == 0)
6689   {
6690     /* NOTE (mm 20001022): We must not allow iSubItem to be touched, for 
6691      * an app might pass only a structure with space up to iItem!
6692      * (MS Office 97 does that for instance in the file open dialog)
6693      */
6694     nItem = LISTVIEW_HitTestItem(hwnd, lpHitTestInfo, FALSE);
6695   }
6696   
6697   return nItem;
6698 }
6699
6700 /***
6701  * DESCRIPTION:
6702  * Inserts a new column.
6703  * 
6704  * PARAMETER(S):
6705  * [I] HWND : window handle
6706  * [I] INT : column index
6707  * [I] LPLVCOLUMNW : column information
6708  *
6709  * RETURN:
6710  *   SUCCESS : new column index
6711  *   FAILURE : -1
6712  */
6713 static LRESULT LISTVIEW_InsertColumnT(HWND hwnd, INT nColumn, 
6714                                       LPLVCOLUMNW lpColumn, BOOL isW)
6715 {
6716   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6717   INT nNewColumn = -1;
6718   HDITEMW hdi;
6719
6720   TRACE("(hwnd=%x, nColumn=%d, lpColumn=%p)\n",hwnd, nColumn, lpColumn);
6721
6722   if (lpColumn != NULL) 
6723   {
6724     /* initialize memory */
6725     ZeroMemory(&hdi, sizeof(hdi));
6726
6727     if (lpColumn->mask & LVCF_FMT) 
6728     {
6729       /* format member is valid */
6730       hdi.mask |= HDI_FORMAT;
6731
6732       /* set text alignment (leftmost column must be left-aligned) */
6733       if (nColumn == 0)
6734       {
6735         hdi.fmt |= HDF_LEFT;
6736       }
6737       else
6738       {
6739         if (lpColumn->fmt & LVCFMT_LEFT)
6740         {
6741           hdi.fmt |= HDF_LEFT;
6742         }
6743         else if (lpColumn->fmt & LVCFMT_RIGHT)
6744         {
6745           hdi.fmt |= HDF_RIGHT;
6746         }
6747         else if (lpColumn->fmt & LVCFMT_CENTER)
6748         {
6749           hdi.fmt |= HDF_CENTER;
6750         }
6751       }
6752  
6753       if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6754       {
6755         hdi.fmt |= HDF_BITMAP_ON_RIGHT;
6756         /* ??? */
6757       }
6758
6759       if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6760       {
6761         /* ??? */
6762       }
6763       
6764       if (lpColumn->fmt & LVCFMT_IMAGE)
6765       {
6766         hdi.fmt |= HDF_IMAGE;
6767         hdi.iImage = I_IMAGECALLBACK;
6768       }
6769     }
6770
6771     if (lpColumn->mask & LVCF_WIDTH) 
6772     {
6773       hdi.mask |= HDI_WIDTH;
6774       if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6775       {
6776         /* make it fill the remainder of the controls width */
6777         HDITEMW hdit;
6778         RECT rcHeader;
6779         INT item_index;
6780         
6781         ZeroMemory(&hdit, sizeof(hdit));
6782   
6783         /* get the width of every item except the current one */
6784         hdit.mask = HDI_WIDTH;
6785         hdi.cxy = 0;
6786         
6787         for(item_index = 0; item_index < (nColumn - 1); item_index++) {
6788           Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdit));
6789           hdi.cxy+=hdit.cxy;
6790         }
6791
6792         /* retrieve the layout of the header */
6793         GetClientRect(hwnd, &rcHeader);
6794 /*        GetWindowRect(infoPtr->hwndHeader, &rcHeader);*/
6795         TRACE("start cxy=%d left=%d right=%d\n", hdi.cxy, rcHeader.left, rcHeader.right);
6796
6797         hdi.cxy = (rcHeader.right - rcHeader.left) - hdi.cxy;
6798       }
6799       else
6800         hdi.cxy = lpColumn->cx;
6801     }
6802   
6803     if (lpColumn->mask & LVCF_TEXT) 
6804     {
6805       hdi.mask |= HDI_TEXT | HDI_FORMAT;
6806       hdi.pszText = lpColumn->pszText;
6807       hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
6808       hdi.fmt |= HDF_STRING;
6809     }
6810   
6811     if (lpColumn->mask & LVCF_IMAGE) 
6812     {
6813       hdi.mask |= HDI_IMAGE;
6814       hdi.iImage = lpColumn->iImage;
6815     }
6816
6817     if (lpColumn->mask & LVCF_ORDER) 
6818     {
6819       hdi.mask |= HDI_ORDER;
6820       hdi.iOrder = lpColumn->iOrder;
6821     }
6822
6823     /* insert item in header control */
6824     nNewColumn = SendMessageW(infoPtr->hwndHeader, HDM_INSERTITEMT(isW),
6825                              (WPARAM)nColumn, (LPARAM)&hdi);
6826     
6827     /* Need to reset the item width when inserting a new column */
6828     infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
6829
6830     LISTVIEW_UpdateScroll(hwnd);
6831     InvalidateRect(hwnd, NULL, FALSE);
6832   }
6833
6834   return nNewColumn;
6835 }
6836
6837 /* LISTVIEW_InsertCompare:  callback routine for comparing pszText members of the LV_ITEMS
6838    in a LISTVIEW on insert.  Passed to DPA_Sort in LISTVIEW_InsertItem.
6839    This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6840    and not during the processing of a LVM_SORTITEMS message. Applications should provide
6841    their own sort proc. when sending LVM_SORTITEMS.
6842 */
6843 /* Platform SDK:
6844     (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6845         if:
6846           LVS_SORTXXX must be specified, 
6847           LVS_OWNERDRAW is not set, 
6848           <item>.pszText is not LPSTR_TEXTCALLBACK.
6849
6850     (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices 
6851     are sorted based on item text..." 
6852 */
6853 static INT WINAPI LISTVIEW_InsertCompare(  LPVOID first, LPVOID second,  LPARAM lParam)
6854 {
6855   LONG lStyle = GetWindowLongW((HWND) lParam, GWL_STYLE);
6856   LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
6857   LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
6858   INT  cmpv = lstrcmpW( lv_first->pszText, lv_second->pszText );
6859   /* if we're sorting descending, negate the return value */
6860   return (lStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6861 }
6862
6863 /***
6864  * nESCRIPTION:
6865  * Inserts a new item in the listview control.
6866  * 
6867  * PARAMETER(S):
6868  * [I] HWND : window handle
6869  * [I] LPLVITEMW : item information
6870  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6871  *
6872  * RETURN:
6873  *   SUCCESS : new item index
6874  *   FAILURE : -1
6875  */
6876 static LRESULT LISTVIEW_InsertItemT(HWND hwnd, LPLVITEMW lpLVItem, BOOL isW)
6877 {
6878   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
6879   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
6880   UINT uView = lStyle & LVS_TYPEMASK;
6881   INT nItem = -1;
6882   HDPA hdpaSubItems;
6883   INT nItemWidth = 0;
6884   LISTVIEW_ITEM *lpItem = NULL;
6885
6886   TRACE("(hwnd=%x, lpLVItem=%s, isW=%d)\n", 
6887         hwnd, debuglvitem_t(lpLVItem, isW), isW);
6888
6889   if (lStyle & LVS_OWNERDATA)
6890   {
6891     nItem = infoPtr->hdpaItems->nItemCount;
6892     infoPtr->hdpaItems->nItemCount ++;
6893     return nItem;
6894   }
6895
6896   if (lpLVItem != NULL)
6897   {
6898     /* make sure it's not a subitem; cannot insert a subitem */
6899     if (lpLVItem->iSubItem == 0)
6900     {
6901       if ( (lpItem = (LISTVIEW_ITEM *)COMCTL32_Alloc(sizeof(LISTVIEW_ITEM))) )
6902       {
6903         ZeroMemory(lpItem, sizeof(LISTVIEW_ITEM));
6904         if (LISTVIEW_InitItemT(hwnd, lpItem, lpLVItem, isW))
6905         {
6906           /* insert item in listview control data structure */
6907           if ( (hdpaSubItems = DPA_Create(8)) )
6908           {
6909             if ( (nItem = DPA_InsertPtr(hdpaSubItems, 0, lpItem)) != -1)
6910             {
6911               if ( ((lStyle & LVS_SORTASCENDING) || (lStyle & LVS_SORTDESCENDING))
6912                       && !(lStyle & LVS_OWNERDRAWFIXED)
6913                       && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText) )
6914               {
6915                 /* Insert the item in the proper sort order based on the pszText
6916                   member. See comments for LISTVIEW_InsertCompare() for greater detail */
6917                   nItem = DPA_InsertPtr( infoPtr->hdpaItems, 
6918                           GETITEMCOUNT( infoPtr ) + 1, hdpaSubItems );
6919                   DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, hwnd );
6920                   nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6921               }
6922               else
6923               {
6924                 nItem = DPA_InsertPtr(infoPtr->hdpaItems, lpLVItem->iItem, 
6925                                     hdpaSubItems);
6926               }
6927               if (nItem != -1)
6928               {
6929                 NMLISTVIEW nmlv;
6930
6931                 LISTVIEW_ShiftIndices(hwnd,nItem,1);
6932
6933                 /* manage item focus */
6934                 if (lpLVItem->mask & LVIF_STATE)
6935                 {
6936                   lpItem->state &= ~(LVIS_FOCUSED|LVIS_SELECTED);
6937                   if (lpLVItem->stateMask & LVIS_SELECTED)
6938                     LISTVIEW_SetSelection(hwnd, nItem);
6939                   else if (lpLVItem->stateMask & LVIS_FOCUSED)
6940                     LISTVIEW_SetItemFocus(hwnd, nItem);
6941                 }
6942                 
6943                 /* send LVN_INSERTITEM notification */
6944                 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6945                 nmlv.iItem = nItem;
6946                 nmlv.lParam = lpItem->lParam;
6947                 listview_notify(hwnd, LVN_INSERTITEM, &nmlv);
6948                 
6949                 if ((uView == LVS_SMALLICON) || (uView == LVS_LIST))
6950                 {
6951                   nItemWidth = LISTVIEW_CalculateWidth(hwnd, lpLVItem->iItem); 
6952                   if (nItemWidth > infoPtr->nItemWidth)
6953                     infoPtr->nItemWidth = nItemWidth;
6954                 }
6955
6956                 /* align items (set position of each item) */
6957                 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6958                 {
6959                   if (lStyle & LVS_ALIGNLEFT)
6960                     LISTVIEW_AlignLeft(hwnd);
6961                   else
6962                     LISTVIEW_AlignTop(hwnd);
6963                 }
6964                 
6965                 LISTVIEW_UpdateScroll(hwnd);
6966                 /* refresh client area */
6967                 InvalidateRect(hwnd, NULL, FALSE);
6968               }
6969             }
6970           }
6971         }
6972       }
6973     }
6974   }
6975
6976   /* free memory if unsuccessful */
6977   if ((nItem == -1) && (lpItem != NULL))
6978     COMCTL32_Free(lpItem);
6979   
6980   return nItem;
6981 }
6982
6983 /***
6984  * DESCRIPTION:
6985  * Redraws a range of items.
6986  * 
6987  * PARAMETER(S):
6988  * [I] HWND : window handle
6989  * [I] INT : first item
6990  * [I] INT : last item
6991  *
6992  * RETURN:
6993  *   SUCCESS : TRUE
6994  *   FAILURE : FALSE
6995  */
6996 static LRESULT LISTVIEW_RedrawItems(HWND hwnd, INT nFirst, INT nLast)
6997 {
6998   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); 
6999   BOOL bResult = FALSE;
7000   RECT rcItem;
7001   INT i;
7002
7003   if (nFirst <= nLast)
7004   {
7005     if ((nFirst >= 0) && (nFirst < GETITEMCOUNT(infoPtr)))
7006     {
7007       if ((nLast >= 0) && (nLast < GETITEMCOUNT(infoPtr)))
7008       {
7009         for (i = nFirst; i <= nLast; i++)
7010         {
7011           rcItem.left = LVIR_BOUNDS;
7012           LISTVIEW_GetItemRect(hwnd, i, &rcItem);
7013           InvalidateRect(hwnd, &rcItem, TRUE);
7014         }
7015       }
7016     }
7017   }
7018
7019   return bResult;
7020 }
7021
7022 /* LISTVIEW_Scroll */
7023
7024 /***
7025  * DESCRIPTION:
7026  * Sets the background color.
7027  * 
7028  * PARAMETER(S):
7029  * [I] HWND : window handle
7030  * [I] COLORREF : background color
7031  *
7032  * RETURN:
7033  *   SUCCESS : TRUE
7034  *   FAILURE : FALSE
7035  */
7036 static LRESULT LISTVIEW_SetBkColor(HWND hwnd, COLORREF clrBk)
7037 {
7038   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7039
7040   if(infoPtr->clrBk!=clrBk){
7041     infoPtr->clrBk = clrBk;
7042     InvalidateRect(hwnd, NULL, TRUE);
7043   }
7044   
7045   return TRUE;
7046 }
7047
7048 /* LISTVIEW_SetBkImage */
7049
7050 /***
7051  * DESCRIPTION:
7052  * Sets the callback mask. This mask will be used when the parent
7053  * window stores state information (some or all).
7054  * 
7055  * PARAMETER(S):
7056  * [I] HWND : window handle
7057  * [I] UINT : state mask
7058  *
7059  * RETURN:
7060  *   SUCCESS : TRUE
7061  *   FAILURE : FALSE
7062  */
7063 static BOOL LISTVIEW_SetCallbackMask(HWND hwnd, UINT uMask)
7064 {
7065   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7066
7067   infoPtr->uCallbackMask = uMask;
7068
7069   return TRUE;
7070 }
7071
7072 /***
7073  * DESCRIPTION:
7074  * Sets the attributes of a header item.
7075  * 
7076  * PARAMETER(S):
7077  * [I] HWND : window handle
7078  * [I] INT : column index
7079  * [I] LPLVCOLUMNW : column attributes
7080  * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, 
7081  *          otherwise it is in fact a LPLVCOLUMNA
7082  *
7083  * RETURN:
7084  *   SUCCESS : TRUE
7085  *   FAILURE : FALSE
7086  */
7087 static LRESULT LISTVIEW_SetColumnT(HWND hwnd, INT nColumn, 
7088                                    LPLVCOLUMNW lpColumn, BOOL isW)
7089 {
7090   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7091   BOOL bResult = FALSE;
7092   HDITEMW hdi, hdiget;
7093
7094   if ((lpColumn != NULL) && (nColumn >= 0) && 
7095       (nColumn < Header_GetItemCount(infoPtr->hwndHeader)))
7096   {
7097     /* initialize memory */
7098     ZeroMemory(&hdi, sizeof(hdi));
7099
7100     if (lpColumn->mask & LVCF_FMT) 
7101     {
7102       /* format member is valid */
7103       hdi.mask |= HDI_FORMAT;
7104
7105       /* get current format first */
7106       hdiget.mask = HDI_FORMAT;
7107       if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
7108               /* preserve HDF_STRING if present */
7109               hdi.fmt = hdiget.fmt & HDF_STRING;
7110
7111       /* set text alignment (leftmost column must be left-aligned) */
7112       if (nColumn == 0)
7113       {
7114         hdi.fmt |= HDF_LEFT;
7115       }
7116       else
7117       {
7118         if (lpColumn->fmt & LVCFMT_LEFT)
7119           hdi.fmt |= HDF_LEFT;
7120         else if (lpColumn->fmt & LVCFMT_RIGHT)
7121           hdi.fmt |= HDF_RIGHT;
7122         else if (lpColumn->fmt & LVCFMT_CENTER)
7123           hdi.fmt |= HDF_CENTER;
7124       }
7125       
7126       if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
7127         hdi.fmt |= HDF_BITMAP_ON_RIGHT;
7128
7129       if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
7130         hdi.fmt |= HDF_IMAGE;
7131       
7132       if (lpColumn->fmt & LVCFMT_IMAGE)
7133       {
7134         hdi.fmt |= HDF_IMAGE;
7135         hdi.iImage = I_IMAGECALLBACK;
7136       }
7137     }
7138
7139     if (lpColumn->mask & LVCF_WIDTH) 
7140     {
7141       hdi.mask |= HDI_WIDTH;
7142       hdi.cxy = lpColumn->cx;
7143     }
7144     
7145     if (lpColumn->mask & LVCF_TEXT) 
7146     {
7147       hdi.mask |= HDI_TEXT | HDI_FORMAT;
7148       hdi.pszText = lpColumn->pszText;
7149       hdi.cchTextMax = textlenT(lpColumn->pszText, isW);
7150       hdi.fmt |= HDF_STRING;
7151     }
7152   
7153     if (lpColumn->mask & LVCF_IMAGE) 
7154     {
7155       hdi.mask |= HDI_IMAGE;
7156       hdi.iImage = lpColumn->iImage;
7157     }
7158
7159     if (lpColumn->mask & LVCF_ORDER) 
7160     {
7161       hdi.mask |= HDI_ORDER;
7162       hdi.iOrder = lpColumn->iOrder;
7163     }
7164
7165     /* set header item attributes */
7166     if (isW)
7167       bResult = Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7168     else
7169       bResult = Header_SetItemA(infoPtr->hwndHeader, nColumn, &hdi);
7170   }
7171   
7172   return bResult;
7173 }
7174
7175 /***
7176  * DESCRIPTION:
7177  * Sets the column order array
7178  *
7179  * PARAMETERS:
7180  * [I] HWND : window handle
7181  * [I] INT : number of elements in column order array
7182  * [I] INT : pointer to column order array
7183  *
7184  * RETURN:
7185  *   SUCCESS : TRUE
7186  *   FAILURE : FALSE
7187  */
7188 static LRESULT LISTVIEW_SetColumnOrderArray(HWND hwnd, INT iCount, LPINT lpiArray)
7189 {
7190   /* LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0); */
7191
7192   FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7193
7194   if (!lpiArray)
7195     return FALSE;
7196
7197   return TRUE;
7198 }
7199
7200
7201 /***
7202  * DESCRIPTION:
7203  * Sets the width of a column
7204  *
7205  * PARAMETERS:
7206  * [I] HWND : window handle
7207  * [I] INT : column index
7208  * [I] INT : column width
7209  *
7210  * RETURN:
7211  *   SUCCESS : TRUE
7212  *   FAILURE : FALSE
7213  */
7214 static LRESULT LISTVIEW_SetColumnWidth(HWND hwnd, INT iCol, INT cx)
7215 {
7216     LISTVIEW_INFO *infoPtr;
7217     HDITEMW hdi;
7218     LRESULT lret;
7219     LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7220     UINT uView = lStyle & LVS_TYPEMASK; 
7221     HDC hdc;
7222     HFONT header_font;
7223     HFONT old_font;
7224     SIZE size;
7225     WCHAR text_buffer[DISP_TEXT_SIZE];
7226     INT header_item_count;
7227     INT item_index;
7228     INT nLabelWidth;
7229     RECT rcHeader;
7230     LVITEMW lvItem;
7231     WCHAR szDispText[DISP_TEXT_SIZE];
7232
7233     /* make sure we can get the listview info */
7234     if (!(infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0)))
7235       return (FALSE);
7236
7237     if (!infoPtr->hwndHeader) /* make sure we have a header */
7238       return (FALSE);
7239
7240     /* set column width only if in report or list mode */
7241     if ((uView != LVS_REPORT) && (uView != LVS_LIST))
7242       return (FALSE);            
7243
7244     TRACE("(hwnd=%x, iCol=%d, cx=%d\n", hwnd, iCol, cx);
7245     
7246     /* take care of invalid cx values */
7247     if((uView == LVS_REPORT) && (cx < -2))
7248       cx = LVSCW_AUTOSIZE;
7249     else if (uView == LVS_LIST && (cx < 1))
7250       return FALSE;
7251  
7252     /* resize all columns if in LVS_LIST mode */
7253     if(uView == LVS_LIST) {
7254       infoPtr->nItemWidth = cx;
7255       InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7256       return TRUE;
7257     }
7258
7259     /* autosize based on listview items width */
7260     if(cx == LVSCW_AUTOSIZE)
7261     {
7262       /* set the width of the column to the width of the widest item */
7263       if (iCol == 0 || uView == LVS_LIST)
7264       {
7265         cx = 0;
7266         for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7267         {
7268           nLabelWidth = LISTVIEW_GetLabelWidth(hwnd, item_index);
7269           cx = (nLabelWidth>cx)?nLabelWidth:cx;
7270         }
7271         if (infoPtr->himlSmall)
7272           cx += infoPtr->iconSize.cx + IMAGE_PADDING;
7273       }
7274       else
7275       {
7276         ZeroMemory(&lvItem, sizeof(lvItem));
7277         lvItem.iSubItem = iCol;
7278         lvItem.mask = LVIF_TEXT;
7279         lvItem.cchTextMax = DISP_TEXT_SIZE;
7280         lvItem.pszText = szDispText;
7281         *lvItem.pszText = '\0';
7282         cx = 0;
7283         for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7284         {
7285           lvItem.iItem = item_index;
7286           LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, TRUE);
7287           nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
7288           cx = (nLabelWidth>cx)?nLabelWidth:cx;
7289         }
7290       }
7291       cx += TRAILING_PADDING;
7292     } /* autosize based on listview header width */
7293     else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7294     {
7295       header_item_count = Header_GetItemCount(infoPtr->hwndHeader);
7296  
7297       /* if iCol is the last column make it fill the remainder of the controls width */
7298       if(iCol == (header_item_count - 1)) {
7299         /* get the width of every item except the current one */
7300         hdi.mask = HDI_WIDTH;
7301         cx = 0;
7302         
7303         for(item_index = 0; item_index < (header_item_count - 1); item_index++) {
7304           Header_GetItemW(infoPtr->hwndHeader, item_index, (LPARAM)(&hdi));
7305           cx+=hdi.cxy;
7306         }
7307  
7308         /* retrieve the layout of the header */
7309         GetWindowRect(infoPtr->hwndHeader, &rcHeader);
7310
7311         cx = (rcHeader.right - rcHeader.left) - cx;
7312       }                                  
7313       else
7314       {
7315         /* Despite what the MS docs say, if this is not the last
7316            column, then MS resizes the column to the width of the
7317            largest text string in the column, including headers
7318            and items. This is different from LVSCW_AUTOSIZE in that
7319            LVSCW_AUTOSIZE ignores the header string length.
7320            */
7321            
7322         /* retrieve header font */
7323         header_font = SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0L, 0L);
7324  
7325         /* retrieve header text */
7326         hdi.mask = HDI_TEXT;
7327         hdi.cchTextMax = sizeof(text_buffer)/sizeof(text_buffer[0]);
7328         hdi.pszText = text_buffer;             
7329     
7330         Header_GetItemW(infoPtr->hwndHeader, iCol, (LPARAM)(&hdi));
7331  
7332         /* determine the width of the text in the header */
7333         hdc = GetDC(hwnd);
7334         old_font = SelectObject(hdc, header_font); /* select the font into hdc */
7335
7336         GetTextExtentPoint32W(hdc, text_buffer, lstrlenW(text_buffer), &size);
7337  
7338         SelectObject(hdc, old_font); /* restore the old font */    
7339         ReleaseDC(hwnd, hdc);
7340  
7341         ZeroMemory(&lvItem, sizeof(lvItem));
7342         lvItem.iSubItem = iCol;
7343         lvItem.mask = LVIF_TEXT;
7344         lvItem.cchTextMax = DISP_TEXT_SIZE;
7345         lvItem.pszText = szDispText;
7346         *lvItem.pszText = '\0';
7347         cx = size.cx;
7348         for(item_index = 0; item_index < GETITEMCOUNT(infoPtr); item_index++)
7349         {
7350           lvItem.iItem = item_index;
7351           LISTVIEW_GetItemT(hwnd, &lvItem, FALSE, TRUE);
7352           nLabelWidth = LISTVIEW_GetStringWidthT(hwnd, lvItem.pszText, TRUE);
7353           nLabelWidth += TRAILING_PADDING;
7354           /* While it is possible for subitems to have icons, even MS messes
7355              up the positioning, so I suspect no applications actually use
7356              them. */
7357           if (item_index == 0 && infoPtr->himlSmall)
7358             nLabelWidth += infoPtr->iconSize.cx + IMAGE_PADDING;
7359           cx = (nLabelWidth>cx)?nLabelWidth:cx;
7360         }
7361       }
7362   }
7363
7364   /* call header to update the column change */
7365   hdi.mask = HDI_WIDTH;                          
7366
7367   hdi.cxy = cx;
7368   lret = Header_SetItemW(infoPtr->hwndHeader, (WPARAM)iCol, (LPARAM)&hdi);
7369  
7370   InvalidateRect(hwnd, NULL, TRUE); /* force redraw of the listview */
7371  
7372   return lret;
7373 }
7374
7375 /***
7376  * DESCRIPTION:
7377  * Sets the extended listview style.
7378  *
7379  * PARAMETERS:
7380  * [I] HWND  : window handle
7381  * [I] DWORD : mask
7382  * [I] DWORD : style
7383  *
7384  * RETURN:
7385  *   SUCCESS : previous style
7386  *   FAILURE : 0
7387  */
7388 static LRESULT LISTVIEW_SetExtendedListViewStyle(HWND hwnd, DWORD dwMask, DWORD dwStyle)
7389 {
7390   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7391   DWORD dwOldStyle = infoPtr->dwExStyle;
7392
7393   /* set new style */
7394   if (dwMask)
7395     infoPtr->dwExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
7396   else
7397     infoPtr->dwExStyle = dwStyle;
7398
7399   return dwOldStyle;
7400 }
7401
7402 /* LISTVIEW_SetHotCursor */
7403
7404 /***
7405  * DESCRIPTION:
7406  * Sets the hot item index.
7407  *
7408  * PARAMETERS:
7409  * [I] HWND  : window handle
7410  * [I] INT   : index
7411  *
7412  * RETURN:
7413  *   SUCCESS : previous hot item index
7414  *   FAILURE : -1 (no hot item)
7415  */
7416 static LRESULT LISTVIEW_SetHotItem(HWND hwnd, INT iIndex)
7417 {
7418   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7419   INT iOldIndex = infoPtr->nHotItem;
7420
7421   /* set new style */
7422   infoPtr->nHotItem = iIndex;
7423
7424   return iOldIndex;
7425 }
7426
7427 /***
7428  * DESCRIPTION:
7429  * Sets the amount of time the cursor must hover over an item before it is selected.
7430  *
7431  * PARAMETER(S):
7432  * [I] HWND : window handle
7433  * [I] DWORD : dwHoverTime, if -1 the hover time is set to the default
7434  *
7435  * RETURN:
7436  * Returns the previous hover time
7437  */
7438 static LRESULT LISTVIEW_SetHoverTime(HWND hwnd, DWORD dwHoverTime)
7439 {
7440   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7441   DWORD oldHoverTime = infoPtr->dwHoverTime;
7442
7443   infoPtr->dwHoverTime = dwHoverTime;
7444
7445   return oldHoverTime;
7446 }
7447
7448 /***
7449  * DESCRIPTION:
7450  * Sets spacing for icons of LVS_ICON style.
7451  *
7452  * PARAMETER(S):
7453  * [I] HWND : window handle
7454  * [I] DWORD : MAKELONG(cx, cy)
7455  *
7456  * RETURN:
7457  *   MAKELONG(oldcx, oldcy)
7458  */
7459 static LRESULT LISTVIEW_SetIconSpacing(HWND hwnd, DWORD spacing)
7460 {
7461   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongA(hwnd, 0);
7462   INT cy = HIWORD(spacing);
7463   INT cx = LOWORD(spacing);
7464   DWORD oldspacing;
7465   LONG lStyle = GetWindowLongA(hwnd, GWL_STYLE);
7466   UINT uView = lStyle & LVS_TYPEMASK;
7467
7468   oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7469   if (cx == -1) /* set to default */
7470     cx = GetSystemMetrics(SM_CXICONSPACING);
7471   if (cy == -1) /* set to default */
7472     cy = GetSystemMetrics(SM_CYICONSPACING);
7473
7474   if (cx)
7475     infoPtr->iconSpacing.cx = cx;
7476   else 
7477   {  /* if 0 then compute width */
7478     if (uView == LVS_ICON)
7479        FIXME("width computation not yet done\n");
7480        /*
7481         * Should scan each item and determine max width of
7482         * icon or label, then make that the width
7483         */
7484      else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7485        infoPtr->iconSpacing.cx = LISTVIEW_GetItemWidth(hwnd);
7486   }
7487   if (cy)
7488       infoPtr->iconSpacing.cy = cy;
7489   else 
7490   {  /* if 0 then compute height */
7491     if (uView == LVS_ICON) 
7492        infoPtr->iconSpacing.cy = infoPtr->iconSize.cy + infoPtr->ntmHeight
7493                                   + ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_OFFSET;
7494         /* FIXME.  I don't think so; I think it is based on twice the ntmHeight */
7495     else /* FIXME: unknown computation for non LVS_ICON - this is a guess */
7496        infoPtr->iconSpacing.cy = LISTVIEW_GetItemHeight(hwnd);
7497   }
7498
7499   TRACE("old=(%d,%d), new=(%ld,%ld)\n", LOWORD(oldspacing), HIWORD(oldspacing),
7500         infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7501
7502   /* these depend on the iconSpacing */
7503   infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
7504   infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7505
7506   return oldspacing;
7507 }
7508
7509 /***
7510  * DESCRIPTION:
7511  * Sets image lists.
7512  * 
7513  * PARAMETER(S):
7514  * [I] HWND : window handle
7515  * [I] INT : image list type  
7516  * [I] HIMAGELIST : image list handle
7517  *
7518  * RETURN:
7519  *   SUCCESS : old image list
7520  *   FAILURE : NULL
7521  */
7522 static HIMAGELIST LISTVIEW_SetImageList(HWND hwnd, INT nType, HIMAGELIST himl)
7523 {
7524   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7525   HIMAGELIST himlOld = 0;
7526   INT oldHeight;
7527
7528   switch (nType) 
7529   {
7530   case LVSIL_NORMAL:
7531     himlOld = infoPtr->himlNormal;
7532     infoPtr->himlNormal = himl;
7533     break;
7534
7535   case LVSIL_SMALL:
7536     himlOld = infoPtr->himlSmall;
7537     infoPtr->himlSmall = himl;
7538     break;
7539
7540   case LVSIL_STATE:
7541     himlOld = infoPtr->himlState;
7542     infoPtr->himlState = himl;
7543     ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7544     break;
7545   }
7546
7547   oldHeight = infoPtr->nItemHeight;
7548   infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
7549   if (infoPtr->nItemHeight != oldHeight)
7550     LISTVIEW_UpdateScroll(hwnd);
7551
7552   return himlOld;
7553 }
7554
7555 /***
7556  * DESCRIPTION:
7557  * Preallocates memory (does *not* set the actual count of items !)
7558  * 
7559  * PARAMETER(S):
7560  * [I] HWND : window handle
7561  * [I] INT   : item count (projected number of items to allocate)
7562  * [I] DWORD : update flags
7563  *
7564  * RETURN:
7565  *   SUCCESS : TRUE
7566  *   FAILURE : FALSE
7567  */
7568 static BOOL LISTVIEW_SetItemCount(HWND hwnd, INT nItems, DWORD dwFlags)
7569 {
7570   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7571
7572   TRACE("(hwnd=%x, nItems=%d, dwFlags=%lx)\n", hwnd, nItems, dwFlags);
7573
7574   if (GetWindowLongW(hwnd, GWL_STYLE) & LVS_OWNERDATA)
7575   {
7576       int precount,topvisible;
7577
7578       TRACE("LVS_OWNERDATA is set!\n");
7579       if (dwFlags & (LVSICF_NOINVALIDATEALL | LVSICF_NOSCROLL))
7580         FIXME("flags %s %s not implemented\n",
7581               (dwFlags & LVSICF_NOINVALIDATEALL) ? "LVSICF_NOINVALIDATEALL"
7582               : "",
7583               (dwFlags & LVSICF_NOSCROLL) ? "LVSICF_NOSCROLL" : "");
7584
7585       /*
7586        * Internally remove all the selections. 
7587        */
7588       do
7589       {
7590         LISTVIEW_SELECTION *selection;
7591         selection = DPA_GetPtr(infoPtr->hdpaSelectionRanges,0);
7592         if (selection)
7593             LISTVIEW_RemoveSelectionRange(hwnd,selection->lower,
7594                                           selection->upper);
7595       }
7596       while (infoPtr->hdpaSelectionRanges->nItemCount>0);
7597  
7598       precount = infoPtr->hdpaItems->nItemCount;
7599       topvisible = ListView_GetTopIndex(hwnd) +
7600                    LISTVIEW_GetCountPerColumn(hwnd) + 1;
7601
7602       infoPtr->hdpaItems->nItemCount = nItems;
7603
7604       infoPtr->nItemWidth = max(LISTVIEW_GetItemWidth(hwnd),
7605                                 DEFAULT_COLUMN_WIDTH);
7606
7607       LISTVIEW_UpdateSize(hwnd);
7608       LISTVIEW_UpdateScroll(hwnd);
7609
7610       if (min(precount,infoPtr->hdpaItems->nItemCount)<topvisible) 
7611         InvalidateRect(hwnd, NULL, TRUE);
7612   }
7613   else
7614   {
7615     /* According to MSDN for non-LVS_OWNERDATA this is just
7616      * a performance issue. The control allocates its internal
7617      * data structures for the number of items specified. It
7618      * cuts down on the number of memory allocations. Therefore
7619      * we will just issue a WARN here
7620      */
7621      WARN("for non-ownerdata performance option not implemented.\n");
7622   }
7623
7624   return TRUE;
7625 }
7626
7627 /***
7628  * DESCRIPTION:
7629  * Sets the position of an item.
7630  * 
7631  * PARAMETER(S):
7632  * [I] HWND : window handle
7633  * [I] INT : item index
7634  * [I] LONG : x coordinate
7635  * [I] LONG : y coordinate
7636  *
7637  * RETURN:
7638  *   SUCCESS : TRUE
7639  *   FAILURE : FALSE
7640  */
7641 static BOOL LISTVIEW_SetItemPosition(HWND hwnd, INT nItem,
7642                                      LONG nPosX, LONG nPosY)
7643 {
7644   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
7645   UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7646   UINT uView = lStyle & LVS_TYPEMASK;
7647   LISTVIEW_ITEM *lpItem;
7648   HDPA hdpaSubItems;
7649   BOOL bResult = FALSE;
7650
7651   TRACE("(hwnd=%x, nItem=%d, X=%ld, Y=%ld)\n", hwnd, nItem, nPosX, nPosY);
7652   
7653   if (lStyle & LVS_OWNERDATA)
7654     return FALSE;
7655
7656   if ((nItem >= 0) || (nItem < GETITEMCOUNT(infoPtr)))
7657   {
7658     if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
7659     {
7660       if ( (hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem)) )
7661       {
7662         if ( (lpItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0)) )
7663         {
7664           POINT orig;
7665           bResult = TRUE;
7666           orig = lpItem->ptPosition;
7667           if ((nPosX == -1) && (nPosY == -1)) 
7668           {
7669             /* This point value seems to be an undocumented feature. The
7670              * best guess is that it means either at the origin, or at
7671              * the true beginning of the list. I will assume the origin.
7672              */
7673             POINT pt1;
7674             if (!LISTVIEW_GetOrigin(hwnd, &pt1)) 
7675             {
7676               pt1.x = 0;
7677               pt1.y = 0;
7678             }
7679             nPosX = pt1.x;
7680             nPosY = pt1.y;
7681             if (uView == LVS_ICON) 
7682             {
7683               nPosX += (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7684               nPosY += ICON_TOP_PADDING;
7685             }
7686             TRACE("requested special (-1,-1), set to origin (%ld,%ld)\n",
7687                   nPosX, nPosY);
7688           }
7689
7690           lpItem->ptPosition.x = nPosX;
7691           lpItem->ptPosition.y = nPosY;
7692           if (uView == LVS_ICON)
7693           {
7694             lpItem->ptPosition.y -= ICON_TOP_PADDING;
7695               lpItem->ptPosition.x -= (infoPtr->iconSpacing.cx - infoPtr->iconSize.cx) / 2;
7696               if ((lpItem->ptPosition.y < 0) || (lpItem->ptPosition.x < 0)) 
7697               {
7698                   FIXME("failed orig (%ld,%ld), intent (%ld,%ld), is (%ld, %ld), setting neg to 0\n",
7699                         orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7700  
7701                   /*
7702                   if (lpItem->ptPosition.x < 0) lpItem->ptPosition.x = 0;
7703                   if (lpItem->ptPosition.y < 0) lpItem->ptPosition.y = 0;
7704                   */
7705               }
7706               else 
7707               {
7708                   TRACE("orig (%ld,%ld), intent (%ld,%ld), is (%ld,%ld)\n",
7709                         orig.x, orig.y, nPosX, nPosY, lpItem->ptPosition.x, lpItem->ptPosition.y);
7710               }
7711           }
7712         }
7713       }
7714     }
7715   }
7716
7717   return bResult;
7718 }
7719
7720 /***
7721  * DESCRIPTION:
7722  * Sets the state of one or many items.
7723  * 
7724  * PARAMETER(S):
7725  * [I] HWND : window handle
7726  * [I]INT : item index
7727  * [I] LPLVITEM : item or subitem info
7728  *
7729  * RETURN:
7730  *   SUCCESS : TRUE
7731  *   FAILURE : FALSE
7732  */
7733 static LRESULT LISTVIEW_SetItemState(HWND hwnd, INT nItem, LPLVITEMW lpLVItem)
7734 {
7735   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7736   BOOL bResult = TRUE;
7737   LVITEMW lvItem;
7738
7739   TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s)\n", 
7740         hwnd, nItem, debuglvitem_t(lpLVItem, TRUE));
7741
7742   ZeroMemory(&lvItem, sizeof(lvItem));
7743   lvItem.mask = LVIF_STATE;
7744   lvItem.state = lpLVItem->state;
7745   lvItem.stateMask = lpLVItem->stateMask ;
7746   lvItem.iItem = nItem;
7747
7748   if (nItem == -1)
7749   {
7750     /* apply to all items */
7751     for (lvItem.iItem = 0; lvItem.iItem < GETITEMCOUNT(infoPtr); lvItem.iItem++)
7752       if (!ListView_SetItemW(hwnd, &lvItem)) bResult = FALSE;
7753   }
7754   else
7755     bResult = ListView_SetItemW(hwnd, &lvItem);
7756
7757   return bResult;
7758 }
7759
7760 /***
7761  * DESCRIPTION:
7762  * Sets the text of an item or subitem.
7763  * 
7764  * PARAMETER(S):
7765  * [I] hwnd : window handle
7766  * [I] nItem : item index
7767  * [I] lpLVItem : item or subitem info
7768  * [I] isW : TRUE if input is Unicode
7769  *
7770  * RETURN:
7771  *   SUCCESS : TRUE
7772  *   FAILURE : FALSE
7773  */
7774 static BOOL LISTVIEW_SetItemTextT(HWND hwnd, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
7775 {
7776   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7777   BOOL bResult = FALSE;
7778   LVITEMW lvItem;
7779
7780   TRACE("(hwnd=%x, nItem=%d, lpLVItem=%s, isW=%d)\n", 
7781         hwnd, nItem, debuglvitem_t(lpLVItem, isW), isW);
7782   
7783   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7784   {
7785     ZeroMemory(&lvItem, sizeof(LVITEMW));
7786     lvItem.mask = LVIF_TEXT;
7787     lvItem.pszText = lpLVItem->pszText;
7788     lvItem.iItem = nItem;
7789     lvItem.iSubItem = lpLVItem->iSubItem;
7790     if(isW) bResult = ListView_SetItemW(hwnd, &lvItem);
7791     else    bResult = ListView_SetItemA(hwnd, &lvItem);
7792   }
7793   
7794   return bResult;
7795 }
7796
7797 /***
7798  * DESCRIPTION:
7799  * Set item index that marks the start of a multiple selection.
7800  *
7801  * PARAMETER(S):
7802  * [I] HWND : window handle
7803  * [I] INT  : index
7804  *
7805  * RETURN:
7806  * Index number or -1 if there is no selection mark.
7807  */
7808 static LRESULT LISTVIEW_SetSelectionMark(HWND hwnd, INT nIndex)
7809 {
7810   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7811   INT nOldIndex = infoPtr->nSelectionMark;
7812
7813   TRACE("(hwnd=%x, nIndex=%d)\n", hwnd, nIndex);
7814
7815   infoPtr->nSelectionMark = nIndex;
7816
7817   return nOldIndex;
7818 }
7819
7820 /***
7821  * DESCRIPTION:
7822  * Sets the text background color.
7823  * 
7824  * PARAMETER(S):
7825  * [I] HWND : window handle
7826  * [I] COLORREF : text background color
7827  *
7828  * RETURN:
7829  *   SUCCESS : TRUE
7830  *   FAILURE : FALSE
7831  */
7832 static LRESULT LISTVIEW_SetTextBkColor(HWND hwnd, COLORREF clrTextBk)
7833 {
7834   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7835
7836   TRACE("(hwnd=%x, clrTextBk=%lx)\n", hwnd, clrTextBk);
7837   
7838   infoPtr->clrTextBk = clrTextBk;
7839   InvalidateRect(hwnd, NULL, TRUE);
7840
7841   return TRUE;
7842 }
7843
7844 /***
7845  * DESCRIPTION:
7846  * Sets the text foreground color.
7847  * 
7848  * PARAMETER(S):
7849  * [I] HWND : window handle
7850  * [I] COLORREF : text color 
7851  *
7852  * RETURN:
7853  *   SUCCESS : TRUE
7854  *   FAILURE : FALSE
7855  */
7856 static LRESULT LISTVIEW_SetTextColor (HWND hwnd, COLORREF clrText)
7857 {
7858   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7859
7860   TRACE("(hwnd=%x, clrText=%lx)\n", hwnd, clrText);
7861
7862   infoPtr->clrText = clrText;
7863   InvalidateRect(hwnd, NULL, TRUE);
7864
7865   return TRUE;
7866 }
7867
7868 /* LISTVIEW_SetToolTips */
7869 /* LISTVIEW_SetUnicodeFormat */
7870 /* LISTVIEW_SetWorkAreas */
7871
7872 /***
7873  * DESCRIPTION:
7874  * Callback internally used by LISTVIEW_SortItems()
7875  * 
7876  * PARAMETER(S):
7877  * [I] LPVOID : first LISTVIEW_ITEM to compare
7878  * [I] LPVOID : second LISTVIEW_ITEM to compare
7879  * [I] LPARAM : HWND of control
7880  *
7881  * RETURN:
7882  *   if first comes before second : negative
7883  *   if first comes after second : positive
7884  *   if first and second are equivalent : zero
7885  */
7886 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7887 {
7888   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW((HWND)lParam, 0);
7889   LISTVIEW_ITEM* lv_first = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)first, 0 );
7890   LISTVIEW_ITEM* lv_second = (LISTVIEW_ITEM*) DPA_GetPtr( (HDPA)second, 0 );
7891
7892   /* Forward the call to the client defined callback */
7893   return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7894 }
7895
7896 /***
7897  * DESCRIPTION:
7898  * Sorts the listview items.
7899  * 
7900  * PARAMETER(S):
7901  * [I] HWND : window handle
7902  * [I] WPARAM : application-defined value
7903  * [I] LPARAM : pointer to comparision callback
7904  *
7905  * RETURN:
7906  *   SUCCESS : TRUE
7907  *   FAILURE : FALSE
7908  */
7909 static LRESULT LISTVIEW_SortItems(HWND hwnd, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7910 {
7911     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7912     UINT lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7913     HDPA hdpaSubItems=NULL;
7914     LISTVIEW_ITEM *pLVItem=NULL;
7915     LPVOID selectionMarkItem;
7916     int nCount, i;
7917
7918     TRACE("(hwnd=%x, pfnCompare=%p, lParamSort=%lx)\n", hwnd, pfnCompare, lParamSort);
7919     
7920     if (lStyle & LVS_OWNERDATA) return FALSE;
7921
7922     if (!infoPtr || !infoPtr->hdpaItems) return FALSE;
7923    
7924     nCount = GETITEMCOUNT(infoPtr);
7925     /* if there are 0 or 1 items, there is no need to sort */
7926     if (nCount < 2)
7927         return TRUE;
7928
7929     infoPtr->pfnCompare = pfnCompare;
7930     infoPtr->lParamSort = lParamSort;
7931     DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, hwnd);
7932
7933     /* Adjust selections and indices so that they are the way they should 
7934      * be after the sort (otherwise, the list items move around, but 
7935      * whatever is at the item's previous original position will be 
7936      * selected instead)
7937      */
7938     selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7939     for (i=0; i < nCount; i++)
7940     {
7941        hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7942        pLVItem = (LISTVIEW_ITEM *)DPA_GetPtr(hdpaSubItems, 0);
7943
7944        if (pLVItem->state & LVIS_SELECTED)
7945           LISTVIEW_AddSelectionRange(hwnd, i, i);
7946        else
7947           LISTVIEW_RemoveSelectionRange(hwnd, i, i);
7948        if (pLVItem->state & LVIS_FOCUSED)
7949           infoPtr->nFocusedItem=i;
7950     }
7951     if (selectionMarkItem != NULL)
7952        infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7953     /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7954
7955     /* align the items */
7956     LISTVIEW_AlignTop(hwnd);
7957
7958     /* refresh the display */
7959     InvalidateRect(hwnd, NULL, TRUE);
7960
7961     return TRUE;
7962 }
7963
7964 /* LISTVIEW_SubItemHitTest */
7965
7966 /***
7967  * DESCRIPTION:
7968  * Updates an items or rearranges the listview control.
7969  * 
7970  * PARAMETER(S):
7971  * [I] HWND : window handle
7972  * [I] INT : item index
7973  *
7974  * RETURN:
7975  *   SUCCESS : TRUE
7976  *   FAILURE : FALSE
7977  */
7978 static LRESULT LISTVIEW_Update(HWND hwnd, INT nItem)
7979 {
7980   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
7981   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
7982   BOOL bResult = FALSE;
7983   RECT rc;
7984
7985   TRACE("(hwnd=%x, nItem=%d)\n", hwnd, nItem);
7986   
7987   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
7988   {
7989     bResult = TRUE;
7990
7991     /* rearrange with default alignment style */
7992     if ((lStyle & LVS_AUTOARRANGE) && (((lStyle & LVS_TYPEMASK) == LVS_ICON) ||
7993         ((lStyle & LVS_TYPEMASK)  == LVS_SMALLICON)))
7994     {
7995       ListView_Arrange(hwnd, 0);
7996     }
7997     else
7998     {
7999       /* get item bounding rectangle */
8000       ListView_GetItemRect(hwnd, nItem, &rc, LVIR_BOUNDS);
8001       InvalidateRect(hwnd, &rc, TRUE);
8002     }
8003   }
8004
8005   return bResult;
8006 }
8007
8008 /***
8009  * DESCRIPTION:
8010  * Creates the listview control.
8011  * 
8012  * PARAMETER(S):
8013  * [I] HWND : window handle
8014  *
8015  * RETURN:
8016  * Zero
8017  */
8018 static LRESULT LISTVIEW_Create(HWND hwnd, LPCREATESTRUCTW lpcs)
8019 {
8020   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8021   UINT uView = lpcs->style & LVS_TYPEMASK;
8022   LOGFONTW logFont;
8023
8024   TRACE("(hwnd=%x, lpcs=%p)\n", hwnd, lpcs);
8025
8026   /* initialize info pointer */
8027   ZeroMemory(infoPtr, sizeof(LISTVIEW_INFO));
8028
8029   /* determine the type of structures to use */
8030   infoPtr->notifyFormat = SendMessageW(GetParent(hwnd), WM_NOTIFYFORMAT, 
8031                                        (WPARAM)hwnd, (LPARAM)NF_QUERY);
8032   
8033   /* initialize color information  */
8034   infoPtr->clrBk = GetSysColor(COLOR_WINDOW);
8035   infoPtr->clrText = GetSysColor(COLOR_WINDOWTEXT);
8036   infoPtr->clrTextBk = CLR_DEFAULT;
8037
8038   /* set default values */
8039   infoPtr->hwndSelf = hwnd;
8040   infoPtr->uCallbackMask = 0;
8041   infoPtr->nFocusedItem = -1;
8042   infoPtr->nSelectionMark = -1;
8043   infoPtr->nHotItem = -1;
8044   infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
8045   infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
8046   ZeroMemory(&infoPtr->rcList, sizeof(RECT));
8047   infoPtr->hwndEdit = 0;
8048   infoPtr->pedititem = NULL;
8049   infoPtr->nEditLabelItem = -1;
8050   infoPtr->bIsDrawing = FALSE;
8051
8052   /* get default font (icon title) */
8053   SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
8054   infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
8055   infoPtr->hFont = infoPtr->hDefaultFont;
8056   LISTVIEW_SaveTextMetrics(hwnd);
8057   
8058   /* create header */
8059   infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, (LPCWSTR)NULL, 
8060     WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS), 
8061     0, 0, 0, 0, hwnd, (HMENU)0, 
8062     lpcs->hInstance, NULL);
8063
8064   /* set header unicode format */
8065   SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT,(WPARAM)TRUE,(LPARAM)NULL);
8066
8067   /* set header font */
8068   SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, 
8069                (LPARAM)TRUE);
8070   
8071   if (uView == LVS_ICON)
8072   {
8073     infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
8074     infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
8075   }
8076   else if (uView == LVS_REPORT)
8077   {
8078     if (!(LVS_NOCOLUMNHEADER & lpcs->style))
8079     {
8080       ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8081     }
8082     else
8083     {
8084       /* set HDS_HIDDEN flag to hide the header bar */
8085       SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, 
8086                     GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
8087     }
8088       
8089
8090     infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8091     infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8092   }
8093   else
8094   {
8095     infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
8096     infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
8097   }
8098
8099   /* display unsupported listview window styles */
8100   LISTVIEW_UnsupportedStyles(lpcs->style);
8101
8102   /* allocate memory for the data structure */
8103   infoPtr->hdpaItems = DPA_Create(10);
8104
8105   /* allocate memory for the selection ranges */
8106   infoPtr->hdpaSelectionRanges = DPA_Create(10);
8107
8108   /* initialize size of items */
8109   infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8110   infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
8111
8112   /* initialize the hover time to -1(indicating the default system hover time) */
8113   infoPtr->dwHoverTime = -1;  
8114
8115   return 0;
8116 }
8117
8118 /***
8119  * DESCRIPTION:
8120  * Erases the background of the listview control.
8121  * 
8122  * PARAMETER(S):
8123  * [I] HWND : window handle
8124  * [I] WPARAM : device context handle
8125  * [I] LPARAM : not used
8126  * 
8127  * RETURN:
8128  *   SUCCESS : TRUE
8129  *   FAILURE : FALSE
8130  */
8131 static LRESULT LISTVIEW_EraseBackground(HWND hwnd, WPARAM wParam, 
8132                                         LPARAM lParam)
8133 {
8134   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8135   BOOL bResult;
8136
8137   TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
8138   
8139   if (infoPtr->clrBk == CLR_NONE) 
8140   {
8141     bResult = SendMessageW(GetParent(hwnd), WM_ERASEBKGND, wParam, lParam);
8142   }
8143   else 
8144   {
8145     RECT rc;
8146     HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
8147     GetClientRect(hwnd, &rc);
8148     FillRect((HDC)wParam, &rc, hBrush);
8149     DeleteObject(hBrush);
8150     bResult = TRUE;
8151   }
8152
8153   return bResult;
8154 }
8155
8156
8157 static void LISTVIEW_FillBackground(HWND hwnd, HDC hdc, LPRECT rc)
8158 {
8159   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8160
8161   TRACE("(hwnd=%x, hdc=%x, rc=%p)\n", hwnd, hdc, rc);
8162
8163   if (infoPtr->clrBk != CLR_NONE) 
8164   {
8165     HBRUSH hBrush = CreateSolidBrush(infoPtr->clrBk);
8166     FillRect(hdc, rc, hBrush);
8167     DeleteObject(hBrush);
8168   }
8169 }
8170
8171 /***
8172  * DESCRIPTION:
8173  * Retrieves the listview control font.
8174  * 
8175  * PARAMETER(S):
8176  * [I] HWND : window handle
8177  *
8178  * RETURN:
8179  * Font handle.
8180  */
8181 static LRESULT LISTVIEW_GetFont(HWND hwnd)
8182 {
8183   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8184
8185   TRACE("(hwnd=%x)\n", hwnd);
8186
8187   return infoPtr->hFont;
8188 }
8189
8190 /***
8191  * DESCRIPTION:
8192  * Performs vertical scrolling.
8193  * 
8194  * PARAMETER(S):
8195  * [I] HWND : window handle
8196  * [I] INT : scroll code
8197  * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION 
8198  *             or SB_THUMBTRACK.
8199  * [I] HWND : scrollbar control window handle
8200  *
8201  * RETURN:
8202  * Zero
8203  */
8204 static LRESULT LISTVIEW_VScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
8205                                 HWND hScrollWnd)
8206 {
8207   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8208   SCROLLINFO scrollInfo;
8209
8210   TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n", 
8211         hwnd, nScrollCode, nCurrentPos, hScrollWnd);
8212
8213   SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8214
8215   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8216   scrollInfo.cbSize = sizeof(SCROLLINFO);
8217   scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8218
8219   if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8220   {
8221     INT nOldScrollPos = scrollInfo.nPos;
8222     switch (nScrollCode)
8223     {
8224     case SB_LINEUP:
8225       if (scrollInfo.nPos > scrollInfo.nMin)
8226         scrollInfo.nPos--;
8227     break;
8228     
8229     case SB_LINEDOWN:
8230       if (scrollInfo.nPos < scrollInfo.nMax)
8231         scrollInfo.nPos++;
8232       break;
8233       
8234     case SB_PAGEUP:
8235       if (scrollInfo.nPos > scrollInfo.nMin)
8236       {
8237         if (scrollInfo.nPos >= scrollInfo.nPage)
8238           scrollInfo.nPos -= scrollInfo.nPage;
8239         else
8240           scrollInfo.nPos = scrollInfo.nMin;
8241       }
8242       break;
8243       
8244     case SB_PAGEDOWN:
8245       if (scrollInfo.nPos < scrollInfo.nMax)
8246       {
8247         if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8248           scrollInfo.nPos += scrollInfo.nPage;
8249         else
8250           scrollInfo.nPos = scrollInfo.nMax;
8251       }
8252       break;
8253
8254     case SB_THUMBPOSITION:
8255     case SB_THUMBTRACK:
8256         scrollInfo.nPos = nCurrentPos;
8257         if (scrollInfo.nPos > scrollInfo.nMax)
8258             scrollInfo.nPos=scrollInfo.nMax;
8259
8260         if (scrollInfo.nPos < scrollInfo.nMin)
8261             scrollInfo.nPos=scrollInfo.nMin;
8262
8263       break;
8264     }
8265
8266     if (nOldScrollPos != scrollInfo.nPos)
8267     {
8268       scrollInfo.fMask = SIF_POS;
8269       SetScrollInfo(hwnd, SB_VERT, &scrollInfo, TRUE);
8270       if (IsWindowVisible(infoPtr->hwndHeader))
8271       {
8272         RECT rListview, rcHeader, rDest;
8273         GetClientRect(hwnd, &rListview);
8274         GetWindowRect(infoPtr->hwndHeader, &rcHeader);
8275         MapWindowPoints((HWND) NULL, hwnd, (LPPOINT) &rcHeader, 2);
8276         SubtractRect(&rDest, &rListview, &rcHeader);
8277         InvalidateRect(hwnd, &rDest, TRUE);
8278       }
8279       else
8280         InvalidateRect(hwnd, NULL, TRUE);
8281     }
8282   }
8283     
8284   return 0;
8285 }
8286
8287 /***
8288  * DESCRIPTION:
8289  * Performs horizontal scrolling.
8290  * 
8291  * PARAMETER(S):
8292  * [I] HWND : window handle
8293  * [I] INT : scroll code
8294  * [I] SHORT : current scroll position if scroll code is SB_THUMBPOSITION 
8295  *             or SB_THUMBTRACK.
8296  * [I] HWND : scrollbar control window handle
8297  *
8298  * RETURN:
8299  * Zero
8300  */
8301 static LRESULT LISTVIEW_HScroll(HWND hwnd, INT nScrollCode, SHORT nCurrentPos,
8302                                 HWND hScrollWnd)
8303 {
8304   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8305   SCROLLINFO scrollInfo;
8306
8307   TRACE("(hwnd=%x, nScrollCode=%d, nCurrentPos=%d, hScrollWnd=%x)\n", 
8308         hwnd, nScrollCode, nCurrentPos, hScrollWnd);
8309   
8310   SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8311
8312   ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8313   scrollInfo.cbSize = sizeof(SCROLLINFO);
8314   scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
8315  
8316   if (GetScrollInfo(hwnd, SB_HORZ, &scrollInfo) != FALSE)
8317   {
8318     INT nOldScrollPos = scrollInfo.nPos;
8319
8320     switch (nScrollCode)
8321     {
8322     case SB_LINELEFT:
8323       if (scrollInfo.nPos > scrollInfo.nMin)
8324         scrollInfo.nPos--;
8325       break;
8326     
8327     case SB_LINERIGHT:
8328       if (scrollInfo.nPos < scrollInfo.nMax)
8329         scrollInfo.nPos++;
8330       break;
8331       
8332     case SB_PAGELEFT:
8333       if (scrollInfo.nPos > scrollInfo.nMin)
8334       {
8335         if (scrollInfo.nPos >= scrollInfo.nPage)
8336           scrollInfo.nPos -= scrollInfo.nPage;
8337         else
8338           scrollInfo.nPos = scrollInfo.nMin;
8339       }
8340       break;
8341       
8342     case SB_PAGERIGHT:
8343       if (scrollInfo.nPos < scrollInfo.nMax)
8344       {
8345         if (scrollInfo.nPos <= scrollInfo.nMax - scrollInfo.nPage)
8346           scrollInfo.nPos += scrollInfo.nPage;
8347         else
8348           scrollInfo.nPos = scrollInfo.nMax;
8349       }
8350       break;
8351
8352     case SB_THUMBPOSITION:
8353     case SB_THUMBTRACK:
8354         scrollInfo.nPos = nCurrentPos;
8355
8356         if (scrollInfo.nPos > scrollInfo.nMax)
8357             scrollInfo.nPos=scrollInfo.nMax;
8358
8359         if (scrollInfo.nPos < scrollInfo.nMin)
8360             scrollInfo.nPos=scrollInfo.nMin;
8361       break;
8362     }
8363
8364     if (nOldScrollPos != scrollInfo.nPos)
8365     {
8366       UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8367       scrollInfo.fMask = SIF_POS;
8368       SetScrollInfo(hwnd, SB_HORZ, &scrollInfo, TRUE);
8369       if(uView == LVS_REPORT)
8370       {
8371           scrollInfo.fMask = SIF_POS;
8372           GetScrollInfo(hwnd, SB_HORZ, &scrollInfo);
8373           LISTVIEW_UpdateHeaderSize(hwnd, scrollInfo.nPos);
8374       }
8375       InvalidateRect(hwnd, NULL, TRUE);
8376     }
8377   }
8378     
8379   return 0;
8380 }
8381
8382 static LRESULT LISTVIEW_MouseWheel(HWND hwnd, INT wheelDelta)
8383 {
8384     UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8385     INT gcWheelDelta = 0;
8386     UINT pulScrollLines = 3;
8387     SCROLLINFO scrollInfo;
8388
8389     TRACE("(hwnd=%x, wheelDelta=%d)\n", hwnd, wheelDelta);
8390
8391     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8392     gcWheelDelta -= wheelDelta;
8393
8394     ZeroMemory(&scrollInfo, sizeof(SCROLLINFO));
8395     scrollInfo.cbSize = sizeof(SCROLLINFO);
8396     scrollInfo.fMask = SIF_POS | SIF_RANGE;
8397
8398     switch(uView)
8399     {
8400     case LVS_ICON:
8401     case LVS_SMALLICON:
8402        /*
8403         *  listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8404         *  should be fixed in the future.
8405         */
8406         if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8407             LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + (gcWheelDelta < 0) ? 37 : -37, 0);
8408         break;
8409
8410     case LVS_REPORT:
8411         if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8412         {
8413             if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) != FALSE)
8414             {
8415                 int cLineScroll = min(LISTVIEW_GetCountPerColumn(hwnd), pulScrollLines);
8416                 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8417                 LISTVIEW_VScroll(hwnd, SB_THUMBPOSITION, scrollInfo.nPos + cLineScroll, 0);
8418             }
8419         }
8420         break;
8421
8422     case LVS_LIST:
8423         LISTVIEW_HScroll(hwnd, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8424         break;
8425     }
8426     return 0;
8427 }
8428
8429 /***
8430  * DESCRIPTION:
8431  * ??? 
8432  * 
8433  * PARAMETER(S):
8434  * [I] HWND : window handle
8435  * [I] INT : virtual key 
8436  * [I] LONG : key data
8437  *
8438  * RETURN:
8439  * Zero
8440  */
8441 static LRESULT LISTVIEW_KeyDown(HWND hwnd, INT nVirtualKey, LONG lKeyData)
8442 {
8443   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8444   UINT uView =  GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8445   INT nItem = -1;
8446   NMLVKEYDOWN nmKeyDown; 
8447
8448   TRACE("(hwnd=%x, nVirtualKey=%d, lKeyData=%ld)\n", hwnd, nVirtualKey, lKeyData);
8449
8450   /* send LVN_KEYDOWN notification */
8451   nmKeyDown.wVKey = nVirtualKey; 
8452   nmKeyDown.flags = 0;
8453   notify(hwnd, LVN_KEYDOWN, &nmKeyDown.hdr);
8454   
8455   switch (nVirtualKey)
8456   {
8457   case VK_RETURN:
8458     if ((GETITEMCOUNT(infoPtr) > 0) && (infoPtr->nFocusedItem != -1))
8459     {
8460       hdr_notify(hwnd, NM_RETURN);        /* NM_RETURN notification */
8461       hdr_notify(hwnd, LVN_ITEMACTIVATE); /* LVN_ITEMACTIVATE notification */
8462     }
8463     break;
8464
8465   case VK_HOME:
8466     if (GETITEMCOUNT(infoPtr) > 0)
8467       nItem = 0;
8468     break;
8469
8470   case VK_END:
8471     if (GETITEMCOUNT(infoPtr) > 0)
8472       nItem = GETITEMCOUNT(infoPtr) - 1;
8473     break;
8474
8475   case VK_LEFT:
8476     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TOLEFT);
8477     break;
8478
8479   case VK_UP:
8480     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_ABOVE);
8481     break;
8482     
8483   case VK_RIGHT:
8484     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_TORIGHT);
8485     break;
8486
8487   case VK_DOWN:
8488     nItem = ListView_GetNextItem(hwnd, infoPtr->nFocusedItem, LVNI_BELOW);
8489     break;
8490
8491   case VK_PRIOR:
8492     if (uView == LVS_REPORT)
8493       nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd);
8494     else
8495       nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(hwnd) 
8496                                     * LISTVIEW_GetCountPerRow(hwnd);
8497     if(nItem < 0) nItem = 0;
8498     break;
8499
8500   case VK_NEXT:
8501     if (uView == LVS_REPORT)
8502       nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd);
8503     else
8504       nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(hwnd)
8505                                     * LISTVIEW_GetCountPerRow(hwnd);
8506     if(nItem >= GETITEMCOUNT(infoPtr)) nItem = GETITEMCOUNT(infoPtr) - 1;
8507     break;
8508   }
8509
8510   if ((nItem != -1) && (nItem != infoPtr->nFocusedItem))
8511   {
8512     if (LISTVIEW_KeySelection(hwnd, nItem))
8513       UpdateWindow(hwnd); /* update client area */
8514   }
8515
8516   return 0;
8517 }
8518
8519 /***
8520  * DESCRIPTION:
8521  * Kills the focus.
8522  * 
8523  * PARAMETER(S):
8524  * [I] HWND : window handle
8525  *
8526  * RETURN:
8527  * Zero
8528  */
8529 static LRESULT LISTVIEW_KillFocus(HWND hwnd)
8530 {
8531   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO*)GetWindowLongW(hwnd, 0);
8532   UINT uView =  GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
8533   INT i,nTop,nBottom;
8534  
8535   TRACE("(hwnd=%x)\n", hwnd);
8536
8537   /* send NM_KILLFOCUS notification */
8538   hdr_notify(hwnd, NM_KILLFOCUS);
8539
8540   /* set window focus flag */
8541   infoPtr->bFocus = FALSE;
8542
8543   /* NEED drawing optimization ; redraw the selected items */
8544   if (uView & LVS_REPORT)
8545   { 
8546     nTop = LISTVIEW_GetTopIndex(hwnd);
8547     nBottom = nTop + 
8548               LISTVIEW_GetCountPerColumn(hwnd) + 1;
8549   }
8550   else
8551   {
8552     nTop = 0;
8553     nBottom = GETITEMCOUNT(infoPtr);
8554   }
8555   for (i = nTop; i<nBottom; i++)
8556   {
8557     if (LISTVIEW_IsSelected(hwnd,i))
8558     {
8559       RECT rcItem;
8560       rcItem.left = LVIR_BOUNDS;
8561       LISTVIEW_GetItemRect(hwnd, i, &rcItem);
8562       InvalidateRect(hwnd, &rcItem, FALSE);
8563     }
8564   }
8565
8566   return 0;
8567 }
8568
8569 /***
8570  * DESCRIPTION:
8571  * Processes double click messages (left mouse button).
8572  * 
8573  * PARAMETER(S):
8574  * [I] HWND : window handle
8575  * [I] WORD : key flag
8576  * [I] WORD : x coordinate
8577  * [I] WORD : y coordinate
8578  *
8579  * RETURN:
8580  * Zero
8581  */
8582 static LRESULT LISTVIEW_LButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX, 
8583                                       WORD wPosY)
8584 {
8585   LVHITTESTINFO htInfo;
8586   NMLISTVIEW nmlv;
8587
8588   TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8589
8590   htInfo.pt.x = wPosX;
8591   htInfo.pt.y = wPosY;
8592
8593   /* send NM_DBLCLK notification */
8594   ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8595   if (LISTVIEW_HitTestItem(hwnd, &htInfo, TRUE) != -1)
8596   {
8597     nmlv.iItem = htInfo.iItem;
8598     nmlv.iSubItem = htInfo.iSubItem;
8599   }
8600   else
8601   {
8602     nmlv.iItem = -1;
8603     nmlv.iSubItem = 0;
8604   }  
8605   nmlv.ptAction.x = wPosX;
8606   nmlv.ptAction.y = wPosY;
8607   listview_notify(hwnd, NM_DBLCLK, &nmlv);
8608
8609
8610   /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8611   if(nmlv.iItem != -1)
8612     hdr_notify(hwnd, LVN_ITEMACTIVATE);
8613
8614   return 0;
8615 }
8616
8617 /***
8618  * DESCRIPTION:
8619  * Processes mouse down messages (left mouse button).
8620  * 
8621  * PARAMETER(S):
8622  * [I] HWND : window handle
8623  * [I] WORD : key flag
8624  * [I] WORD : x coordinate
8625  * [I] WORD : y coordinate
8626  *
8627  * RETURN:
8628  * Zero
8629  */
8630 static LRESULT LISTVIEW_LButtonDown(HWND hwnd, WORD wKey, WORD wPosX, 
8631                                     WORD wPosY)
8632 {
8633   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8634   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
8635   static BOOL bGroupSelect = TRUE;
8636   POINT ptPosition;
8637   INT nItem;
8638
8639   TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8640
8641   /* send NM_RELEASEDCAPTURE notification */
8642   hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8643  
8644   if (infoPtr->bFocus == FALSE)
8645     SetFocus(hwnd);
8646
8647   /* set left button down flag */
8648   infoPtr->bLButtonDown = TRUE;
8649   
8650   ptPosition.x = wPosX;
8651   ptPosition.y = wPosY;
8652   nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
8653   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
8654   {
8655     if (lStyle & LVS_SINGLESEL)
8656     {
8657       if ((ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED)
8658           && infoPtr->nEditLabelItem == -1) 
8659           infoPtr->nEditLabelItem = nItem;
8660       else
8661         LISTVIEW_SetSelection(hwnd, nItem);
8662     }
8663     else
8664     {
8665       if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8666       {
8667         if (bGroupSelect)
8668           LISTVIEW_AddGroupSelection(hwnd, nItem);
8669         else
8670           LISTVIEW_AddSelection(hwnd, nItem);
8671       }
8672       else if (wKey & MK_CONTROL)
8673       {
8674         bGroupSelect = LISTVIEW_ToggleSelection(hwnd, nItem);
8675       }
8676       else  if (wKey & MK_SHIFT)
8677       {
8678         LISTVIEW_SetGroupSelection(hwnd, nItem);
8679       }
8680       else
8681       {
8682         BOOL was_selected =
8683             (ListView_GetItemState(hwnd, nItem, LVIS_SELECTED) & LVIS_SELECTED);
8684
8685         /* set selection (clears other pre-existing selections) */
8686         LISTVIEW_SetSelection(hwnd, nItem);
8687
8688         if (was_selected && infoPtr->nEditLabelItem == -1)
8689           infoPtr->nEditLabelItem = nItem;
8690       }
8691     }
8692   }
8693   else
8694   {
8695     /* remove all selections */
8696     LISTVIEW_RemoveAllSelections(hwnd);
8697   }
8698
8699   /* redraw if we could have possibly selected something */
8700   if(!GETITEMCOUNT(infoPtr)) InvalidateRect(hwnd, NULL, TRUE);
8701
8702   return 0;
8703 }
8704
8705 /***
8706  * DESCRIPTION:
8707  * Processes mouse up messages (left mouse button).
8708  * 
8709  * PARAMETER(S):
8710  * [I] HWND : window handle
8711  * [I] WORD : key flag
8712  * [I] WORD : x coordinate
8713  * [I] WORD : y coordinate
8714  *
8715  * RETURN:
8716  * Zero
8717  */
8718 static LRESULT LISTVIEW_LButtonUp(HWND hwnd, WORD wKey, WORD wPosX, 
8719                                   WORD wPosY)
8720 {
8721   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8722
8723   TRACE("(hwnd=%x, key=%hu, X=%hu, Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8724
8725   if (infoPtr->bLButtonDown != FALSE) 
8726   {
8727     LVHITTESTINFO lvHitTestInfo;
8728     NMLISTVIEW nmlv;
8729
8730     lvHitTestInfo.pt.x = wPosX;
8731     lvHitTestInfo.pt.y = wPosY;
8732
8733   /* send NM_CLICK notification */
8734     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8735     if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
8736     {
8737         nmlv.iItem = lvHitTestInfo.iItem;
8738         nmlv.iSubItem = lvHitTestInfo.iSubItem;
8739     }
8740     else
8741     {
8742         nmlv.iItem = -1;
8743         nmlv.iSubItem = 0;
8744     }
8745     nmlv.ptAction.x = wPosX;
8746     nmlv.ptAction.y = wPosY;
8747     listview_notify(hwnd, NM_CLICK, &nmlv);
8748
8749     /* set left button flag */
8750     infoPtr->bLButtonDown = FALSE;
8751
8752     if(infoPtr->nEditLabelItem != -1)
8753     {
8754       if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && lvHitTestInfo.flags & LVHT_ONITEMLABEL)
8755         LISTVIEW_EditLabelT(hwnd, lvHitTestInfo.iItem, TRUE);
8756       infoPtr->nEditLabelItem = -1;
8757     }
8758   }
8759
8760   return 0;
8761 }
8762
8763 /***
8764  * DESCRIPTION:
8765  * Creates the listview control (called before WM_CREATE).
8766  * 
8767  * PARAMETER(S):
8768  * [I] HWND : window handle
8769  * [I] WPARAM : unhandled 
8770  * [I] LPARAM : widow creation info
8771  * 
8772  * RETURN:
8773  * Zero
8774  */
8775 static LRESULT LISTVIEW_NCCreate(HWND hwnd, WPARAM wParam, LPARAM lParam)
8776 {
8777   LISTVIEW_INFO *infoPtr;
8778
8779   TRACE("(hwnd=%x, wParam=%x, lParam=%lx)\n", hwnd, wParam, lParam);
8780
8781   /* allocate memory for info structure */
8782   infoPtr = (LISTVIEW_INFO *)COMCTL32_Alloc(sizeof(LISTVIEW_INFO));
8783   if (infoPtr == NULL) 
8784   {
8785     ERR("could not allocate info memory!\n");
8786     return 0;
8787   }
8788
8789   SetWindowLongW(hwnd, 0, (LONG)infoPtr);
8790   if ((LISTVIEW_INFO *)GetWindowLongW(hwnd, 0) != infoPtr) 
8791   {
8792     ERR("pointer assignment error!\n");
8793     return 0;
8794   }
8795
8796   return DefWindowProcW(hwnd, WM_NCCREATE, wParam, lParam);
8797 }
8798
8799 /***
8800  * DESCRIPTION:
8801  * Destroys the listview control (called after WM_DESTROY).
8802  * 
8803  * PARAMETER(S):
8804  * [I] HWND : window handle
8805  * 
8806  * RETURN:
8807  * Zero
8808  */
8809 static LRESULT LISTVIEW_NCDestroy(HWND hwnd)
8810 {
8811   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8812
8813   TRACE("(hwnd=%x)\n", hwnd);
8814
8815   /* delete all items */
8816   LISTVIEW_DeleteAllItems(hwnd);
8817
8818   /* destroy data structure */
8819   DPA_Destroy(infoPtr->hdpaItems);
8820   DPA_Destroy(infoPtr->hdpaSelectionRanges);
8821
8822   /* destroy font */
8823   infoPtr->hFont = (HFONT)0;
8824   if (infoPtr->hDefaultFont)
8825   {
8826     DeleteObject(infoPtr->hDefaultFont);
8827   }
8828
8829   /* free listview info pointer*/
8830   COMCTL32_Free(infoPtr);
8831
8832   SetWindowLongW(hwnd, 0, 0);
8833   return 0;
8834 }
8835
8836 /***
8837  * DESCRIPTION:
8838  * Handles notifications from children.
8839  * 
8840  * PARAMETER(S):
8841  * [I] HWND : window handle
8842  * [I] INT : control identifier
8843  * [I] LPNMHDR : notification information
8844  * 
8845  * RETURN:
8846  * Zero
8847  */
8848 static LRESULT LISTVIEW_Notify(HWND hwnd, INT nCtrlId, LPNMHDR lpnmh)
8849 {
8850   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8851   
8852   TRACE("(hwnd=%x, nCtrlId=%d, lpnmh=%p)\n", hwnd, nCtrlId, lpnmh);
8853   
8854   if (lpnmh->hwndFrom == infoPtr->hwndHeader) 
8855   {
8856     /* handle notification from header control */
8857     if (lpnmh->code == HDN_ENDTRACKW)
8858     {
8859       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8860       InvalidateRect(hwnd, NULL, TRUE);
8861     }
8862     else if(lpnmh->code ==  HDN_ITEMCLICKW || lpnmh->code ==  HDN_ITEMCLICKA)
8863     {
8864         /* Handle sorting by Header Column */
8865         NMLISTVIEW nmlv;
8866
8867         ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8868         nmlv.iItem = -1;
8869         nmlv.iSubItem = ((LPNMHEADERW)lpnmh)->iItem;
8870         listview_notify(hwnd, LVN_COLUMNCLICK, &nmlv);
8871     }
8872     else if(lpnmh->code == NM_RELEASEDCAPTURE)
8873     {
8874       /* Idealy this should be done in HDN_ENDTRACKA
8875        * but since SetItemBounds in Header.c is called after
8876        * the notification is sent, it is neccessary to handle the
8877        * update of the scroll bar here (Header.c works fine as it is,
8878        * no need to disturb it)
8879        */
8880       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
8881       LISTVIEW_UpdateScroll(hwnd);
8882       InvalidateRect(hwnd, NULL, TRUE);
8883     }
8884
8885   }
8886
8887   return 0;
8888 }
8889
8890 /***
8891  * DESCRIPTION:
8892  * Determines the type of structure to use.
8893  * 
8894  * PARAMETER(S):
8895  * [I] HWND : window handle of the sender
8896  * [I] HWND : listview window handle 
8897  * [I] INT : command specifying the nature of the WM_NOTIFYFORMAT
8898  *
8899  * RETURN:
8900  * Zero
8901  */
8902 static LRESULT LISTVIEW_NotifyFormat(HWND hwndFrom, HWND hwnd, INT nCommand)
8903 {
8904   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8905
8906   TRACE("(hwndFrom=%x, hwnd=%x, nCommand=%d)\n", hwndFrom, hwnd, nCommand);
8907   
8908   if (nCommand == NF_REQUERY)
8909     infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, 
8910                                          (WPARAM)hwnd, (LPARAM)NF_QUERY);
8911   return 0;
8912 }
8913
8914 /***
8915  * DESCRIPTION:
8916  * Paints/Repaints the listview control.
8917  * 
8918  * PARAMETER(S):
8919  * [I] HWND : window handle
8920  * [I] HDC : device context handle
8921  *
8922  * RETURN:
8923  * Zero
8924  */
8925 static LRESULT LISTVIEW_Paint(HWND hwnd, HDC hdc)
8926 {
8927   PAINTSTRUCT ps;
8928
8929   TRACE("(hwnd=%x, hdc=%x)\n", hwnd, hdc);
8930
8931   if (hdc == 0)
8932   {
8933     hdc = BeginPaint(hwnd, &ps);
8934     LISTVIEW_Refresh(hwnd, hdc);
8935     EndPaint(hwnd, &ps);
8936   }
8937   else
8938   {
8939     LISTVIEW_Refresh(hwnd, hdc);
8940   }
8941
8942   return 0;
8943 }
8944
8945 /***
8946  * DESCRIPTION:
8947  * Processes double click messages (right mouse button).
8948  * 
8949  * PARAMETER(S):
8950  * [I] HWND : window handle
8951  * [I] WORD : key flag
8952  * [I] WORD : x coordinate
8953  * [I] WORD : y coordinate
8954  *
8955  * RETURN:
8956  * Zero
8957  */
8958 static LRESULT LISTVIEW_RButtonDblClk(HWND hwnd, WORD wKey, WORD wPosX, 
8959                                       WORD wPosY)
8960 {
8961   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8962
8963   /* send NM_RELEASEDCAPTURE notification */ 
8964   hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8965
8966   /* send NM_RDBLCLK notification */
8967   hdr_notify(hwnd, NM_RDBLCLK);
8968
8969   return 0;
8970 }
8971
8972 /***
8973  * DESCRIPTION:
8974  * Processes mouse down messages (right mouse button).
8975  * 
8976  * PARAMETER(S):
8977  * [I] HWND : window handle
8978  * [I] WORD : key flag
8979  * [I] WORD : x coordinate
8980  * [I] WORD : y coordinate
8981  *
8982  * RETURN:
8983  * Zero
8984  */
8985 static LRESULT LISTVIEW_RButtonDown(HWND hwnd, WORD wKey, WORD wPosX, 
8986                                     WORD wPosY)
8987 {
8988   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
8989   POINT ptPosition;
8990   INT nItem;
8991   NMLISTVIEW nmlv;
8992   LVHITTESTINFO lvHitTestInfo;
8993
8994   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
8995
8996   /* send NM_RELEASEDCAPTURE notification */
8997   hdr_notify(hwnd, NM_RELEASEDCAPTURE);
8998  
8999   /* make sure the listview control window has the focus */
9000   if (infoPtr->bFocus == FALSE)
9001     SetFocus(hwnd);
9002
9003   /* set right button down flag */
9004   infoPtr->bRButtonDown = TRUE;
9005
9006   /* determine the index of the selected item */
9007   ptPosition.x = wPosX;
9008   ptPosition.y = wPosY;
9009   nItem = LISTVIEW_MouseSelection(hwnd, ptPosition);
9010   if ((nItem >= 0) && (nItem < GETITEMCOUNT(infoPtr)))
9011   {
9012     LISTVIEW_SetItemFocus(hwnd,nItem);
9013     if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9014         !LISTVIEW_IsSelected(hwnd,nItem))
9015       LISTVIEW_SetSelection(hwnd, nItem);
9016   }
9017   else
9018   {
9019     LISTVIEW_RemoveAllSelections(hwnd);
9020   }
9021
9022   lvHitTestInfo.pt.x = wPosX;
9023   lvHitTestInfo.pt.y = wPosY;
9024
9025   /* Send NM_RClICK notification */
9026   ZeroMemory(&nmlv, sizeof(nmlv));
9027   if (LISTVIEW_HitTestItem(hwnd, &lvHitTestInfo, TRUE) != -1)
9028   {
9029     nmlv.iItem = lvHitTestInfo.iItem;
9030     nmlv.iSubItem = lvHitTestInfo.iSubItem;
9031   }
9032   else
9033   {
9034     nmlv.iItem = -1;
9035     nmlv.iSubItem = 0;
9036   }
9037   nmlv.ptAction.x = wPosX;
9038   nmlv.ptAction.y = wPosY;
9039   listview_notify(hwnd, NM_RCLICK, &nmlv);
9040   
9041   return 0;
9042 }
9043
9044 /***
9045  * DESCRIPTION:
9046  * Processes mouse up messages (right mouse button).
9047  * 
9048  * PARAMETER(S):
9049  * [I] HWND : window handle
9050  * [I] WORD : key flag
9051  * [I] WORD : x coordinate
9052  * [I] WORD : y coordinate
9053  *
9054  * RETURN:
9055  * Zero
9056  */
9057 static LRESULT LISTVIEW_RButtonUp(HWND hwnd, WORD wKey, WORD wPosX, 
9058                                   WORD wPosY)
9059 {
9060   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9061
9062   TRACE("(hwnd=%x,key=%hu,X=%hu,Y=%hu)\n", hwnd, wKey, wPosX, wPosY);
9063
9064   if (infoPtr->bRButtonDown) 
9065   {
9066     POINT pt;
9067
9068     pt.x = wPosX;
9069     pt.y = wPosY;
9070
9071     /* set button flag */
9072     infoPtr->bRButtonDown = FALSE;
9073     
9074     /* Change to screen coordinate for WM_CONTEXTMENU */
9075     ClientToScreen(hwnd, &pt);
9076     
9077     /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9078     SendMessageW( hwnd, WM_CONTEXTMENU, (WPARAM) hwnd, MAKELPARAM(pt.x, pt.y));
9079   }
9080   
9081   return 0;
9082 }
9083
9084 /***
9085  * DESCRIPTION:
9086  * Sets the focus.  
9087  * 
9088  * PARAMETER(S):
9089  * [I] HWND : window handle
9090  * [I] HWND : window handle of previously focused window
9091  *
9092  * RETURN:
9093  * Zero
9094  */
9095 static LRESULT LISTVIEW_SetFocus(HWND hwnd, HWND hwndLoseFocus)
9096 {
9097   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9098
9099   TRACE("(hwnd=%x, hwndLoseFocus=%x)\n", hwnd, hwndLoseFocus);
9100
9101   /* send NM_SETFOCUS notification */
9102   hdr_notify(hwnd, NM_SETFOCUS);
9103
9104   /* set window focus flag */
9105   infoPtr->bFocus = TRUE;
9106
9107   UpdateWindow(hwnd);
9108
9109   return 0;
9110 }
9111
9112 /***
9113  * DESCRIPTION:
9114  * Sets the font.  
9115  * 
9116  * PARAMETER(S):
9117  * [I] HWND : window handle
9118  * [I] HFONT : font handle
9119  * [I] WORD : redraw flag
9120  *
9121  * RETURN:
9122  * Zero
9123  */
9124 static LRESULT LISTVIEW_SetFont(HWND hwnd, HFONT hFont, WORD fRedraw)
9125 {
9126   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9127   UINT uView = GetWindowLongW(hwnd, GWL_STYLE) & LVS_TYPEMASK;
9128
9129   TRACE("(hwnd=%x,hfont=%x,redraw=%hu)\n", hwnd, hFont, fRedraw);
9130
9131   infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9132   LISTVIEW_SaveTextMetrics(hwnd);
9133
9134   if (uView == LVS_REPORT)
9135   {
9136     /* set header font */
9137     SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, 
9138                    MAKELPARAM(fRedraw, 0));
9139   }
9140
9141   /* invalidate listview control client area */
9142   InvalidateRect(hwnd, NULL, TRUE);
9143   
9144   if (fRedraw != FALSE)
9145     UpdateWindow(hwnd);
9146
9147   return 0;
9148 }
9149
9150 /***
9151  * DESCRIPTION:
9152  * Message handling for WM_SETREDRAW.  
9153  * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9154  * 
9155  * PARAMETER(S):
9156  * [I] HWND   : window handle
9157  * [I] bRedraw: state of redraw flag
9158  *
9159  * RETURN:
9160  * DefWinProc return value
9161  */
9162 static LRESULT LISTVIEW_SetRedraw(HWND hwnd, BOOL bRedraw)
9163 {
9164     LRESULT lResult = DefWindowProcW(hwnd, WM_SETREDRAW, bRedraw, 0);
9165     if(bRedraw)
9166         RedrawWindow(hwnd, NULL, 0, 
9167             RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
9168     return lResult;
9169 }
9170
9171 /***
9172  * DESCRIPTION:
9173  * Resizes the listview control. This function processes WM_SIZE
9174  * messages.  At this time, the width and height are not used.
9175  * 
9176  * PARAMETER(S):
9177  * [I] HWND : window handle
9178  * [I] WORD : new width
9179  * [I] WORD : new height
9180  *
9181  * RETURN:
9182  * Zero
9183  */
9184 static LRESULT LISTVIEW_Size(HWND hwnd, int Width, int Height)
9185 {
9186   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE); 
9187   UINT uView = lStyle & LVS_TYPEMASK;
9188
9189   TRACE("(hwnd=%x, width=%d, height=%d)\n", hwnd, Width, Height);
9190
9191   if (LISTVIEW_UpdateSize(hwnd))
9192   {
9193     if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
9194     {
9195         if (lStyle & LVS_ALIGNLEFT)
9196             LISTVIEW_AlignLeft(hwnd);
9197         else
9198             LISTVIEW_AlignTop(hwnd);
9199     }
9200
9201     LISTVIEW_UpdateScroll(hwnd);
9202   
9203     /* invalidate client area + erase background */
9204     InvalidateRect(hwnd, NULL, TRUE);
9205   }
9206
9207   return 0;
9208 }
9209
9210 /***
9211  * DESCRIPTION:
9212  * Sets the size information.
9213  * 
9214  * PARAMETER(S):
9215  * [I] HWND : window handle
9216  *
9217  * RETURN:
9218  * Zero if no size change
9219  * 1 of size changed
9220  */
9221 static BOOL LISTVIEW_UpdateSize(HWND hwnd)
9222 {
9223   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9224   LONG lStyle = GetWindowLongW(hwnd, GWL_STYLE);
9225   UINT uView = lStyle & LVS_TYPEMASK;
9226   RECT rcList;
9227   RECT rcOld;
9228   
9229   TRACE("(hwnd=%x)\n", hwnd);
9230   
9231   GetClientRect(hwnd, &rcList);
9232   CopyRect(&rcOld,&(infoPtr->rcList));
9233   infoPtr->rcList.left = 0;
9234   infoPtr->rcList.right = max(rcList.right - rcList.left, 1);
9235   infoPtr->rcList.top = 0;
9236   infoPtr->rcList.bottom = max(rcList.bottom - rcList.top, 1);
9237      
9238   if (uView == LVS_LIST)
9239   {
9240     if (lStyle & WS_HSCROLL)
9241     {
9242       INT nHScrollHeight = GetSystemMetrics(SM_CYHSCROLL);
9243       if (infoPtr->rcList.bottom > nHScrollHeight)
9244         infoPtr->rcList.bottom -= nHScrollHeight;
9245     }
9246   }
9247   else if (uView == LVS_REPORT)
9248   {
9249     HDLAYOUT hl;
9250     WINDOWPOS wp;
9251
9252     hl.prc = &rcList;
9253     hl.pwpos = &wp;
9254     Header_Layout(infoPtr->hwndHeader, &hl);
9255
9256     SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9257
9258     if (!(LVS_NOCOLUMNHEADER & lStyle))
9259       infoPtr->rcList.top = max(wp.cy, 0);
9260   }
9261   return (EqualRect(&rcOld,&(infoPtr->rcList)));
9262 }
9263
9264 /***
9265  * DESCRIPTION:
9266  * Processes WM_STYLECHANGED messages. 
9267  * 
9268  * PARAMETER(S):
9269  * [I] HWND : window handle
9270  * [I] WPARAM : window style type (normal or extended)
9271  * [I] LPSTYLESTRUCT : window style information
9272  *
9273  * RETURN:
9274  * Zero
9275  */
9276 static INT LISTVIEW_StyleChanged(HWND hwnd, WPARAM wStyleType, 
9277                                  LPSTYLESTRUCT lpss)
9278 {
9279   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9280   UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9281   UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9282   RECT rcList = infoPtr->rcList;
9283
9284   TRACE("(hwnd=%x, styletype=%x, stylestruct=%p)\n", 
9285         hwnd, wStyleType, lpss);
9286
9287   if (wStyleType == GWL_STYLE)
9288   {
9289     if (uOldView == LVS_REPORT)
9290       ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9291  
9292     if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9293         ((lpss->styleNew & WS_HSCROLL) == 0))
9294        ShowScrollBar(hwnd, SB_HORZ, FALSE);
9295  
9296     if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9297         ((lpss->styleNew & WS_VSCROLL) == 0))
9298        ShowScrollBar(hwnd, SB_VERT, FALSE);
9299  
9300     if (uNewView == LVS_ICON)
9301     {
9302       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXICON);
9303       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYICON);
9304       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9305       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9306       if (lpss->styleNew & LVS_ALIGNLEFT)
9307         LISTVIEW_AlignLeft(hwnd);
9308       else
9309         LISTVIEW_AlignTop(hwnd);
9310     }
9311     else if (uNewView == LVS_REPORT)
9312     {
9313       HDLAYOUT hl;
9314       WINDOWPOS wp;
9315
9316       hl.prc = &rcList;
9317       hl.pwpos = &wp;
9318       Header_Layout(infoPtr->hwndHeader, &hl);
9319       SetWindowPos(infoPtr->hwndHeader, hwnd, wp.x, wp.y, wp.cx, wp.cy, 
9320                    wp.flags);
9321       if (!(LVS_NOCOLUMNHEADER & lpss->styleNew))
9322         ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9323       
9324       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9325       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9326       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9327       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9328     }
9329     else if (uNewView == LVS_LIST)
9330     {
9331       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9332       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9333       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9334       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9335     }
9336     else
9337     {
9338       infoPtr->iconSize.cx = GetSystemMetrics(SM_CXSMICON);
9339       infoPtr->iconSize.cy = GetSystemMetrics(SM_CYSMICON);
9340       infoPtr->nItemWidth = LISTVIEW_GetItemWidth(hwnd);
9341       infoPtr->nItemHeight = LISTVIEW_GetItemHeight(hwnd);
9342       if (lpss->styleNew & LVS_ALIGNLEFT)
9343         LISTVIEW_AlignLeft(hwnd);
9344       else
9345         LISTVIEW_AlignTop(hwnd);
9346     }
9347
9348     /* update the size of the client area */
9349     LISTVIEW_UpdateSize(hwnd);
9350
9351     /* add scrollbars if needed */
9352     LISTVIEW_UpdateScroll(hwnd);
9353     
9354     /* invalidate client area + erase background */
9355     InvalidateRect(hwnd, NULL, TRUE);
9356
9357     /* print the list of unsupported window styles */
9358     LISTVIEW_UnsupportedStyles(lpss->styleNew);
9359   }
9360
9361   /* If they change the view and we have an active edit control 
9362      we will need to kill the control since the redraw will
9363      misplace the edit control.
9364    */
9365   if (infoPtr->hwndEdit &&
9366         ((uNewView & (LVS_ICON|LVS_LIST|LVS_SMALLICON)) !=
9367         ((LVS_ICON|LVS_LIST|LVS_SMALLICON) & uOldView)))
9368   {
9369      SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9370   }
9371
9372   return 0;
9373 }
9374
9375 /***
9376  * DESCRIPTION:
9377  * Window procedure of the listview control.
9378  *
9379  */
9380 static LRESULT WINAPI LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
9381                                    LPARAM lParam)
9382 {
9383   TRACE("(hwnd=%x uMsg=%x wParam=%x lParam=%lx)\n", hwnd, uMsg, wParam, lParam);
9384   if (!GetWindowLongW(hwnd, 0) && (uMsg != WM_NCCREATE))
9385     return DefWindowProcW( hwnd, uMsg, wParam, lParam );
9386   switch (uMsg)
9387   {
9388   case LVM_APPROXIMATEVIEWRECT: 
9389     return LISTVIEW_ApproximateViewRect(hwnd, (INT)wParam, 
9390                                         LOWORD(lParam), HIWORD(lParam));
9391   case LVM_ARRANGE: 
9392     return LISTVIEW_Arrange(hwnd, (INT)wParam);
9393
9394 /* case LVM_CREATEDRAGIMAGE: */
9395
9396   case LVM_DELETEALLITEMS:
9397     return LISTVIEW_DeleteAllItems(hwnd);
9398
9399   case LVM_DELETECOLUMN:
9400     return LISTVIEW_DeleteColumn(hwnd, (INT)wParam);
9401
9402   case LVM_DELETEITEM:
9403     return LISTVIEW_DeleteItem(hwnd, (INT)wParam);
9404
9405   case LVM_EDITLABELW:
9406     return LISTVIEW_EditLabelT(hwnd, (INT)wParam, TRUE);
9407     
9408   case LVM_EDITLABELA:
9409     return LISTVIEW_EditLabelT(hwnd, (INT)wParam, FALSE);
9410
9411   case LVM_ENSUREVISIBLE:
9412     return LISTVIEW_EnsureVisible(hwnd, (INT)wParam, (BOOL)lParam);
9413
9414   case LVM_FINDITEMW:
9415     return LISTVIEW_FindItemW(hwnd, (INT)wParam, (LPLVFINDINFOW)lParam);
9416
9417   case LVM_FINDITEMA:
9418     return LISTVIEW_FindItemA(hwnd, (INT)wParam, (LPLVFINDINFOA)lParam);
9419
9420   case LVM_GETBKCOLOR:
9421     return LISTVIEW_GetBkColor(hwnd);
9422
9423 /*      case LVM_GETBKIMAGE: */
9424
9425   case LVM_GETCALLBACKMASK:
9426     return LISTVIEW_GetCallbackMask(hwnd);
9427
9428   case LVM_GETCOLUMNA:
9429     return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9430
9431   case LVM_GETCOLUMNW:
9432     return LISTVIEW_GetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9433
9434   case LVM_GETCOLUMNORDERARRAY:
9435     return LISTVIEW_GetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9436
9437   case LVM_GETCOLUMNWIDTH:
9438     return LISTVIEW_GetColumnWidth(hwnd, (INT)wParam);
9439
9440   case LVM_GETCOUNTPERPAGE:
9441     return LISTVIEW_GetCountPerPage(hwnd);
9442
9443   case LVM_GETEDITCONTROL:
9444     return LISTVIEW_GetEditControl(hwnd);
9445
9446   case LVM_GETEXTENDEDLISTVIEWSTYLE:
9447     return LISTVIEW_GetExtendedListViewStyle(hwnd);
9448
9449   case LVM_GETHEADER:
9450     return LISTVIEW_GetHeader(hwnd);
9451
9452   case LVM_GETHOTCURSOR:
9453     FIXME("LVM_GETHOTCURSOR: unimplemented\n");
9454     return FALSE;
9455
9456   case LVM_GETHOTITEM:
9457     return LISTVIEW_GetHotItem(hwnd);
9458
9459   case LVM_GETHOVERTIME:
9460     return LISTVIEW_GetHoverTime(hwnd);
9461
9462   case LVM_GETIMAGELIST:
9463     return LISTVIEW_GetImageList(hwnd, (INT)wParam);
9464
9465   case LVM_GETISEARCHSTRINGA:
9466   case LVM_GETISEARCHSTRINGW:
9467     FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9468     return FALSE;
9469
9470   case LVM_GETITEMA:
9471     return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, FALSE);
9472
9473   case LVM_GETITEMW:
9474     return LISTVIEW_GetItemT(hwnd, (LPLVITEMW)lParam, FALSE, TRUE);
9475
9476   case LVM_GETITEMCOUNT:
9477     return LISTVIEW_GetItemCount(hwnd);
9478
9479   case LVM_GETITEMPOSITION:
9480     return LISTVIEW_GetItemPosition(hwnd, (INT)wParam, (LPPOINT)lParam);
9481
9482   case LVM_GETITEMRECT: 
9483     return LISTVIEW_GetItemRect(hwnd, (INT)wParam, (LPRECT)lParam);
9484
9485   case LVM_GETITEMSPACING: 
9486     return LISTVIEW_GetItemSpacing(hwnd, (BOOL)wParam);
9487
9488   case LVM_GETITEMSTATE: 
9489     return LISTVIEW_GetItemState(hwnd, (INT)wParam, (UINT)lParam);
9490     
9491   case LVM_GETITEMTEXTA:
9492     return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9493
9494   case LVM_GETITEMTEXTW:
9495     return LISTVIEW_GetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9496
9497   case LVM_GETNEXTITEM:
9498     return LISTVIEW_GetNextItem(hwnd, (INT)wParam, LOWORD(lParam));
9499
9500   case LVM_GETNUMBEROFWORKAREAS:
9501     FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9502     return 1;
9503
9504   case LVM_GETORIGIN:
9505     return LISTVIEW_GetOrigin(hwnd, (LPPOINT)lParam);
9506
9507   case LVM_GETSELECTEDCOUNT:
9508     return LISTVIEW_GetSelectedCount(hwnd);
9509
9510   case LVM_GETSELECTIONMARK: 
9511     return LISTVIEW_GetSelectionMark(hwnd);
9512
9513   case LVM_GETSTRINGWIDTHA:
9514     return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, FALSE);
9515
9516   case LVM_GETSTRINGWIDTHW:
9517     return LISTVIEW_GetStringWidthT(hwnd, (LPCWSTR)lParam, TRUE);
9518     
9519   case LVM_GETSUBITEMRECT:
9520     return LISTVIEW_GetSubItemRect(hwnd, (UINT)wParam, ((LPRECT)lParam)->top,
9521                                    ((LPRECT)lParam)->left, (LPRECT)lParam);
9522
9523   case LVM_GETTEXTBKCOLOR:
9524     return LISTVIEW_GetTextBkColor(hwnd);
9525
9526   case LVM_GETTEXTCOLOR:
9527     return LISTVIEW_GetTextColor(hwnd);
9528
9529   case LVM_GETTOOLTIPS:
9530     FIXME("LVM_GETTOOLTIPS: unimplemented\n");
9531     return FALSE;
9532
9533   case LVM_GETTOPINDEX:
9534     return LISTVIEW_GetTopIndex(hwnd);
9535
9536   /*case LVM_GETUNICODEFORMAT:
9537     FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9538     return FALSE;*/
9539
9540   case LVM_GETVIEWRECT:
9541     return LISTVIEW_GetViewRect(hwnd, (LPRECT)lParam);
9542
9543   case LVM_GETWORKAREAS:
9544     FIXME("LVM_GETWORKAREAS: unimplemented\n");
9545     return FALSE;
9546
9547   case LVM_HITTEST:
9548     return LISTVIEW_HitTest(hwnd, (LPLVHITTESTINFO)lParam);
9549
9550   case LVM_INSERTCOLUMNA:
9551     return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9552
9553   case LVM_INSERTCOLUMNW:
9554     return LISTVIEW_InsertColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9555
9556   case LVM_INSERTITEMA:
9557     return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9558
9559   case LVM_INSERTITEMW:
9560     return LISTVIEW_InsertItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9561
9562   case LVM_REDRAWITEMS:
9563     return LISTVIEW_RedrawItems(hwnd, (INT)wParam, (INT)lParam);
9564
9565 /*   case LVM_SCROLL:  */
9566 /*     return LISTVIEW_Scroll(hwnd, (INT)wParam, (INT)lParam); */
9567
9568   case LVM_SETBKCOLOR:
9569     return LISTVIEW_SetBkColor(hwnd, (COLORREF)lParam);
9570
9571 /*      case LVM_SETBKIMAGE: */
9572
9573   case LVM_SETCALLBACKMASK:
9574     return LISTVIEW_SetCallbackMask(hwnd, (UINT)wParam);
9575
9576   case LVM_SETCOLUMNA:
9577     return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9578
9579   case LVM_SETCOLUMNW:
9580     return LISTVIEW_SetColumnT(hwnd, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9581
9582   case LVM_SETCOLUMNORDERARRAY:
9583     return LISTVIEW_SetColumnOrderArray(hwnd, (INT)wParam, (LPINT)lParam);
9584
9585   case LVM_SETCOLUMNWIDTH:
9586     return LISTVIEW_SetColumnWidth(hwnd, (INT)wParam, SLOWORD(lParam));
9587
9588   case LVM_SETEXTENDEDLISTVIEWSTYLE:
9589     return LISTVIEW_SetExtendedListViewStyle(hwnd, (DWORD)wParam, (DWORD)lParam);
9590
9591 /*      case LVM_SETHOTCURSOR: */
9592
9593   case LVM_SETHOTITEM:
9594     return LISTVIEW_SetHotItem(hwnd, (INT)wParam);
9595
9596   case LVM_SETHOVERTIME:
9597     return LISTVIEW_SetHoverTime(hwnd, (DWORD)wParam);
9598
9599   case LVM_SETICONSPACING:
9600     return LISTVIEW_SetIconSpacing(hwnd, (DWORD)lParam);
9601         
9602   case LVM_SETIMAGELIST:
9603     return (LRESULT)LISTVIEW_SetImageList(hwnd, (INT)wParam, (HIMAGELIST)lParam);
9604
9605   case LVM_SETITEMA:
9606     return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, FALSE);
9607
9608   case LVM_SETITEMW:
9609     return LISTVIEW_SetItemT(hwnd, (LPLVITEMW)lParam, TRUE);
9610
9611   case LVM_SETITEMCOUNT: 
9612     return LISTVIEW_SetItemCount(hwnd, (INT)wParam, (DWORD)lParam);
9613     
9614   case LVM_SETITEMPOSITION:
9615     return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, (INT)LOWORD(lParam),
9616                                     (INT)HIWORD(lParam));
9617
9618   case LVM_SETITEMPOSITION32:
9619     return LISTVIEW_SetItemPosition(hwnd, (INT)wParam, ((POINT*)lParam)->x,
9620                                     ((POINT*)lParam)->y);
9621
9622   case LVM_SETITEMSTATE:
9623     return LISTVIEW_SetItemState(hwnd, (INT)wParam, (LPLVITEMW)lParam);
9624
9625   case LVM_SETITEMTEXTA:
9626     return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9627
9628   case LVM_SETITEMTEXTW:
9629     return LISTVIEW_SetItemTextT(hwnd, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9630
9631   case LVM_SETSELECTIONMARK:
9632     return LISTVIEW_SetSelectionMark(hwnd, (INT)lParam);
9633
9634   case LVM_SETTEXTBKCOLOR:
9635     return LISTVIEW_SetTextBkColor(hwnd, (COLORREF)lParam);
9636
9637   case LVM_SETTEXTCOLOR:
9638     return LISTVIEW_SetTextColor(hwnd, (COLORREF)lParam);
9639
9640 /*      case LVM_SETTOOLTIPS: */
9641 /*      case LVM_SETUNICODEFORMAT: */
9642 /*      case LVM_SETWORKAREAS: */
9643
9644   case LVM_SORTITEMS:
9645     return LISTVIEW_SortItems(hwnd, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9646
9647 /*      case LVM_SUBITEMHITTEST: */
9648
9649   case LVM_UPDATE: 
9650     return LISTVIEW_Update(hwnd, (INT)wParam);
9651
9652   case WM_CHAR:
9653     return LISTVIEW_ProcessLetterKeys( hwnd, wParam, lParam );
9654
9655   case WM_COMMAND:
9656     return LISTVIEW_Command(hwnd, wParam, lParam);
9657
9658   case WM_CREATE:
9659     return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9660     
9661   case WM_ERASEBKGND:
9662     return LISTVIEW_EraseBackground(hwnd, wParam, lParam);
9663
9664   case WM_GETDLGCODE:
9665     return DLGC_WANTCHARS | DLGC_WANTARROWS;
9666
9667   case WM_GETFONT:
9668     return LISTVIEW_GetFont(hwnd);
9669
9670   case WM_HSCROLL:
9671     return LISTVIEW_HScroll(hwnd, (INT)LOWORD(wParam), 
9672                             (INT)HIWORD(wParam), (HWND)lParam);
9673
9674   case WM_KEYDOWN:
9675     return LISTVIEW_KeyDown(hwnd, (INT)wParam, (LONG)lParam);
9676
9677   case WM_KILLFOCUS:
9678     return LISTVIEW_KillFocus(hwnd);
9679
9680   case WM_LBUTTONDBLCLK:
9681     return LISTVIEW_LButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam), 
9682                                 HIWORD(lParam));
9683     
9684   case WM_LBUTTONDOWN:
9685     return LISTVIEW_LButtonDown(hwnd, (WORD)wParam, LOWORD(lParam), 
9686                                 HIWORD(lParam));
9687   case WM_LBUTTONUP:
9688     return LISTVIEW_LButtonUp(hwnd, (WORD)wParam, LOWORD(lParam), 
9689                               HIWORD(lParam));
9690   case WM_MOUSEMOVE:
9691     return LISTVIEW_MouseMove (hwnd, wParam, lParam);
9692
9693   case WM_MOUSEHOVER:
9694     return LISTVIEW_MouseHover(hwnd, wParam, lParam);
9695
9696   case WM_NCCREATE:
9697     return LISTVIEW_NCCreate(hwnd, wParam, lParam);
9698
9699   case WM_NCDESTROY:
9700     return LISTVIEW_NCDestroy(hwnd);
9701
9702   case WM_NOTIFY:
9703     return LISTVIEW_Notify(hwnd, (INT)wParam, (LPNMHDR)lParam);
9704
9705   case WM_NOTIFYFORMAT:
9706     return LISTVIEW_NotifyFormat(hwnd, (HWND)wParam, (INT)lParam);
9707
9708   case WM_PAINT: 
9709     return LISTVIEW_Paint(hwnd, (HDC)wParam); 
9710
9711   case WM_RBUTTONDBLCLK:
9712     return LISTVIEW_RButtonDblClk(hwnd, (WORD)wParam, LOWORD(lParam), 
9713                                   HIWORD(lParam));
9714
9715   case WM_RBUTTONDOWN:
9716     return LISTVIEW_RButtonDown(hwnd, (WORD)wParam, LOWORD(lParam), 
9717                                 HIWORD(lParam));
9718
9719   case WM_RBUTTONUP:
9720     return LISTVIEW_RButtonUp(hwnd, (WORD)wParam, LOWORD(lParam), 
9721                               HIWORD(lParam));
9722
9723   case WM_SETFOCUS:
9724     return LISTVIEW_SetFocus(hwnd, (HWND)wParam);
9725
9726   case WM_SETFONT:
9727     return LISTVIEW_SetFont(hwnd, (HFONT)wParam, (WORD)lParam);
9728
9729   case WM_SETREDRAW: 
9730     return LISTVIEW_SetRedraw(hwnd, (BOOL)wParam);
9731
9732   case WM_SIZE:
9733     return LISTVIEW_Size(hwnd, (int)SLOWORD(lParam), (int)SHIWORD(lParam));
9734
9735   case WM_STYLECHANGED:
9736     return LISTVIEW_StyleChanged(hwnd, wParam, (LPSTYLESTRUCT)lParam);
9737
9738 /*      case WM_TIMER: */
9739
9740   case WM_VSCROLL:
9741     return LISTVIEW_VScroll(hwnd, (INT)LOWORD(wParam), 
9742                             (INT)HIWORD(wParam), (HWND)lParam);
9743
9744   case WM_MOUSEWHEEL:
9745       if (wParam & (MK_SHIFT | MK_CONTROL))
9746           return DefWindowProcW( hwnd, uMsg, wParam, lParam );
9747       return LISTVIEW_MouseWheel(hwnd, (short int)HIWORD(wParam));/*    case WM_WINDOWPOSCHANGED: */
9748
9749 /*      case WM_WININICHANGE: */
9750
9751   default:
9752     if (uMsg >= WM_USER)
9753     {
9754       ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, 
9755           lParam);
9756     }
9757
9758     /* call default window procedure */
9759     return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9760   }
9761
9762   return 0;
9763 }
9764
9765 /***
9766  * DESCRIPTION:
9767  * Registers the window class.
9768  * 
9769  * PARAMETER(S):
9770  * None
9771  *
9772  * RETURN:
9773  * None
9774  */
9775 VOID LISTVIEW_Register(void)
9776 {
9777   WNDCLASSW wndClass;
9778
9779     ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9780     wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9781     wndClass.lpfnWndProc = (WNDPROC)LISTVIEW_WindowProc;
9782     wndClass.cbClsExtra = 0;
9783     wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9784     wndClass.hCursor = LoadCursorW(0, IDC_ARROWW);
9785     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9786     wndClass.lpszClassName = WC_LISTVIEWW;
9787     RegisterClassW(&wndClass);
9788 }
9789
9790 /***
9791  * DESCRIPTION:
9792  * Unregisters the window class.
9793  * 
9794  * PARAMETER(S):
9795  * None
9796  *
9797  * RETURN:
9798  * None
9799  */
9800 VOID LISTVIEW_Unregister(void)
9801 {
9802     UnregisterClassW(WC_LISTVIEWW, (HINSTANCE)NULL);
9803 }
9804
9805 /***
9806  * DESCRIPTION:
9807  * Handle any WM_COMMAND messages
9808  * 
9809  * PARAMETER(S):
9810  *
9811  * RETURN:
9812  */
9813 static LRESULT LISTVIEW_Command(HWND hwnd, WPARAM wParam, LPARAM lParam)
9814 {
9815     switch (HIWORD(wParam))
9816     {
9817         case EN_UPDATE:
9818         {
9819             /* 
9820              * Adjust the edit window size 
9821              */
9822             WCHAR buffer[1024];
9823             LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(hwnd, 0);
9824             HDC           hdc      = GetDC(infoPtr->hwndEdit);
9825             HFONT         hFont, hOldFont = 0;
9826             RECT          rect;
9827             SIZE          sz;
9828             int           len;
9829
9830             len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9831             GetWindowRect(infoPtr->hwndEdit, &rect);
9832
9833             /* Select font to get the right dimension of the string */
9834             hFont = SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9835             if(hFont != 0)
9836             {
9837                 hOldFont = SelectObject(hdc, hFont);
9838             }
9839
9840             if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9841             {
9842                 TEXTMETRICW textMetric;
9843
9844                 /* Add Extra spacing for the next character */
9845                 GetTextMetricsW(hdc, &textMetric);
9846                 sz.cx += (textMetric.tmMaxCharWidth * 2);
9847
9848                 SetWindowPos ( 
9849                     infoPtr->hwndEdit,
9850                     HWND_TOP, 
9851                     0, 
9852                     0,
9853                     sz.cx,
9854                     rect.bottom - rect.top,
9855                     SWP_DRAWFRAME|SWP_NOMOVE);
9856             }
9857             if(hFont != 0)
9858                 SelectObject(hdc, hOldFont);
9859
9860             ReleaseDC(hwnd, hdc);
9861
9862             break;
9863         }
9864
9865         default:
9866           return SendMessageW (GetParent (hwnd), WM_COMMAND, wParam, lParam);
9867     }
9868
9869     return 0;
9870 }
9871
9872
9873 /***
9874  * DESCRIPTION:
9875  * Subclassed edit control windproc function
9876  *
9877  * PARAMETER(S):
9878  *
9879  * RETURN:
9880  */
9881 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, 
9882         WPARAM wParam, LPARAM lParam, BOOL isW)
9883 {
9884     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(GetParent(hwnd), 0);
9885     EDITLABEL_ITEM *einfo = infoPtr->pedititem;
9886     static BOOL bIgnoreKillFocus = FALSE;
9887     BOOL cancel = FALSE;
9888
9889     TRACE("(hwnd=%x, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9890           hwnd, uMsg, wParam, lParam, isW);
9891     
9892     switch (uMsg)
9893     {
9894         case WM_GETDLGCODE:
9895           return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9896                         
9897         case WM_KILLFOCUS:
9898             if(bIgnoreKillFocus) return TRUE;
9899             break;
9900
9901         case WM_DESTROY:
9902         {
9903             WNDPROC editProc = einfo->EditWndProc;
9904             SetWindowLongW(hwnd, GWL_WNDPROC, (LONG)editProc);
9905             COMCTL32_Free(einfo);
9906             infoPtr->pedititem = NULL;
9907             return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9908         }
9909
9910         case WM_KEYDOWN:
9911             if (VK_ESCAPE == (INT)wParam)
9912             {
9913                 cancel = TRUE;
9914                 break;
9915             }
9916             else if (VK_RETURN == (INT)wParam)
9917                 break;
9918
9919         default:
9920             return CallWindowProcT(einfo->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9921     }
9922
9923     if (einfo->EditLblCb)
9924     {
9925         LPWSTR buffer = NULL;
9926         
9927         if (!cancel)
9928         {
9929             DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9930
9931             if (len)
9932             {
9933                 if ( (buffer = COMCTL32_Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9934                 {
9935                     if (isW) GetWindowTextW(hwnd, buffer, len+1);
9936                     else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9937                 }
9938             }
9939         }
9940         /* Processing LVN_ENDLABELEDIT message could kill the focus       */
9941         /* eg. Using a messagebox                                         */
9942         bIgnoreKillFocus = TRUE;
9943         einfo->EditLblCb(GetParent(hwnd), buffer, einfo->param);
9944
9945         if (buffer) COMCTL32_Free(buffer);
9946
9947         einfo->EditLblCb = NULL;
9948         bIgnoreKillFocus = FALSE;
9949     }
9950
9951     SendMessageW(hwnd, WM_CLOSE, 0, 0);
9952     return TRUE;
9953 }
9954
9955 /***
9956  * DESCRIPTION:
9957  * Subclassed edit control windproc function
9958  *
9959  * PARAMETER(S):
9960  *
9961  * RETURN:
9962  */
9963 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9964 {
9965     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9966 }
9967
9968 /***
9969  * DESCRIPTION:
9970  * Subclassed edit control windproc function
9971  *
9972  * PARAMETER(S):
9973  *
9974  * RETURN:
9975  */
9976 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9977 {
9978     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9979 }
9980
9981 /***
9982  * DESCRIPTION:
9983  * Creates a subclassed edit cotrol
9984  *
9985  * PARAMETER(S):
9986  *
9987  * RETURN:
9988  */
9989 HWND CreateEditLabelT(LPCWSTR text, DWORD style, INT x, INT y, 
9990         INT width, INT height, HWND parent, HINSTANCE hinst, 
9991         EditlblCallbackW EditLblCb, DWORD param, BOOL isW)
9992 {
9993     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongW(parent, 0);
9994     WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9995     HWND hedit;
9996     SIZE sz;
9997     HDC hdc;
9998     HDC hOldFont=0;
9999     TEXTMETRICW textMetric;
10000
10001     TRACE("(text=%s, ..., isW=%d)\n", debugstr_t(text, isW), isW);
10002     
10003     if (NULL == (infoPtr->pedititem = COMCTL32_Alloc(sizeof(EDITLABEL_ITEM))))
10004         return 0;
10005
10006     style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|WS_BORDER;
10007     hdc = GetDC(parent);
10008
10009     /* Select the font to get appropriate metric dimensions */
10010     if(infoPtr->hFont != 0)
10011         hOldFont = SelectObject(hdc, infoPtr->hFont);
10012
10013     /*Get String Lenght in pixels */
10014     GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
10015
10016     /*Add Extra spacing for the next character */
10017     GetTextMetricsW(hdc, &textMetric);
10018     sz.cx += (textMetric.tmMaxCharWidth * 2);
10019
10020     if(infoPtr->hFont != 0)
10021         SelectObject(hdc, hOldFont);
10022
10023     ReleaseDC(parent, hdc);
10024     if (isW) 
10025         hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
10026     else
10027         hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, parent, 0, hinst, 0);
10028
10029     if (!hedit)
10030     {
10031         COMCTL32_Free(infoPtr->pedititem);
10032         return 0;
10033     }
10034
10035     infoPtr->pedititem->param = param;
10036     infoPtr->pedititem->EditLblCb = EditLblCb;
10037     infoPtr->pedititem->EditWndProc = (WNDPROC) 
10038         (isW ? SetWindowLongW(hedit, GWL_WNDPROC, (LONG)EditLblWndProcW) :
10039                SetWindowLongA(hedit, GWL_WNDPROC, (LONG)EditLblWndProcA) );
10040
10041     SendMessageW(hedit, WM_SETFONT, infoPtr->hFont, FALSE);
10042
10043     return hedit;
10044 }