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