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