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