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