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