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