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