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