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