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