~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/comctl32/listview.c

Version: ~ [ wine-1.1.33 ] ~ [ wine-1.1.32 ] ~ [ wine-1.1.31 ] ~ [ wine-1.1.30 ] ~ [ wine-1.1.29 ] ~ [ wine-1.1.28 ] ~ [ wine-1.1.27 ] ~ [ wine-1.1.26 ] ~ [ wine-1.1.25 ] ~ [ wine-1.1.24 ] ~ [ wine-1.1.23 ] ~ [ wine-1.1.22 ] ~ [ wine-1.1.21 ] ~ [ wine-1.1.20 ] ~ [ wine-1.1.19 ] ~ [ wine-1.1.18 ] ~ [ wine-1.1.17 ] ~ [ wine-1.1.16 ] ~ [ wine-1.1.15 ] ~ [ wine-1.1.14 ] ~ [ wine-1.1.13 ] ~ [ wine-1.1.12 ] ~ [ wine-1.1.11 ] ~ [ wine-1.1.10 ] ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~

  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  * Copyright 2009 Nikolay Sivov
 10  * Copyright 2009 Owen Rudge for CodeWeavers
 11  *
 12  * This library is free software; you can redistribute it and/or
 13  * modify it under the terms of the GNU Lesser General Public
 14  * License as published by the Free Software Foundation; either
 15  * version 2.1 of the License, or (at your option) any later version.
 16  *
 17  * This library is distributed in the hope that it will be useful,
 18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 20  * Lesser General Public License for more details.
 21  *
 22  * You should have received a copy of the GNU Lesser General Public
 23  * License along with this library; if not, write to the Free Software
 24  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 25  *
 26  * NOTES
 27  *
 28  * This code was audited for completeness against the documented features
 29  * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
 30  * 
 31  * Unless otherwise noted, we believe this code to be complete, as per
 32  * the specification mentioned above.
 33  * If you discover missing features, or bugs, please note them below.
 34  * 
 35  * TODO:
 36  *
 37  * Default Message Processing
 38  *   -- WM_CREATE: create the icon and small icon image lists at this point only if
 39  *      the LVS_SHAREIMAGELISTS style is not specified.
 40  *   -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
 41  *      or small icon and the LVS_AUTOARRANGE style is specified.
 42  *   -- WM_TIMER
 43  *   -- WM_WININICHANGE
 44  *
 45  * Features
 46  *   -- Hot item handling, mouse hovering
 47  *   -- Workareas support
 48  *   -- Tilemode support
 49  *   -- Groups support
 50  *
 51  * Bugs
 52  *   -- Expand large item in ICON mode when the cursor is flying over the icon or text.
 53  *   -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
 54  *   -- LVA_SNAPTOGRID not implemented
 55  *   -- LISTVIEW_ApproximateViewRect partially implemented
 56  *   -- LISTVIEW_SetColumnWidth ignores header images & bitmap
 57  *   -- LISTVIEW_SetIconSpacing is incomplete
 58  *   -- LISTVIEW_StyleChanged doesn't handle some changes too well
 59  *
 60  * Speedups
 61  *   -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
 62  *      linear in the number of items in the list, and this is
 63  *      unacceptable for large lists.
 64  *   -- if list is sorted by item text LISTVIEW_InsertItemT could use
 65  *      binary search to calculate item index (e.g. DPA_Search()).
 66  *      This requires sorted state to be reliably tracked in item modifiers.
 67  *   -- we should keep an ordered array of coordinates in iconic mode
 68  *      this would allow to frame items (iterator_frameditems),
 69  *      and find nearest item (LVFI_NEARESTXY) a lot more efficiently
 70  *
 71  * Flags
 72  *   -- LVIF_COLUMNS
 73  *   -- LVIF_GROUPID
 74  *
 75  * States
 76  *   -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
 77  *   -- LVIS_CUT
 78  *   -- LVIS_DROPHILITED
 79  *   -- LVIS_OVERLAYMASK
 80  *
 81  * Styles
 82  *   -- LVS_NOLABELWRAP
 83  *   -- LVS_NOSCROLL (see Q137520)
 84  *   -- LVS_ALIGNTOP
 85  *
 86  * Extended Styles
 87  *   -- LVS_EX_BORDERSELECT
 88  *   -- LVS_EX_FLATSB
 89  *   -- LVS_EX_INFOTIP
 90  *   -- LVS_EX_LABELTIP
 91  *   -- LVS_EX_MULTIWORKAREAS
 92  *   -- LVS_EX_REGIONAL
 93  *   -- LVS_EX_SIMPLESELECT
 94  *   -- LVS_EX_TWOCLICKACTIVATE
 95  *   -- LVS_EX_UNDERLINECOLD
 96  *   -- LVS_EX_UNDERLINEHOT
 97  *   
 98  * Notifications:
 99  *   -- LVN_BEGINSCROLL, LVN_ENDSCROLL
100  *   -- LVN_GETINFOTIP
101  *   -- LVN_HOTTRACK
102  *   -- LVN_SETDISPINFO
103  *   -- NM_HOVER
104  *   -- LVN_BEGINRDRAG
105  *
106  * Messages:
107  *   -- LVM_ENABLEGROUPVIEW
108  *   -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
109  *   -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
110  *   -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
111  *   -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
112  *   -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
113  *   -- LVM_GETINSERTMARKRECT
114  *   -- LVM_GETNUMBEROFWORKAREAS
115  *   -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
116  *   -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
117  *   -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
118  *   -- LVM_GETTILEINFO, LVM_SETTILEINFO
119  *   -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
120  *   -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
121  *   -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
122  *   -- LVM_INSERTGROUPSORTED
123  *   -- LVM_INSERTMARKHITTEST
124  *   -- LVM_ISGROUPVIEWENABLED
125  *   -- LVM_MOVEGROUP
126  *   -- LVM_MOVEITEMTOGROUP
127  *   -- LVM_SETINFOTIP
128  *   -- LVM_SETTILEWIDTH
129  *   -- LVM_SORTGROUPS
130  *
131  * Macros:
132  *   -- ListView_GetHoverTime, ListView_SetHoverTime
133  *   -- ListView_GetISearchString
134  *   -- ListView_GetNumberOfWorkAreas
135  *   -- ListView_GetWorkAreas, ListView_SetWorkAreas
136  *
137  * Functions:
138  *   -- LVGroupComparE
139  *
140  * Known differences in message stream from native control (not known if
141  * these differences cause problems):
142  *   LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
143  *   LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
144  *   WM_CREATE does not issue WM_QUERYUISTATE and associated registry
145  *     processing for "USEDOUBLECLICKTIME".
146  */
147 
148 #include "config.h"
149 #include "wine/port.h"
150 
151 #include <assert.h>
152 #include <ctype.h>
153 #include <string.h>
154 #include <stdlib.h>
155 #include <stdarg.h>
156 #include <stdio.h>
157 
158 #include "windef.h"
159 #include "winbase.h"
160 #include "winnt.h"
161 #include "wingdi.h"
162 #include "winuser.h"
163 #include "winnls.h"
164 #include "commctrl.h"
165 #include "comctl32.h"
166 #include "uxtheme.h"
167 
168 #include "wine/debug.h"
169 #include "wine/unicode.h"
170 
171 WINE_DEFAULT_DEBUG_CHANNEL(listview);
172 
173 /* make sure you set this to 0 for production use! */
174 #define DEBUG_RANGES 1
175 
176 typedef struct tagCOLUMN_INFO
177 {
178   RECT rcHeader;        /* tracks the header's rectangle */
179   INT fmt;              /* same as LVCOLUMN.fmt */
180   INT cxMin;
181 } COLUMN_INFO;
182 
183 typedef struct tagITEMHDR
184 {
185   LPWSTR pszText;
186   INT iImage;
187 } ITEMHDR, *LPITEMHDR;
188 
189 typedef struct tagSUBITEM_INFO
190 {
191   ITEMHDR hdr;
192   INT iSubItem;
193 } SUBITEM_INFO;
194 
195 typedef struct tagITEM_ID ITEM_ID;
196 
197 typedef struct tagITEM_INFO
198 {
199   ITEMHDR hdr;
200   UINT state;
201   LPARAM lParam;
202   INT iIndent;
203   ITEM_ID *id;
204 } ITEM_INFO;
205 
206 struct tagITEM_ID
207 {
208   UINT id;   /* item id */
209   HDPA item; /* link to item data */
210 };
211 
212 typedef struct tagRANGE
213 {
214   INT lower;
215   INT upper;
216 } RANGE;
217 
218 typedef struct tagRANGES
219 {
220   HDPA hdpa;
221 } *RANGES;
222 
223 typedef struct tagITERATOR
224 {
225   INT nItem;
226   INT nSpecial;
227   RANGE range;
228   RANGES ranges;
229   INT index;
230 } ITERATOR;
231 
232 typedef struct tagDELAYED_ITEM_EDIT
233 {
234   BOOL fEnabled;
235   INT iItem;
236 } DELAYED_ITEM_EDIT;
237 
238 typedef struct tagLISTVIEW_INFO
239 {
240   HWND hwndSelf;
241   HBRUSH hBkBrush;
242   COLORREF clrBk;
243   COLORREF clrText;
244   COLORREF clrTextBk;
245   HIMAGELIST himlNormal;
246   HIMAGELIST himlSmall;
247   HIMAGELIST himlState;
248   BOOL bLButtonDown;
249   BOOL bRButtonDown;
250   BOOL bDragging;
251   BOOL bMarqueeSelect;       /* marquee selection/highlight underway */
252   BOOL bScrolling;
253   RECT marqueeRect;         /* absolute coordinates of marquee selection */
254   RECT marqueeDrawRect;     /* relative coordinates for drawing marquee */
255   POINT marqueeOrigin;      /* absolute coordinates of marquee click origin */
256   POINT ptClickPos;         /* point where the user clicked */ 
257   BOOL bNoItemMetrics;          /* flags if item metrics are not yet computed */
258   INT nItemHeight;
259   INT nItemWidth;
260   RANGES selectionRanges;
261   INT nSelectionMark;
262   INT nHotItem;
263   SHORT notifyFormat;
264   HWND hwndNotify;
265   RECT rcList;                 /* This rectangle is really the window
266                                 * client rectangle possibly reduced by the 
267                                 * horizontal scroll bar and/or header - see 
268                                 * LISTVIEW_UpdateSize. This rectangle offset
269                                 * by the LISTVIEW_GetOrigin value is in
270                                 * client coordinates   */
271   SIZE iconSize;
272   SIZE iconSpacing;
273   SIZE iconStateSize;
274   UINT uCallbackMask;
275   HWND hwndHeader;
276   HCURSOR hHotCursor;
277   HFONT hDefaultFont;
278   HFONT hFont;
279   INT ntmHeight;                /* Some cached metrics of the font used */
280   INT ntmMaxCharWidth;          /* by the listview to draw items */
281   INT nEllipsisWidth;
282   BOOL bRedraw;                 /* Turns on/off repaints & invalidations */
283   BOOL bAutoarrange;            /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
284   BOOL bFocus;
285   BOOL bDoChangeNotify;         /* send change notification messages? */
286   INT nFocusedItem;
287   RECT rcFocus;
288   DWORD dwStyle;                /* the cached window GWL_STYLE */
289   DWORD dwLvExStyle;            /* extended listview style */
290   DWORD uView;                  /* current view available through LVM_[G,S]ETVIEW */
291   INT nItemCount;               /* the number of items in the list */
292   HDPA hdpaItems;               /* array ITEM_INFO pointers */
293   HDPA hdpaItemIds;             /* array of ITEM_ID pointers */
294   HDPA hdpaPosX;                /* maintains the (X, Y) coordinates of the */
295   HDPA hdpaPosY;                /* items in LVS_ICON, and LVS_SMALLICON modes */
296   HDPA hdpaColumns;             /* array of COLUMN_INFO pointers */
297   BOOL colRectsDirty;           /* trigger column rectangles requery from header */
298   POINT currIconPos;            /* this is the position next icon will be placed */
299   PFNLVCOMPARE pfnCompare;
300   LPARAM lParamSort;
301   HWND hwndEdit;
302   WNDPROC EditWndProc;
303   INT nEditLabelItem;
304   INT nLButtonDownItem;         /* tracks item to reset multiselection on WM_LBUTTONUP */
305   DWORD dwHoverTime;
306   HWND hwndToolTip;
307 
308   DWORD cditemmode;             /* Keep the custom draw flags for an item/row */
309 
310   DWORD lastKeyPressTimestamp;
311   WPARAM charCode;
312   INT nSearchParamLength;
313   WCHAR szSearchParam[ MAX_PATH ];
314   BOOL bIsDrawing;
315   INT nMeasureItemHeight;
316   INT xTrackLine;               /* The x coefficient of the track line or -1 if none */
317   DELAYED_ITEM_EDIT itemEdit;   /* Pointer to this structure will be the timer ID */
318 
319   DWORD iVersion; /* CCM_[G,S]ETVERSION */
320 } LISTVIEW_INFO;
321 
322 /*
323  * constants
324  */
325 /* How many we debug buffer to allocate */
326 #define DEBUG_BUFFERS 20
327 /* The size of a single debug buffer */
328 #define DEBUG_BUFFER_SIZE 256
329 
330 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
331 #define SB_INTERNAL      -1
332 
333 /* maximum size of a label */
334 #define DISP_TEXT_SIZE 512
335 
336 /* padding for items in list and small icon display modes */
337 #define WIDTH_PADDING 12
338 
339 /* padding for items in list, report and small icon display modes */
340 #define HEIGHT_PADDING 1
341 
342 /* offset of items in report display mode */
343 #define REPORT_MARGINX 2
344 
345 /* padding for icon in large icon display mode
346  *   ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
347  *                                 that HITTEST will see.
348  *   ICON_TOP_PADDING_HITABLE - spacing between above and icon.
349  *   ICON_TOP_PADDING - sum of the two above.
350  *   ICON_BOTTOM_PADDING - between bottom of icon and top of text
351  *   LABEL_HOR_PADDING - between text and sides of box
352  *   LABEL_VERT_PADDING - between bottom of text and end of box
353  *
354  *   ICON_LR_PADDING - additional width above icon size.
355  *   ICON_LR_HALF - half of the above value
356  */
357 #define ICON_TOP_PADDING_NOTHITABLE  2
358 #define ICON_TOP_PADDING_HITABLE     2
359 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
360 #define ICON_BOTTOM_PADDING          4
361 #define LABEL_HOR_PADDING            5
362 #define LABEL_VERT_PADDING           7
363 #define ICON_LR_PADDING              16
364 #define ICON_LR_HALF                 (ICON_LR_PADDING/2)
365 
366 /* default label width for items in list and small icon display modes */
367 #define DEFAULT_LABEL_WIDTH 40
368 /* maximum select rectangle width for empty text item in LV_VIEW_DETAILS */
369 #define MAX_EMPTYTEXT_SELECT_WIDTH 80
370 
371 /* default column width for items in list display mode */
372 #define DEFAULT_COLUMN_WIDTH 128
373 
374 /* Size of "line" scroll for V & H scrolls */
375 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
376 
377 /* Padding between image and label */
378 #define IMAGE_PADDING  2
379 
380 /* Padding behind the label */
381 #define TRAILING_LABEL_PADDING  12
382 #define TRAILING_HEADER_PADDING  11
383 
384 /* Border for the icon caption */
385 #define CAPTION_BORDER  2
386 
387 /* Standard DrawText flags */
388 #define LV_ML_DT_FLAGS  (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
389 #define LV_FL_DT_FLAGS  (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
390 #define LV_SL_DT_FLAGS  (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
391 
392 /* Image index from state */
393 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
394 
395 /* The time in milliseconds to reset the search in the list */
396 #define KEY_DELAY       450
397 
398 /* Dump the LISTVIEW_INFO structure to the debug channel */
399 #define LISTVIEW_DUMP(iP) do { \
400   TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
401         iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
402         iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
403   TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
404         iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
405         iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
406   TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
407         iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
408         iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
409   TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
410 } while(0)
411 
412 static const WCHAR themeClass[] = {'L','i','s','t','V','i','e','w',0};
413 
414 /*
415  * forward declarations
416  */
417 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *, LPLVITEMW, BOOL);
418 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *, INT, LPRECT);
419 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *, INT, LPPOINT);
420 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *, INT, LPPOINT);
421 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *, INT, LPRECT);
422 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *, INT);
423 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *, LPPOINT);
424 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *, LPRECT);
425 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
426 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
427 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *, LPCWSTR, BOOL);
428 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT, BOOL);
429 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *, INT, UINT);
430 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
431 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
432 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
433 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
434 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, BOOL);
435 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
436 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
437 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *, BOOL, BOOL);
438 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *, INT, INT);
439 
440 /******** Text handling functions *************************************/
441 
442 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
443  * text string. The string may be ANSI or Unicode, in which case
444  * the boolean isW tells us the type of the string.
445  *
446  * The name of the function tell what type of strings it expects:
447  *   W: Unicode, T: ANSI/Unicode - function of isW
448  */
449 
450 static inline BOOL is_textW(LPCWSTR text)
451 {
452     return text != NULL && text != LPSTR_TEXTCALLBACKW;
453 }
454 
455 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
456 {
457     /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
458     return is_textW(text);
459 }
460 
461 static inline int textlenT(LPCWSTR text, BOOL isW)
462 {
463     return !is_textT(text, isW) ? 0 :
464            isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
465 }
466 
467 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
468 {
469     if (isDestW)
470         if (isSrcW) lstrcpynW(dest, src, max);
471         else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
472     else
473         if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
474         else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
475 }
476 
477 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
478 {
479     LPWSTR wstr = (LPWSTR)text;
480 
481     if (!isW && is_textT(text, isW))
482     {
483         INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
484         wstr = Alloc(len * sizeof(WCHAR));
485         if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
486     }
487     TRACE("   wstr=%s\n", text == LPSTR_TEXTCALLBACKW ?  "(callback)" : debugstr_w(wstr));
488     return wstr;
489 }
490 
491 static inline void textfreeT(LPWSTR wstr, BOOL isW)
492 {
493     if (!isW && is_textT(wstr, isW)) Free (wstr);
494 }
495 
496 /*
497  * dest is a pointer to a Unicode string
498  * src is a pointer to a string (Unicode if isW, ANSI if !isW)
499  */
500 static BOOL textsetptrT(LPWSTR *dest, LPCWSTR src, BOOL isW)
501 {
502     BOOL bResult = TRUE;
503     
504     if (src == LPSTR_TEXTCALLBACKW)
505     {
506         if (is_textW(*dest)) Free(*dest);
507         *dest = LPSTR_TEXTCALLBACKW;
508     }
509     else
510     {
511         LPWSTR pszText = textdupTtoW(src, isW);
512         if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
513         bResult = Str_SetPtrW(dest, pszText);
514         textfreeT(pszText, isW);
515     }
516     return bResult;
517 }
518 
519 /*
520  * compares a Unicode to a Unicode/ANSI text string
521  */
522 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
523 {
524     if (!aw) return bt ? -1 : 0;
525     if (!bt) return aw ? 1 : 0;
526     if (aw == LPSTR_TEXTCALLBACKW)
527         return bt == LPSTR_TEXTCALLBACKW ? 1 : -1;
528     if (bt != LPSTR_TEXTCALLBACKW)
529     {
530         LPWSTR bw = textdupTtoW(bt, isW);
531         int r = bw ? lstrcmpW(aw, bw) : 1;
532         textfreeT(bw, isW);
533         return r;
534     }       
535             
536     return 1;
537 }
538     
539 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
540 {
541     int res;
542 
543     n = min(min(n, lstrlenW(s1)), lstrlenW(s2));
544     res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
545     return res ? res - sizeof(WCHAR) : res;
546 }
547 
548 /******** Debugging functions *****************************************/
549 
550 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
551 {
552     if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
553     return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
554 }
555 
556 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
557 {
558     if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
559     n = min(textlenT(text, isW), n);
560     return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
561 }
562 
563 static char* debug_getbuf(void)
564 {
565     static int index = 0;
566     static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
567     return buffers[index++ % DEBUG_BUFFERS];
568 }
569 
570 static inline const char* debugrange(const RANGE *lprng)
571 {
572     if (!lprng) return "(null)";
573     return wine_dbg_sprintf("[%d, %d]", lprng->lower, lprng->upper);
574 }
575 
576 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
577 {
578     char* buf = debug_getbuf(), *text = buf;
579     int len, size = DEBUG_BUFFER_SIZE;
580 
581     if (pScrollInfo == NULL) return "(null)";
582     len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
583     if (len == -1) goto end; buf += len; size -= len;
584     if (pScrollInfo->fMask & SIF_RANGE)
585         len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
586     else len = 0;
587     if (len == -1) goto end; buf += len; size -= len;
588     if (pScrollInfo->fMask & SIF_PAGE)
589         len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
590     else len = 0;
591     if (len == -1) goto end; buf += len; size -= len;
592     if (pScrollInfo->fMask & SIF_POS)
593         len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
594     else len = 0;
595     if (len == -1) goto end; buf += len; size -= len;
596     if (pScrollInfo->fMask & SIF_TRACKPOS)
597         len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
598     else len = 0;
599     if (len == -1) goto end; buf += len; size -= len;
600     goto undo;
601 end:
602     buf = text + strlen(text);
603 undo:
604     if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
605     return text;
606 } 
607 
608 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
609 {
610     if (!plvnm) return "(null)";
611     return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
612                  " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
613                  plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
614                  plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam);
615 }
616 
617 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
618 {
619     char* buf = debug_getbuf(), *text = buf;
620     int len, size = DEBUG_BUFFER_SIZE;
621     
622     if (lpLVItem == NULL) return "(null)";
623     len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
624     if (len == -1) goto end; buf += len; size -= len;
625     if (lpLVItem->mask & LVIF_STATE)
626         len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
627     else len = 0;
628     if (len == -1) goto end; buf += len; size -= len;
629     if (lpLVItem->mask & LVIF_TEXT)
630         len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
631     else len = 0;
632     if (len == -1) goto end; buf += len; size -= len;
633     if (lpLVItem->mask & LVIF_IMAGE)
634         len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
635     else len = 0;
636     if (len == -1) goto end; buf += len; size -= len;
637     if (lpLVItem->mask & LVIF_PARAM)
638         len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
639     else len = 0;
640     if (len == -1) goto end; buf += len; size -= len;
641     if (lpLVItem->mask & LVIF_INDENT)
642         len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
643     else len = 0;
644     if (len == -1) goto end; buf += len; size -= len;
645     goto undo;
646 end:
647     buf = text + strlen(text);
648 undo:
649     if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
650     return text;
651 }
652 
653 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
654 {
655     char* buf = debug_getbuf(), *text = buf;
656     int len, size = DEBUG_BUFFER_SIZE;
657     
658     if (lpColumn == NULL) return "(null)";
659     len = snprintf(buf, size, "{");
660     if (len == -1) goto end; buf += len; size -= len;
661     if (lpColumn->mask & LVCF_SUBITEM)
662         len = snprintf(buf, size, "iSubItem=%d, ",  lpColumn->iSubItem);
663     else len = 0;
664     if (len == -1) goto end; buf += len; size -= len;
665     if (lpColumn->mask & LVCF_FMT)
666         len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
667     else len = 0;
668     if (len == -1) goto end; buf += len; size -= len;
669     if (lpColumn->mask & LVCF_WIDTH)
670         len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
671     else len = 0;
672     if (len == -1) goto end; buf += len; size -= len;
673     if (lpColumn->mask & LVCF_TEXT)
674         len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
675     else len = 0;
676     if (len == -1) goto end; buf += len; size -= len;
677     if (lpColumn->mask & LVCF_IMAGE)
678         len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
679     else len = 0;
680     if (len == -1) goto end; buf += len; size -= len;
681     if (lpColumn->mask & LVCF_ORDER)
682         len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
683     else len = 0;
684     if (len == -1) goto end; buf += len; size -= len;
685     goto undo;
686 end:
687     buf = text + strlen(text);
688 undo:
689     if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
690     return text;
691 }
692 
693 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
694 {
695     if (!lpht) return "(null)";
696 
697     return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
698                  wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
699 }
700 
701 /* Return the corresponding text for a given scroll value */
702 static inline LPCSTR debugscrollcode(int nScrollCode)
703 {
704   switch(nScrollCode)
705   {
706   case SB_LINELEFT: return "SB_LINELEFT";
707   case SB_LINERIGHT: return "SB_LINERIGHT";
708   case SB_PAGELEFT: return "SB_PAGELEFT";
709   case SB_PAGERIGHT: return "SB_PAGERIGHT";
710   case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
711   case SB_THUMBTRACK: return "SB_THUMBTRACK";
712   case SB_ENDSCROLL: return "SB_ENDSCROLL";
713   case SB_INTERNAL: return "SB_INTERNAL";
714   default: return "unknown";
715   }
716 }
717 
718 
719 /******** Notification functions ************************************/
720 
721 static int get_ansi_notification(UINT unicodeNotificationCode)
722 {
723     switch (unicodeNotificationCode)
724     {
725     case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
726     case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
727     case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
728     case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
729     case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
730     case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
731     /* header forwards */
732     case HDN_TRACKW: return HDN_TRACKA;
733     case HDN_ENDTRACKW: return HDN_ENDTRACKA;
734     case HDN_BEGINDRAG: return HDN_BEGINDRAG;
735     case HDN_ENDDRAG: return HDN_ENDDRAG;
736     case HDN_ITEMCHANGINGW: return HDN_ITEMCHANGINGA;
737     case HDN_ITEMCHANGEDW: return HDN_ITEMCHANGEDA;
738     case HDN_ITEMCLICKW: return HDN_ITEMCLICKA;
739     case HDN_DIVIDERDBLCLICKW: return HDN_DIVIDERDBLCLICKA;
740     }
741     ERR("unknown notification %x\n", unicodeNotificationCode);
742     assert(FALSE);
743     return 0;
744 }
745 
746 /* forwards header notifications to listview parent */
747 static LRESULT notify_forward_header(const LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
748 {
749     NMHEADERA nmhA;
750     HDITEMA hditema;
751     HD_TEXTFILTERA textfilter;
752     LPSTR text = NULL, filter = NULL;
753     LRESULT ret;
754 
755     /* on unicode format exit earlier */
756     if (infoPtr->notifyFormat == NFR_UNICODE)
757         return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
758                             (WPARAM)lpnmh->hdr.idFrom, (LPARAM)lpnmh);
759 
760     /* header always supplies unicode notifications,
761        all we have to do is to convert strings to ANSI */
762     nmhA = *(NMHEADERA*)lpnmh;
763     if (lpnmh->pitem)
764     {
765         hditema = *(HDITEMA*)lpnmh->pitem;
766         nmhA.pitem = &hditema;
767         /* convert item text */
768         if (lpnmh->pitem->mask & HDI_TEXT)
769         {
770             hditema.pszText = NULL;
771             Str_SetPtrWtoA(&hditema.pszText, lpnmh->pitem->pszText);
772             text = hditema.pszText;
773         }
774         /* convert filter text */
775         if ((lpnmh->pitem->mask & HDI_FILTER) && (lpnmh->pitem->type == HDFT_ISSTRING) &&
776              lpnmh->pitem->pvFilter)
777         {
778             hditema.pvFilter = &textfilter;
779             textfilter = *(HD_TEXTFILTERA*)(lpnmh->pitem->pvFilter);
780             textfilter.pszText = NULL;
781             Str_SetPtrWtoA(&textfilter.pszText, ((HD_TEXTFILTERW*)lpnmh->pitem->pvFilter)->pszText);
782             filter = textfilter.pszText;
783         }
784     }
785     nmhA.hdr.code = get_ansi_notification(lpnmh->hdr.code);
786 
787     ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
788                        (WPARAM)nmhA.hdr.idFrom, (LPARAM)&nmhA);
789 
790     /* cleanup */
791     Free(text);
792     Free(filter);
793 
794     return ret;
795 }
796 
797 static LRESULT notify_hdr(const LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
798 {
799     LRESULT result;
800     
801     TRACE("(code=%d)\n", code);
802 
803     pnmh->hwndFrom = infoPtr->hwndSelf;
804     pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
805     pnmh->code = code;
806     result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, pnmh->idFrom, (LPARAM)pnmh);
807 
808     TRACE("  <= %ld\n", result);
809 
810     return result;
811 }
812 
813 static inline BOOL notify(const LISTVIEW_INFO *infoPtr, INT code)
814 {
815     NMHDR nmh;
816     HWND hwnd = infoPtr->hwndSelf;
817     notify_hdr(infoPtr, code, &nmh);
818     return IsWindow(hwnd);
819 }
820 
821 static inline void notify_itemactivate(const LISTVIEW_INFO *infoPtr, const LVHITTESTINFO *htInfo)
822 {
823     NMITEMACTIVATE nmia;
824     LVITEMW item;
825 
826     if (htInfo) {
827       nmia.uNewState = 0;
828       nmia.uOldState = 0;
829       nmia.uChanged  = 0;
830       nmia.uKeyFlags = 0;
831       
832       item.mask = LVIF_PARAM|LVIF_STATE;
833       item.iItem = htInfo->iItem;
834       item.iSubItem = 0;
835       if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
836           nmia.lParam = item.lParam;
837           nmia.uOldState = item.state;
838           nmia.uNewState = item.state | LVIS_ACTIVATING;
839           nmia.uChanged  = LVIF_STATE;
840       }
841       
842       nmia.iItem = htInfo->iItem;
843       nmia.iSubItem = htInfo->iSubItem;
844       nmia.ptAction = htInfo->pt;     
845       
846       if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
847       if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
848       if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
849     }
850     notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
851 }
852 
853 static inline LRESULT notify_listview(const LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
854 {
855     TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
856     return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
857 }
858 
859 static BOOL notify_click(const LISTVIEW_INFO *infoPtr, INT code, const LVHITTESTINFO *lvht)
860 {
861     NMITEMACTIVATE nmia;
862     LVITEMW item;
863     HWND hwnd = infoPtr->hwndSelf;
864 
865     TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht)); 
866     ZeroMemory(&nmia, sizeof(nmia));
867     nmia.iItem = lvht->iItem;
868     nmia.iSubItem = lvht->iSubItem;
869     nmia.ptAction = lvht->pt;
870     item.mask = LVIF_PARAM;
871     item.iItem = lvht->iItem;
872     item.iSubItem = 0;
873     if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmia.lParam = item.lParam;
874     notify_hdr(infoPtr, code, (LPNMHDR)&nmia);
875     return IsWindow(hwnd);
876 }
877 
878 static BOOL notify_deleteitem(const LISTVIEW_INFO *infoPtr, INT nItem)
879 {
880     NMLISTVIEW nmlv;
881     LVITEMW item;
882     HWND hwnd = infoPtr->hwndSelf;
883 
884     ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
885     nmlv.iItem = nItem;
886     item.mask = LVIF_PARAM;
887     item.iItem = nItem;
888     item.iSubItem = 0;
889     if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
890     notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
891     return IsWindow(hwnd);
892 }
893 
894 /*
895   Send notification. depends on dispinfoW having same
896   structure as dispinfoA.
897   infoPtr : listview struct
898   notificationCode : *Unicode* notification code
899   pdi : dispinfo structure (can be unicode or ansi)
900   isW : TRUE if dispinfo is Unicode
901 */
902 static BOOL notify_dispinfoT(const LISTVIEW_INFO *infoPtr, UINT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
903 {
904     BOOL bResult = FALSE;
905     BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
906     INT cchTempBufMax = 0, savCchTextMax = 0;
907     UINT realNotifCode;
908     LPWSTR pszTempBuf = NULL, savPszText = NULL;
909 
910     if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
911     {
912         convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
913         convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
914     }
915 
916     if (convertToAnsi || convertToUnicode)
917     {
918         if (notificationCode != LVN_GETDISPINFOW)
919         {
920             cchTempBufMax = convertToUnicode ?
921                 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
922                 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
923         }
924         else
925         {
926             cchTempBufMax = pdi->item.cchTextMax;
927             *pdi->item.pszText = 0; /* make sure we don't process garbage */
928         }
929 
930         pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
931         if (!pszTempBuf) return FALSE;
932 
933         if (convertToUnicode)
934             MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
935                                 pszTempBuf, cchTempBufMax);
936         else
937             WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
938                                 cchTempBufMax, NULL, NULL);
939 
940         savCchTextMax = pdi->item.cchTextMax;
941         savPszText = pdi->item.pszText;
942         pdi->item.pszText = pszTempBuf;
943         pdi->item.cchTextMax = cchTempBufMax;
944     }
945 
946     if (infoPtr->notifyFormat == NFR_ANSI)
947         realNotifCode = get_ansi_notification(notificationCode);
948     else
949         realNotifCode = notificationCode;
950     TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
951     bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
952 
953     if (convertToUnicode || convertToAnsi)
954     {
955         if (convertToUnicode) /* note : pointer can be changed by app ! */
956             WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
957                                 savCchTextMax, NULL, NULL);
958         else
959             MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
960                                 savPszText, savCchTextMax);
961         pdi->item.pszText = savPszText; /* restores our buffer */
962         pdi->item.cchTextMax = savCchTextMax;
963         Free (pszTempBuf);
964     }
965     return bResult;
966 }
967 
968 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, const LISTVIEW_INFO *infoPtr, HDC hdc,
969                             const RECT *rcBounds, const LVITEMW *lplvItem)
970 {
971     ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
972     lpnmlvcd->nmcd.hdc = hdc;
973     lpnmlvcd->nmcd.rc = *rcBounds;
974     lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
975     lpnmlvcd->clrText   = infoPtr->clrText;
976     if (!lplvItem) return;
977     lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
978     lpnmlvcd->iSubItem = lplvItem->iSubItem;
979     if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
980     if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
981     if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
982     lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
983 }
984 
985 static inline DWORD notify_customdraw (const LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
986 {
987     BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
988     DWORD result;
989 
990     lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
991     if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM; 
992     if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
993     if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
994     result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
995     if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
996     return result;
997 }
998 
999 static void prepaint_setup (const LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd, BOOL SubItem)
1000 {
1001     if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
1002         lpnmlvcd->clrTextBk = comctl32_color.clrWindow;
1003     if (lpnmlvcd->clrText == CLR_DEFAULT)
1004         lpnmlvcd->clrText = comctl32_color.clrWindowText;
1005 
1006     /* apparently, for selected items, we have to override the returned values */
1007     if (!SubItem)
1008     {
1009         if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
1010         {
1011             if (infoPtr->bFocus)
1012             {
1013                 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
1014                 lpnmlvcd->clrText   = comctl32_color.clrHighlightText;
1015             }
1016             else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
1017             {
1018                 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
1019                 lpnmlvcd->clrText   = comctl32_color.clrBtnText;
1020             }
1021         }
1022     }
1023 
1024     /* Set the text attributes */
1025     if (lpnmlvcd->clrTextBk != CLR_NONE)
1026     {
1027         SetBkMode(hdc, OPAQUE);
1028         SetBkColor(hdc,lpnmlvcd->clrTextBk);
1029     }
1030     else
1031         SetBkMode(hdc, TRANSPARENT);
1032     SetTextColor(hdc, lpnmlvcd->clrText);
1033 }
1034 
1035 static inline DWORD notify_postpaint (const LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
1036 {
1037     return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
1038 }
1039 
1040 /* returns TRUE when repaint needed, FALSE otherwise */
1041 static BOOL notify_measureitem(LISTVIEW_INFO *infoPtr)
1042 {
1043     MEASUREITEMSTRUCT mis;
1044     mis.CtlType = ODT_LISTVIEW;
1045     mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1046     mis.itemID = -1;
1047     mis.itemWidth = 0;
1048     mis.itemData = 0;
1049     mis.itemHeight= infoPtr->nItemHeight;
1050     SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
1051     if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
1052     {
1053         infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
1054         return TRUE;
1055     }
1056     return FALSE;
1057 }
1058 
1059 /******** Item iterator functions **********************************/
1060 
1061 static RANGES ranges_create(int count);
1062 static void ranges_destroy(RANGES ranges);
1063 static BOOL ranges_add(RANGES ranges, RANGE range);
1064 static BOOL ranges_del(RANGES ranges, RANGE range);
1065 static void ranges_dump(RANGES ranges);
1066 
1067 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
1068 {
1069     RANGE range = { nItem, nItem + 1 };
1070 
1071     return ranges_add(ranges, range);
1072 }
1073 
1074 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
1075 {
1076     RANGE range = { nItem, nItem + 1 };
1077 
1078     return ranges_del(ranges, range);
1079 }
1080 
1081 /***
1082  * ITERATOR DOCUMENTATION
1083  *
1084  * The iterator functions allow for easy, and convenient iteration
1085  * over items of interest in the list. Typically, you create a
1086  * iterator, use it, and destroy it, as such:
1087  *   ITERATOR i;
1088  *
1089  *   iterator_xxxitems(&i, ...);
1090  *   while (iterator_{prev,next}(&i)
1091  *   {
1092  *       //code which uses i.nItem
1093  *   }
1094  *   iterator_destroy(&i);
1095  *
1096  *   where xxx is either: framed, or visible.
1097  * Note that it is important that the code destroys the iterator
1098  * after it's done with it, as the creation of the iterator may
1099  * allocate memory, which thus needs to be freed.
1100  * 
1101  * You can iterate both forwards, and backwards through the list,
1102  * by using iterator_next or iterator_prev respectively.
1103  * 
1104  * Lower numbered items are draw on top of higher number items in
1105  * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1106  * items may overlap). So, to test items, you should use
1107  *    iterator_next
1108  * which lists the items top to bottom (in Z-order).
1109  * For drawing items, you should use
1110  *    iterator_prev
1111  * which lists the items bottom to top (in Z-order).
1112  * If you keep iterating over the items after the end-of-items
1113  * marker (-1) is returned, the iterator will start from the
1114  * beginning. Typically, you don't need to test for -1,
1115  * because iterator_{next,prev} will return TRUE if more items
1116  * are to be iterated over, or FALSE otherwise.
1117  *
1118  * Note: the iterator is defined to be bidirectional. That is,
1119  *       any number of prev followed by any number of next, or
1120  *       five versa, should leave the iterator at the same item:
1121  *           prev * n, next * n = next * n, prev * n
1122  *
1123  * The iterator has a notion of an out-of-order, special item,
1124  * which sits at the start of the list. This is used in
1125  * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1126  * which needs to be first, as it may overlap other items.
1127  *           
1128  * The code is a bit messy because we have:
1129  *   - a special item to deal with
1130  *   - simple range, or composite range
1131  *   - empty range.
1132  * If you find bugs, or want to add features, please make sure you
1133  * always check/modify *both* iterator_prev, and iterator_next.
1134  */
1135 
1136 /****
1137  * This function iterates through the items in increasing order,
1138  * but prefixed by the special item, then -1. That is:
1139  *    special, 1, 2, 3, ..., n, -1.
1140  * Each item is listed only once.
1141  */
1142 static inline BOOL iterator_next(ITERATOR* i)
1143 {
1144     if (i->nItem == -1)
1145     {
1146         i->nItem = i->nSpecial;
1147         if (i->nItem != -1) return TRUE;
1148     }
1149     if (i->nItem == i->nSpecial)
1150     {
1151         if (i->ranges) i->index = 0;
1152         goto pickarange;
1153     }
1154 
1155     i->nItem++;
1156 testitem:
1157     if (i->nItem == i->nSpecial) i->nItem++;
1158     if (i->nItem < i->range.upper) return TRUE;
1159 
1160 pickarange:
1161     if (i->ranges)
1162     {
1163         if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1164             i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1165         else goto end;
1166     }
1167     else if (i->nItem >= i->range.upper) goto end;
1168 
1169     i->nItem = i->range.lower;
1170     if (i->nItem >= 0) goto testitem;
1171 end:
1172     i->nItem = -1;
1173     return FALSE;
1174 }
1175 
1176 /****
1177  * This function iterates through the items in decreasing order,
1178  * followed by the special item, then -1. That is:
1179  *    n, n-1, ..., 3, 2, 1, special, -1.
1180  * Each item is listed only once.
1181  */
1182 static inline BOOL iterator_prev(ITERATOR* i)
1183 {
1184     BOOL start = FALSE;
1185 
1186     if (i->nItem == -1)
1187     {
1188         start = TRUE;
1189         if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1190         goto pickarange;
1191     }
1192     if (i->nItem == i->nSpecial)
1193     {
1194         i->nItem = -1;
1195         return FALSE;
1196     }
1197 
1198 testitem:
1199     i->nItem--;
1200     if (i->nItem == i->nSpecial) i->nItem--;
1201     if (i->nItem >= i->range.lower) return TRUE;
1202 
1203 pickarange:
1204     if (i->ranges)
1205     {
1206         if (i->index > 0)
1207             i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1208         else goto end;
1209     }
1210     else if (!start && i->nItem < i->range.lower) goto end;
1211 
1212     i->nItem = i->range.upper;
1213     if (i->nItem > 0) goto testitem;
1214 end:
1215     return (i->nItem = i->nSpecial) != -1;
1216 }
1217 
1218 static RANGE iterator_range(const ITERATOR *i)
1219 {
1220     RANGE range;
1221 
1222     if (!i->ranges) return i->range;
1223 
1224     if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1225     {
1226         range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1227         range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1228     }
1229     else range.lower = range.upper = 0;
1230 
1231     return range;
1232 }
1233 
1234 /***
1235  * Releases resources associated with this ierator.
1236  */
1237 static inline void iterator_destroy(const ITERATOR *i)
1238 {
1239     ranges_destroy(i->ranges);
1240 }
1241 
1242 /***
1243  * Create an empty iterator.
1244  */
1245 static inline BOOL iterator_empty(ITERATOR* i)
1246 {
1247     ZeroMemory(i, sizeof(*i));
1248     i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1249     return TRUE;
1250 }
1251 
1252 /***
1253  * Create an iterator over a range.
1254  */
1255 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1256 {
1257     iterator_empty(i);
1258     i->range = range;
1259     return TRUE;
1260 }
1261 
1262 /***
1263  * Create an iterator over a bunch of ranges.
1264  * Please note that the iterator will take ownership of the ranges,
1265  * and will free them upon destruction.
1266  */
1267 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1268 {
1269     iterator_empty(i);
1270     i->ranges = ranges;
1271     return TRUE;
1272 }
1273 
1274 /***
1275  * Creates an iterator over the items which intersect frame.
1276  * Uses absolute coordinates rather than compensating for the current offset.
1277  */
1278 static BOOL iterator_frameditems_absolute(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *frame)
1279 {
1280     RECT rcItem, rcTemp;
1281     
1282     /* in case we fail, we want to return an empty iterator */
1283     if (!iterator_empty(i)) return FALSE;
1284 
1285     TRACE("(frame=%s)\n", wine_dbgstr_rect(frame));
1286 
1287     if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)
1288     {
1289         INT nItem;
1290         
1291         if (infoPtr->uView == LV_VIEW_ICON && infoPtr->nFocusedItem != -1)
1292         {
1293             LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1294             if (IntersectRect(&rcTemp, &rcItem, frame))
1295                 i->nSpecial = infoPtr->nFocusedItem;
1296         }
1297         if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1298         /* to do better here, we need to have PosX, and PosY sorted */
1299         TRACE("building icon ranges:\n");
1300         for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1301         {
1302             rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1303             rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1304             rcItem.right = rcItem.left + infoPtr->nItemWidth;
1305             rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1306             if (IntersectRect(&rcTemp, &rcItem, frame))
1307                 ranges_additem(i->ranges, nItem);
1308         }
1309         return TRUE;
1310     }
1311     else if (infoPtr->uView == LV_VIEW_DETAILS)
1312     {
1313         RANGE range;
1314         
1315         if (frame->left >= infoPtr->nItemWidth) return TRUE;
1316         if (frame->top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1317         
1318         range.lower = max(frame->top / infoPtr->nItemHeight, 0);
1319         range.upper = min((frame->bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1320         if (range.upper <= range.lower) return TRUE;
1321         if (!iterator_rangeitems(i, range)) return FALSE;
1322         TRACE("    report=%s\n", debugrange(&i->range));
1323     }
1324     else
1325     {
1326         INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1327         INT nFirstRow = max(frame->top / infoPtr->nItemHeight, 0);
1328         INT nLastRow = min((frame->bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1329         INT nFirstCol;
1330         INT nLastCol;
1331         INT lower;
1332         RANGE item_range;
1333         INT nCol;
1334 
1335         if (infoPtr->nItemWidth)
1336         {
1337             nFirstCol = max(frame->left / infoPtr->nItemWidth, 0);
1338             nLastCol  = min((frame->right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1339         }
1340         else
1341         {
1342             nFirstCol = max(frame->left, 0);
1343             nLastCol  = min(frame->right - 1, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1344         }
1345 
1346         lower = nFirstCol * nPerCol + nFirstRow;
1347 
1348         TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1349               nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1350         
1351         if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1352 
1353         if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1354         TRACE("building list ranges:\n");
1355         for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1356         {
1357             item_range.lower = nCol * nPerCol + nFirstRow;
1358             if(item_range.lower >= infoPtr->nItemCount) break;
1359             item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1360             TRACE("   list=%s\n", debugrange(&item_range));
1361             ranges_add(i->ranges, item_range);
1362         }
1363     }
1364 
1365     return TRUE;
1366 }
1367 
1368 /***
1369  * Creates an iterator over the items which intersect lprc.
1370  */
1371 static BOOL iterator_frameditems(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *lprc)
1372 {
1373     RECT frame = *lprc;
1374     POINT Origin;
1375 
1376     TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc));
1377 
1378     LISTVIEW_GetOrigin(infoPtr, &Origin);
1379     OffsetRect(&frame, -Origin.x, -Origin.y);
1380 
1381     return iterator_frameditems_absolute(i, infoPtr, &frame);
1382 }
1383 
1384 /***
1385  * Creates an iterator over the items which intersect the visible region of hdc.
1386  */
1387 static BOOL iterator_visibleitems(ITERATOR *i, const LISTVIEW_INFO *infoPtr, HDC  hdc)
1388 {
1389     POINT Origin, Position;
1390     RECT rcItem, rcClip;
1391     INT rgntype;
1392     
1393     rgntype = GetClipBox(hdc, &rcClip);
1394     if (rgntype == NULLREGION) return iterator_empty(i);
1395     if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1396     if (rgntype == SIMPLEREGION) return TRUE;
1397 
1398     /* first deal with the special item */
1399     if (i->nSpecial != -1)
1400     {
1401         LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1402         if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1403     }
1404     
1405     /* if we can't deal with the region, we'll just go with the simple range */
1406     LISTVIEW_GetOrigin(infoPtr, &Origin);
1407     TRACE("building visible range:\n");
1408     if (!i->ranges && i->range.lower < i->range.upper)
1409     {
1410         if (!(i->ranges = ranges_create(50))) return TRUE;
1411         if (!ranges_add(i->ranges, i->range))
1412         {
1413             ranges_destroy(i->ranges);
1414             i->ranges = 0;
1415             return TRUE;
1416         }
1417     }
1418 
1419     /* now delete the invisible items from the list */
1420     while(iterator_next(i))
1421     {
1422         LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1423         rcItem.left = (infoPtr->uView == LV_VIEW_DETAILS) ? Origin.x : Position.x + Origin.x;
1424         rcItem.top = Position.y + Origin.y;
1425         rcItem.right = rcItem.left + infoPtr->nItemWidth;
1426         rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1427         if (!RectVisible(hdc, &rcItem))
1428             ranges_delitem(i->ranges, i->nItem);
1429     }
1430     /* the iterator should restart on the next iterator_next */
1431     TRACE("done\n");
1432     
1433     return TRUE;
1434 }
1435 
1436 /******** Misc helper functions ************************************/
1437 
1438 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1439                                       WPARAM wParam, LPARAM lParam, BOOL isW)
1440 {
1441     if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1442     else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1443 }
1444 
1445 static inline BOOL is_autoarrange(const LISTVIEW_INFO *infoPtr)
1446 {
1447     return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1448            (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON);
1449 }
1450 
1451 static void toggle_checkbox_state(LISTVIEW_INFO *infoPtr, INT nItem)
1452 {
1453     DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
1454     if(state == 1 || state == 2)
1455     {
1456         LVITEMW lvitem;
1457         state ^= 3;
1458         lvitem.state = INDEXTOSTATEIMAGEMASK(state);
1459         lvitem.stateMask = LVIS_STATEIMAGEMASK;
1460         LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
1461     }
1462 }
1463 
1464 /* this should be called after window style got updated,
1465    it used to reset view state to match current window style */
1466 static inline void map_style_view(LISTVIEW_INFO *infoPtr)
1467 {
1468     switch (infoPtr->dwStyle & LVS_TYPEMASK)
1469     {
1470     case LVS_ICON:
1471         infoPtr->uView = LV_VIEW_ICON;
1472         break;
1473     case LVS_REPORT:
1474         infoPtr->uView = LV_VIEW_DETAILS;
1475         break;
1476     case LVS_SMALLICON:
1477         infoPtr->uView = LV_VIEW_SMALLICON;
1478         break;
1479     case LVS_LIST:
1480         infoPtr->uView = LV_VIEW_LIST;
1481     }
1482 }
1483 
1484 /* computes next item id value */
1485 static DWORD get_next_itemid(const LISTVIEW_INFO *infoPtr)
1486 {
1487     INT count = DPA_GetPtrCount(infoPtr->hdpaItemIds);
1488 
1489     if (count > 0)
1490     {
1491         ITEM_ID *lpID = DPA_GetPtr(infoPtr->hdpaItemIds, count - 1);
1492         return lpID->id + 1;
1493     }
1494     return 0;
1495 }
1496 
1497 /******** Internal API functions ************************************/
1498 
1499 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(const LISTVIEW_INFO *infoPtr, INT nSubItem)
1500 {
1501     static COLUMN_INFO mainItem;
1502 
1503     if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1504     assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1505 
1506     /* update cached column rectangles */
1507     if (infoPtr->colRectsDirty)
1508     {
1509         COLUMN_INFO *info;
1510         LISTVIEW_INFO *Ptr = (LISTVIEW_INFO*)infoPtr;
1511         INT i;
1512 
1513         for (i = 0; i < DPA_GetPtrCount(infoPtr->hdpaColumns); i++) {
1514             info = DPA_GetPtr(infoPtr->hdpaColumns, i);
1515             SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, i, (LPARAM)&info->rcHeader);
1516         }
1517         Ptr->colRectsDirty = FALSE;
1518     }
1519 
1520     return DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1521 }
1522 
1523 static INT LISTVIEW_CreateHeader(LISTVIEW_INFO *infoPtr)
1524 {
1525     DWORD dFlags = WS_CHILD | HDS_HORZ | HDS_FULLDRAG | HDS_DRAGDROP;
1526     HINSTANCE hInst;
1527 
1528     if (infoPtr->hwndHeader) return 0;
1529 
1530     TRACE("Creating header for list %p\n", infoPtr->hwndSelf);
1531 
1532     /* setup creation flags */
1533     dFlags |= (LVS_NOSORTHEADER & infoPtr->dwStyle) ? 0 : HDS_BUTTONS;
1534     dFlags |= (LVS_NOCOLUMNHEADER & infoPtr->dwStyle) ? HDS_HIDDEN : 0;
1535 
1536     hInst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
1537 
1538     /* create header */
1539     infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL, dFlags,
1540       0, 0, 0, 0, infoPtr->hwndSelf, NULL, hInst, NULL);
1541     if (!infoPtr->hwndHeader) return -1;
1542 
1543     /* set header unicode format */
1544     SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, TRUE, 0);
1545 
1546     /* set header font */
1547     SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
1548 
1549     LISTVIEW_UpdateSize(infoPtr);
1550 
1551     return 0;
1552 }
1553 
1554 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO *infoPtr, INT nSubItem, LPRECT lprc)
1555 {
1556     *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1557 }
1558         
1559 static inline BOOL LISTVIEW_GetItemW(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1560 {
1561     return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1562 }
1563 
1564 /* used to handle collapse main item column case */
1565 static inline BOOL LISTVIEW_DrawFocusRect(const LISTVIEW_INFO *infoPtr, HDC hdc)
1566 {
1567     return (infoPtr->rcFocus.left < infoPtr->rcFocus.right) ?
1568             DrawFocusRect(hdc, &infoPtr->rcFocus) : FALSE;
1569 }
1570 
1571 /* Listview invalidation functions: use _only_ these functions to invalidate */
1572 
1573 static inline BOOL is_redrawing(const LISTVIEW_INFO *infoPtr)
1574 {
1575     return infoPtr->bRedraw;
1576 }
1577 
1578 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO *infoPtr, const RECT* rect)
1579 {
1580     if(!is_redrawing(infoPtr)) return; 
1581     TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect));
1582     InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1583 }
1584 
1585 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO *infoPtr, INT nItem)
1586 {
1587     RECT rcBox;
1588 
1589     if(!is_redrawing(infoPtr)) return; 
1590     LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1591     LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1592 }
1593 
1594 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1595 {
1596     POINT Origin, Position;
1597     RECT rcBox;
1598     
1599     if(!is_redrawing(infoPtr)) return; 
1600     assert (infoPtr->uView == LV_VIEW_DETAILS);
1601     LISTVIEW_GetOrigin(infoPtr, &Origin);
1602     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1603     LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1604     rcBox.top = 0;
1605     rcBox.bottom = infoPtr->nItemHeight;
1606     OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1607     LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1608 }
1609 
1610 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO *infoPtr)
1611 {
1612     LISTVIEW_InvalidateRect(infoPtr, NULL);
1613 }
1614 
1615 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO *infoPtr, INT nColumn)
1616 {
1617     RECT rcCol;
1618     
1619     if(!is_redrawing(infoPtr)) return; 
1620     LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1621     rcCol.top = infoPtr->rcList.top;
1622     rcCol.bottom = infoPtr->rcList.bottom;
1623     LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1624 }
1625 
1626 /***
1627  * DESCRIPTION:
1628  * Retrieves the number of items that can fit vertically in the client area.
1629  *
1630  * PARAMETER(S):
1631  * [I] infoPtr : valid pointer to the listview structure
1632  *
1633  * RETURN:
1634  * Number of items per row.
1635  */
1636 static inline INT LISTVIEW_GetCountPerRow(const LISTVIEW_INFO *infoPtr)
1637 {
1638     INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1639 
1640     return max(nListWidth/(infoPtr->nItemWidth ? infoPtr->nItemWidth : 1), 1);
1641 }
1642 
1643 /***
1644  * DESCRIPTION:
1645  * Retrieves the number of items that can fit horizontally in the client
1646  * area.
1647  *
1648  * PARAMETER(S):
1649  * [I] infoPtr : valid pointer to the listview structure
1650  *
1651  * RETURN:
1652  * Number of items per column.
1653  */
1654 static inline INT LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO *infoPtr)
1655 {
1656     INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1657 
1658     return max(nListHeight / infoPtr->nItemHeight, 1);
1659 }
1660 
1661 
1662 /*************************************************************************
1663  *              LISTVIEW_ProcessLetterKeys
1664  *
1665  *  Processes keyboard messages generated by pressing the letter keys
1666  *  on the keyboard.
1667  *  What this does is perform a case insensitive search from the
1668  *  current position with the following quirks:
1669  *  - If two chars or more are pressed in quick succession we search
1670  *    for the corresponding string (e.g. 'abc').
1671  *  - If there is a delay we wipe away the current search string and
1672  *    restart with just that char.
1673  *  - If the user keeps pressing the same character, whether slowly or
1674  *    fast, so that the search string is entirely composed of this
1675  *    character ('aaaaa' for instance), then we search for first item
1676  *    that starting with that character.
1677  *  - If the user types the above character in quick succession, then
1678  *    we must also search for the corresponding string ('aaaaa'), and
1679  *    go to that string if there is a match.
1680  *
1681  * PARAMETERS
1682  *   [I] hwnd : handle to the window
1683  *   [I] charCode : the character code, the actual character
1684  *   [I] keyData : key data
1685  *
1686  * RETURNS
1687  *
1688  *  Zero.
1689  *
1690  * BUGS
1691  *
1692  *  - The current implementation has a list of characters it will
1693  *    accept and it ignores everything else. In particular it will
1694  *    ignore accentuated characters which seems to match what
1695  *    Windows does. But I'm not sure it makes sense to follow
1696  *    Windows there.
1697  *  - We don't sound a beep when the search fails.
1698  *
1699  * SEE ALSO
1700  *
1701  *  TREEVIEW_ProcessLetterKeys
1702  */
1703 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1704 {
1705     INT nItem;
1706     INT endidx,idx;
1707     LVITEMW item;
1708     WCHAR buffer[MAX_PATH];
1709     DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1710 
1711     /* simple parameter checking */
1712     if (!charCode || !keyData) return 0;
1713 
1714     /* only allow the valid WM_CHARs through */
1715     if (!isalnumW(charCode) &&
1716         charCode != '.' && charCode != '`' && charCode != '!' &&
1717         charCode != '@' && charCode != '#' && charCode != '$' &&
1718         charCode != '%' && charCode != '^' && charCode != '&' &&
1719         charCode != '*' && charCode != '(' && charCode != ')' &&
1720         charCode != '-' && charCode != '_' && charCode != '+' &&
1721         charCode != '=' && charCode != '\\'&& charCode != ']' &&
1722         charCode != '}' && charCode != '[' && charCode != '{' &&
1723         charCode != '/' && charCode != '?' && charCode != '>' &&
1724         charCode != '<' && charCode != ',' && charCode != '~')
1725         return 0;
1726 
1727     /* if there's one item or less, there is no where to go */
1728     if (infoPtr->nItemCount <= 1) return 0;
1729 
1730     /* update the search parameters */
1731     infoPtr->lastKeyPressTimestamp = GetTickCount();
1732     if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1733         if (infoPtr->nSearchParamLength < MAX_PATH-1)
1734             infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1735         if (infoPtr->charCode != charCode)
1736             infoPtr->charCode = charCode = 0;
1737     } else {
1738         infoPtr->charCode=charCode;
1739         infoPtr->szSearchParam[0]=charCode;
1740         infoPtr->nSearchParamLength=1;
1741         /* Redundant with the 1 char string */
1742         charCode=0;
1743     }
1744 
1745     /* and search from the current position */
1746     nItem=-1;
1747     if (infoPtr->nFocusedItem >= 0) {
1748         endidx=infoPtr->nFocusedItem;
1749         idx=endidx;
1750         /* if looking for single character match,
1751          * then we must always move forward
1752          */
1753         if (infoPtr->nSearchParamLength == 1)
1754             idx++;
1755     } else {
1756         endidx=infoPtr->nItemCount;
1757         idx=0;
1758     }
1759 
1760     /* Let application handle this for virtual listview */
1761     if (infoPtr->dwStyle & LVS_OWNERDATA)
1762     {
1763         NMLVFINDITEMW nmlv;
1764         LVFINDINFOW lvfi;
1765 
1766         ZeroMemory(&lvfi, sizeof(lvfi));
1767         lvfi.flags = (LVFI_WRAP | LVFI_PARTIAL);
1768         infoPtr->szSearchParam[infoPtr->nSearchParamLength] = '\0';
1769         lvfi.psz = infoPtr->szSearchParam;
1770         nmlv.iStart = idx;
1771         nmlv.lvfi = lvfi;
1772 
1773         nItem = notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
1774 
1775         if (nItem != -1)
1776             LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1777 
1778         return 0;
1779     }
1780 
1781     do {
1782         if (idx == infoPtr->nItemCount) {
1783             if (endidx == infoPtr->nItemCount || endidx == 0)
1784                 break;
1785             idx=0;
1786         }
1787 
1788         /* get item */
1789         item.mask = LVIF_TEXT;
1790         item.iItem = idx;
1791         item.iSubItem = 0;
1792         item.pszText = buffer;
1793         item.cchTextMax = MAX_PATH;
1794         if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1795 
1796         /* check for a match */
1797         if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1798             nItem=idx;
1799             break;
1800         } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1801                     (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1802             /* This would work but we must keep looking for a longer match */
1803             nItem=idx;
1804         }
1805         idx++;
1806     } while (idx != endidx);
1807 
1808     if (nItem != -1)
1809         LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1810 
1811     return 0;
1812 }
1813 
1814 /*************************************************************************
1815  * LISTVIEW_UpdateHeaderSize [Internal]
1816  *
1817  * Function to resize the header control
1818  *
1819  * PARAMS
1820  * [I]  hwnd : handle to a window
1821  * [I]  nNewScrollPos : scroll pos to set
1822  *
1823  * RETURNS
1824  * None.
1825  */
1826 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1827 {
1828     RECT winRect;
1829     POINT point[2];
1830 
1831     TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1832 
1833     if (!infoPtr->hwndHeader)  return;
1834 
1835     GetWindowRect(infoPtr->hwndHeader, &winRect);
1836     point[0].x = winRect.left;
1837     point[0].y = winRect.top;
1838     point[1].x = winRect.right;
1839     point[1].y = winRect.bottom;
1840 
1841     MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1842     point[0].x = -nNewScrollPos;
1843     point[1].x += nNewScrollPos;
1844 
1845     SetWindowPos(infoPtr->hwndHeader,0,
1846         point[0].x,point[0].y,point[1].x,point[1].y,
1847         (infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW |
1848         SWP_NOZORDER | SWP_NOACTIVATE);
1849 }
1850 
1851 /***
1852  * DESCRIPTION:
1853  * Update the scrollbars. This functions should be called whenever
1854  * the content, size or view changes.
1855  *
1856  * PARAMETER(S):
1857  * [I] infoPtr : valid pointer to the listview structure
1858  *
1859  * RETURN:
1860  * None
1861  */
1862 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *infoPtr)
1863 {
1864     SCROLLINFO horzInfo, vertInfo;
1865     INT dx, dy;
1866 
1867     if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1868 
1869     ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1870     horzInfo.cbSize = sizeof(SCROLLINFO);
1871     horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1872 
1873     /* for now, we'll set info.nMax to the _count_, and adjust it later */
1874     if (infoPtr->uView == LV_VIEW_LIST)
1875     {
1876         INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1877         horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1878 
1879         /* scroll by at least one column per page */
1880         if(horzInfo.nPage < infoPtr->nItemWidth)
1881                 horzInfo.nPage = infoPtr->nItemWidth;
1882 
1883         if (infoPtr->nItemWidth)
1884             horzInfo.nPage /= infoPtr->nItemWidth;
1885     }
1886     else if (infoPtr->uView == LV_VIEW_DETAILS)
1887     {
1888         horzInfo.nMax = infoPtr->nItemWidth;
1889     }
1890     else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
1891     {
1892         RECT rcView;
1893 
1894         if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1895     }
1896   
1897     horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1898     horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1899     dx = GetScrollPos(infoPtr->hwndSelf, SB_HORZ);
1900     dx -= SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1901     TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1902 
1903     /* Setting the horizontal scroll can change the listview size
1904      * (and potentially everything else) so we need to recompute
1905      * everything again for the vertical scroll
1906      */
1907 
1908     ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1909     vertInfo.cbSize = sizeof(SCROLLINFO);
1910     vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1911 
1912     if (infoPtr->uView == LV_VIEW_DETAILS)
1913     {
1914         vertInfo.nMax = infoPtr->nItemCount;
1915         
1916         /* scroll by at least one page */
1917         if(vertInfo.nPage < infoPtr->nItemHeight)
1918           vertInfo.nPage = infoPtr->nItemHeight;
1919 
1920         if (infoPtr->nItemHeight > 0)
1921             vertInfo.nPage /= infoPtr->nItemHeight;
1922     }
1923     else if (infoPtr->uView != LV_VIEW_LIST) /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
1924     {
1925         RECT rcView;
1926 
1927         if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1928     }
1929 
1930     vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1931     vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1932     dy = GetScrollPos(infoPtr->hwndSelf, SB_VERT);
1933     dy -= SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1934     TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1935 
1936     /* Change of the range may have changed the scroll pos. If so move the content */
1937     if (dx != 0 || dy != 0)
1938     {
1939         RECT listRect;
1940         listRect = infoPtr->rcList;
1941         ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &listRect, &listRect, 0, 0,
1942             SW_ERASE | SW_INVALIDATE);
1943     }
1944 
1945     /* Update the Header Control */
1946     if (infoPtr->uView == LV_VIEW_DETAILS)
1947     {
1948         horzInfo.fMask = SIF_POS;
1949         GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1950         LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1951     }
1952 }
1953 
1954 
1955 /***
1956  * DESCRIPTION:
1957  * Shows/hides the focus rectangle. 
1958  *
1959  * PARAMETER(S):
1960  * [I] infoPtr : valid pointer to the listview structure
1961  * [I] fShow : TRUE to show the focus, FALSE to hide it.
1962  *
1963  * RETURN:
1964  * None
1965  */
1966 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO *infoPtr, BOOL fShow)
1967 {
1968     HDC hdc;
1969 
1970     TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1971 
1972     if (infoPtr->nFocusedItem < 0) return;
1973 
1974     /* we need some gymnastics in ICON mode to handle large items */
1975     if (infoPtr->uView == LV_VIEW_ICON)
1976     {
1977         RECT rcBox;
1978 
1979         LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox); 
1980         if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1981         {
1982             LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1983             return;
1984         }
1985     }
1986 
1987     if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1988 
1989     /* for some reason, owner draw should work only in report mode */
1990     if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
1991     {
1992         DRAWITEMSTRUCT dis;
1993         LVITEMW item;
1994 
1995         HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1996         HFONT hOldFont = SelectObject(hdc, hFont);
1997 
1998         item.iItem = infoPtr->nFocusedItem;
1999         item.iSubItem = 0;
2000         item.mask = LVIF_PARAM;
2001         if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
2002            
2003         ZeroMemory(&dis, sizeof(dis)); 
2004         dis.CtlType = ODT_LISTVIEW;
2005         dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
2006         dis.itemID = item.iItem;
2007         dis.itemAction = ODA_FOCUS;
2008         if (fShow) dis.itemState |= ODS_FOCUS;
2009         dis.hwndItem = infoPtr->hwndSelf;
2010         dis.hDC = hdc;
2011         LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
2012         dis.itemData = item.lParam;
2013 
2014         SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
2015 
2016         SelectObject(hdc, hOldFont);
2017     }
2018     else
2019     {
2020         LISTVIEW_DrawFocusRect(infoPtr, hdc);
2021     }
2022 done:
2023     ReleaseDC(infoPtr->hwndSelf, hdc);
2024 }
2025 
2026 /***
2027  * Invalidates all visible selected items.
2028  */
2029 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO *infoPtr)
2030 {
2031     ITERATOR i; 
2032    
2033     iterator_frameditems(&i, infoPtr, &infoPtr->rcList); 
2034     while(iterator_next(&i))
2035     {
2036         if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
2037             LISTVIEW_InvalidateItem(infoPtr, i.nItem);
2038     }
2039     iterator_destroy(&i);
2040 }
2041 
2042             
2043 /***
2044  * DESCRIPTION:            [INTERNAL]
2045  * Computes an item's (left,top) corner, relative to rcView.
2046  * That is, the position has NOT been made relative to the Origin.
2047  * This is deliberate, to avoid computing the Origin over, and
2048  * over again, when this function is called in a loop. Instead,
2049  * one can factor the computation of the Origin before the loop,
2050  * and offset the value returned by this function, on every iteration.
2051  * 
2052  * PARAMETER(S):
2053  * [I] infoPtr : valid pointer to the listview structure
2054  * [I] nItem  : item number
2055  * [O] lpptOrig : item top, left corner
2056  *
2057  * RETURN:
2058  *   None.
2059  */
2060 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
2061 {
2062     assert(nItem >= 0 && nItem < infoPtr->nItemCount);
2063 
2064     if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
2065     {
2066         lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2067         lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2068     }
2069     else if (infoPtr->uView == LV_VIEW_LIST)
2070     {
2071         INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
2072         lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
2073         lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
2074     }
2075     else /* LV_VIEW_DETAILS */
2076     {
2077         lpptPosition->x = REPORT_MARGINX;
2078         /* item is always at zero indexed column */
2079         if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2080             lpptPosition->x += LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left;
2081         lpptPosition->y = nItem * infoPtr->nItemHeight;
2082     }
2083 }
2084     
2085 /***
2086  * DESCRIPTION:            [INTERNAL]
2087  * Compute the rectangles of an item.  This is to localize all
2088  * the computations in one place. If you are not interested in some
2089  * of these values, simply pass in a NULL -- the function is smart
2090  * enough to compute only what's necessary. The function computes
2091  * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
2092  * one, the BOX rectangle. This rectangle is very cheap to compute,
2093  * and is guaranteed to contain all the other rectangles. Computing
2094  * the ICON rect is also cheap, but all the others are potentially
2095  * expensive. This gives an easy and effective optimization when
2096  * searching (like point inclusion, or rectangle intersection):
2097  * first test against the BOX, and if TRUE, test against the desired
2098  * rectangle.
2099  * If the function does not have all the necessary information
2100  * to computed the requested rectangles, will crash with a
2101  * failed assertion. This is done so we catch all programming
2102  * errors, given that the function is called only from our code.
2103  *
2104  * We have the following 'special' meanings for a few fields:
2105  *   * If LVIS_FOCUSED is set, we assume the item has the focus
2106  *     This is important in ICON mode, where it might get a larger
2107  *     then usual rectangle
2108  *
2109  * Please note that subitem support works only in REPORT mode.
2110  *
2111  * PARAMETER(S):
2112  * [I] infoPtr : valid pointer to the listview structure
2113  * [I] lpLVItem : item to compute the measures for
2114  * [O] lprcBox : ptr to Box rectangle
2115  *                Same as LVM_GETITEMRECT with LVIR_BOUNDS
2116  * [0] lprcSelectBox : ptr to select box rectangle
2117  *                Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
2118  * [O] lprcIcon : ptr to Icon rectangle
2119  *                Same as LVM_GETITEMRECT with LVIR_ICON
2120  * [O] lprcStateIcon: ptr to State Icon rectangle
2121  * [O] lprcLabel : ptr to Label rectangle
2122  *                Same as LVM_GETITEMRECT with LVIR_LABEL
2123  *
2124  * RETURN:
2125  *   None.
2126  */
2127 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
2128                                     LPRECT lprcBox, LPRECT lprcSelectBox,
2129                                     LPRECT lprcIcon, LPRECT lprcStateIcon, LPRECT lprcLabel)
2130 {
2131     BOOL doSelectBox = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
2132     RECT Box, SelectBox, Icon, Label;
2133     COLUMN_INFO *lpColumnInfo = NULL;
2134     SIZE labelSize = { 0, 0 };
2135 
2136     TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
2137 
2138     /* Be smart and try to figure out the minimum we have to do */
2139     if (lpLVItem->iSubItem) assert(infoPtr->uView == LV_VIEW_DETAILS);
2140     if (infoPtr->uView == LV_VIEW_ICON && (lprcBox || lprcLabel))
2141     {
2142         assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
2143         if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
2144     }
2145     if (lprcSelectBox) doSelectBox = TRUE;
2146     if (lprcLabel) doLabel = TRUE;
2147     if (doLabel || lprcIcon || lprcStateIcon) doIcon = TRUE;
2148     if (doSelectBox)
2149     {
2150         doIcon = TRUE;
2151         doLabel = TRUE;
2152     }
2153 
2154     /************************************************************/
2155     /* compute the box rectangle (it should be cheap to do)     */
2156     /************************************************************/
2157     if (lpLVItem->iSubItem || infoPtr->uView == LV_VIEW_DETAILS)
2158         lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
2159 
2160     if (lpLVItem->iSubItem)    
2161     {
2162         Box = lpColumnInfo->rcHeader;
2163     }
2164     else
2165     {
2166         Box.left = 0;
2167         Box.right = infoPtr->nItemWidth;
2168     }
2169     Box.top = 0;
2170     Box.bottom = infoPtr->nItemHeight;
2171 
2172     /******************************************************************/
2173     /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON  */
2174     /******************************************************************/
2175     if (doIcon)
2176     {
2177         LONG state_width = 0;
2178 
2179         if (infoPtr->himlState && lpLVItem->iSubItem == 0)
2180             state_width = infoPtr->iconStateSize.cx;
2181 
2182         if (infoPtr->uView == LV_VIEW_ICON)
2183         {
2184             Icon.left   = Box.left + state_width;
2185             if (infoPtr->himlNormal)
2186                 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx - state_width) / 2;
2187             Icon.top    = Box.top + ICON_TOP_PADDING;
2188             Icon.right  = Icon.left;
2189             Icon.bottom = Icon.top;
2190             if (infoPtr->himlNormal)
2191             {
2192                 Icon.right  += infoPtr->iconSize.cx;
2193                 Icon.bottom += infoPtr->iconSize.cy;
2194             }
2195         }
2196         else /* LV_VIEW_SMALLICON, LV_VIEW_LIST or LV_VIEW_DETAILS */
2197         {
2198             Icon.left   = Box.left + state_width;
2199 
2200             if (infoPtr->uView == LV_VIEW_DETAILS && lpLVItem->iSubItem == 0)
2201             {
2202                 /* we need the indent in report mode */
2203                 assert(lpLVItem->mask & LVIF_INDENT);
2204                 Icon.left += infoPtr->iconSize.cx * lpLVItem->iIndent + REPORT_MARGINX;
2205             }
2206 
2207             Icon.top    = Box.top;
2208             Icon.right  = Icon.left;
2209             if (infoPtr->himlSmall &&
2210                 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
2211                  ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
2212                 Icon.right += infoPtr->iconSize.cx;
2213             Icon.bottom = Icon.top + infoPtr->iconSize.cy;
2214         }
2215         if(lprcIcon) *lprcIcon = Icon;
2216         TRACE("    - icon=%s\n", wine_dbgstr_rect(&Icon));
2217 
2218         /* TODO: is this correct? */
2219         if (lprcStateIcon)
2220         {
2221             lprcStateIcon->left = Icon.left - state_width;
2222             lprcStateIcon->right = Icon.left;
2223             lprcStateIcon->top = Icon.top;
2224             lprcStateIcon->bottom = lprcStateIcon->top + infoPtr->iconSize.cy;
2225             TRACE("    - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon));
2226         }
2227      }
2228      else Icon.right = 0;
2229 
2230     /************************************************************/
2231     /* compute LABEL bounding box (ala LVM_GETITEMRECT)         */
2232     /************************************************************/
2233     if (doLabel)
2234     {
2235         /* calculate how far to the right can the label stretch */
2236         Label.right = Box.right;
2237         if (infoPtr->uView == LV_VIEW_DETAILS)
2238         {
2239             if (lpLVItem->iSubItem == 0)
2240             {
2241                 /* we need a zero based rect here */
2242                 Label = lpColumnInfo->rcHeader;
2243                 OffsetRect(&Label, -Label.left, 0);
2244             }
2245         }
2246 
2247         if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && infoPtr->uView == LV_VIEW_DETAILS))
2248         {
2249            labelSize.cx = infoPtr->nItemWidth;
2250            labelSize.cy = infoPtr->nItemHeight;
2251            goto calc_label;
2252         }
2253         
2254         /* we need the text in non owner draw mode */
2255         assert(lpLVItem->mask & LVIF_TEXT);
2256         if (is_textT(lpLVItem->pszText, TRUE))
2257         {
2258             HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2259             HDC hdc = GetDC(infoPtr->hwndSelf);
2260             HFONT hOldFont = SelectObject(hdc, hFont);
2261             UINT uFormat;
2262             RECT rcText;
2263 
2264             /* compute rough rectangle where the label will go */
2265             SetRectEmpty(&rcText);
2266             rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2267             rcText.bottom = infoPtr->nItemHeight;
2268             if (infoPtr->uView == LV_VIEW_ICON)
2269                 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2270 
2271             /* now figure out the flags */
2272             if (infoPtr->uView == LV_VIEW_ICON)
2273                 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2274             else
2275                 uFormat = LV_SL_DT_FLAGS;
2276             
2277             DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2278 
2279             if (rcText.right != rcText.left)
2280                 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2281 
2282             labelSize.cy = rcText.bottom - rcText.top;
2283 
2284             SelectObject(hdc, hOldFont);
2285             ReleaseDC(infoPtr->hwndSelf, hdc);
2286         }
2287 
2288 calc_label:
2289         if (infoPtr->uView == LV_VIEW_ICON)
2290         {
2291             Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2292             Label.top  = Box.top + ICON_TOP_PADDING_HITABLE +
2293                          infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2294             Label.right = Label.left + labelSize.cx;
2295             Label.bottom = Label.top + infoPtr->nItemHeight;
2296             if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2297             {
2298                 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2299                 labelSize.cy /= infoPtr->ntmHeight;
2300                 labelSize.cy = max(labelSize.cy, 1);
2301                 labelSize.cy *= infoPtr->ntmHeight;
2302              }
2303              Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2304         }
2305         else if (infoPtr->uView == LV_VIEW_DETAILS)
2306         {
2307             Label.left = Icon.right;
2308             Label.top = Box.top;
2309             Label.right = lpLVItem->iSubItem ? lpColumnInfo->rcHeader.right :
2310                           lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
2311             Label.bottom = Label.top + infoPtr->nItemHeight;
2312         }
2313         else /* LV_VIEW_SMALLICON or LV_VIEW_LIST */
2314         {
2315             Label.left = Icon.right;
2316             Label.top = Box.top;
2317             Label.right = min(Label.left + labelSize.cx, Label.right);
2318             Label.bottom = Label.top + infoPtr->nItemHeight;
2319         }
2320   
2321         if (lprcLabel) *lprcLabel = Label;
2322         TRACE("    - label=%s\n", wine_dbgstr_rect(&Label));
2323     }
2324 
2325     /************************************************************/
2326     /* compute SELECT bounding box                              */
2327     /************************************************************/
2328     if (doSelectBox)
2329     {
2330         if (infoPtr->uView == LV_VIEW_DETAILS)
2331         {
2332             SelectBox.left = Icon.left;
2333             SelectBox.top = Box.top;
2334             SelectBox.bottom = Box.bottom;
2335 
2336             if (labelSize.cx)
2337                 SelectBox.right = min(Label.left + labelSize.cx, Label.right);
2338             else
2339                 SelectBox.right = min(Label.left + MAX_EMPTYTEXT_SELECT_WIDTH, Label.right);
2340         }
2341         else
2342         {
2343             UnionRect(&SelectBox, &Icon, &Label);
2344         }
2345         if (lprcSelectBox) *lprcSelectBox = SelectBox;
2346         TRACE("    - select box=%s\n", wine_dbgstr_rect(&SelectBox));
2347     }
2348 
2349     /* Fix the Box if necessary */
2350     if (lprcBox)
2351     {
2352         if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2353         else *lprcBox = Box;
2354     }
2355     TRACE("    - box=%s\n", wine_dbgstr_rect(&Box));
2356 }
2357 
2358 /***
2359  * DESCRIPTION:            [INTERNAL]
2360  *
2361  * PARAMETER(S):
2362  * [I] infoPtr : valid pointer to the listview structure
2363  * [I] nItem : item number
2364  * [O] lprcBox : ptr to Box rectangle
2365  *
2366  * RETURN:
2367  *   None.
2368  */
2369 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2370 {
2371     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2372     POINT Position, Origin;
2373     LVITEMW lvItem;
2374 
2375     LISTVIEW_GetOrigin(infoPtr, &Origin);
2376     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2377 
2378     /* Be smart and try to figure out the minimum we have to do */
2379     lvItem.mask = 0;
2380     if (infoPtr->uView == LV_VIEW_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2381         lvItem.mask |= LVIF_TEXT;
2382     lvItem.iItem = nItem;
2383     lvItem.iSubItem = 0;
2384     lvItem.pszText = szDispText;
2385     lvItem.cchTextMax = DISP_TEXT_SIZE;
2386     if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2387     if (infoPtr->uView == LV_VIEW_ICON)
2388     {
2389         lvItem.mask |= LVIF_STATE;
2390         lvItem.stateMask = LVIS_FOCUSED;
2391         lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2392     }
2393     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0, 0);
2394 
2395     if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT &&
2396         SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 0, 0))
2397     {
2398         OffsetRect(lprcBox, Origin.x, Position.y + Origin.y);
2399     }
2400     else
2401         OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2402 }
2403 
2404 /* LISTVIEW_MapIdToIndex helper */
2405 static INT CALLBACK MapIdSearchCompare(LPVOID p1, LPVOID p2, LPARAM lParam)
2406 {
2407     ITEM_ID *id1 = (ITEM_ID*)p1;
2408     ITEM_ID *id2 = (ITEM_ID*)p2;
2409 
2410     if (id1->id == id2->id) return 0;
2411 
2412     return (id1->id < id2->id) ? -1 : 1;
2413 }
2414 
2415 /***
2416  * DESCRIPTION:
2417  * Returns the item index for id specified.
2418  *
2419  * PARAMETER(S):
2420  * [I] infoPtr : valid pointer to the listview structure
2421  * [I] iID : item id to get index for
2422  *
2423  * RETURN:
2424  * Item index, or -1 on failure.
2425  */
2426 static INT LISTVIEW_MapIdToIndex(const LISTVIEW_INFO *infoPtr, UINT iID)
2427 {
2428     ITEM_ID ID;
2429     INT index;
2430 
2431     TRACE("iID=%d\n", iID);
2432 
2433     if (infoPtr->dwStyle & LVS_OWNERDATA) return -1;
2434     if (infoPtr->nItemCount == 0) return -1;
2435 
2436     ID.id = iID;
2437     index = DPA_Search(infoPtr->hdpaItemIds, &ID, -1, &MapIdSearchCompare, 0, DPAS_SORTED);
2438 
2439     if (index != -1)
2440     {
2441         ITEM_ID *lpID = DPA_GetPtr(infoPtr->hdpaItemIds, index);
2442         return DPA_GetPtrIndex(infoPtr->hdpaItems, lpID->item);
2443     }
2444 
2445     return -1;
2446 }
2447 
2448 /***
2449  * DESCRIPTION:
2450  * Returns the item id for index given.
2451  *
2452  * PARAMETER(S):
2453  * [I] infoPtr : valid pointer to the listview structure
2454  * [I] iItem : item index to get id for
2455  *
2456  * RETURN:
2457  * Item id.
2458  */
2459 static DWORD LISTVIEW_MapIndexToId(const LISTVIEW_INFO *infoPtr, INT iItem)
2460 {
2461     ITEM_INFO *lpItem;
2462     HDPA hdpaSubItems;
2463 
2464     TRACE("iItem=%d\n", iItem);
2465 
2466     if (infoPtr->dwStyle & LVS_OWNERDATA) return -1;
2467     if (iItem < 0 || iItem >= infoPtr->nItemCount) return -1;
2468 
2469     hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, iItem);
2470     lpItem = DPA_GetPtr(hdpaSubItems, 0);
2471 
2472     return lpItem->id->id;
2473 }
2474 
2475 /***
2476  * DESCRIPTION:
2477  * Returns the current icon position, and advances it along the top.
2478  * The returned position is not offset by Origin.
2479  *
2480  * PARAMETER(S):
2481  * [I] infoPtr : valid pointer to the listview structure
2482  * [O] lpPos : will get the current icon position
2483  *
2484  * RETURN:
2485  * None
2486  */
2487 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2488 {
2489     INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2490     
2491     *lpPos = infoPtr->currIconPos;
2492     
2493     infoPtr->currIconPos.x += infoPtr->nItemWidth;
2494     if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2495 
2496     infoPtr->currIconPos.x  = 0;
2497     infoPtr->currIconPos.y += infoPtr->nItemHeight;
2498 }
2499 
2500     
2501 /***
2502  * DESCRIPTION:
2503  * Returns the current icon position, and advances it down the left edge.
2504  * The returned position is not offset by Origin.
2505  *
2506  * PARAMETER(S):
2507  * [I] infoPtr : valid pointer to the listview structure
2508  * [O] lpPos : will get the current icon position
2509  *
2510  * RETURN:
2511  * None
2512  */
2513 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2514 {
2515     INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2516     
2517     *lpPos = infoPtr->currIconPos;
2518     
2519     infoPtr->currIconPos.y += infoPtr->nItemHeight;
2520     if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2521 
2522     infoPtr->currIconPos.x += infoPtr->nItemWidth;
2523     infoPtr->currIconPos.y  = 0;
2524 }
2525 
2526     
2527 /***
2528  * DESCRIPTION:
2529  * Moves an icon to the specified position.
2530  * It takes care of invalidating the item, etc.
2531  *
2532  * PARAMETER(S):
2533  * [I] infoPtr : valid pointer to the listview structure
2534  * [I] nItem : the item to move
2535  * [I] lpPos : the new icon position
2536  * [I] isNew : flags the item as being new
2537  *
2538  * RETURN:
2539  *   Success: TRUE
2540  *   Failure: FALSE
2541  */
2542 static BOOL LISTVIEW_MoveIconTo(const LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2543 {
2544     POINT old;
2545     
2546     if (!isNew)
2547     { 
2548         old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2549         old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2550     
2551         if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2552         LISTVIEW_InvalidateItem(infoPtr, nItem);
2553     }
2554 
2555     /* Allocating a POINTER for every item is too resource intensive,
2556      * so we'll keep the (x,y) in different arrays */
2557     if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2558     if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2559 
2560     LISTVIEW_InvalidateItem(infoPtr, nItem);
2561 
2562     return TRUE;
2563 }
2564 
2565 /***
2566  * DESCRIPTION:
2567  * Arranges listview items in icon display mode.
2568  *
2569  * PARAMETER(S):
2570  * [I] infoPtr : valid pointer to the listview structure
2571  * [I] nAlignCode : alignment code
2572  *
2573  * RETURN:
2574  *   SUCCESS : TRUE
2575  *   FAILURE : FALSE
2576  */
2577 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2578 {
2579     void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2580     POINT pos;
2581     INT i;
2582 
2583     if (infoPtr->uView != LV_VIEW_ICON && infoPtr->uView != LV_VIEW_SMALLICON) return FALSE;
2584   
2585     TRACE("nAlignCode=%d\n", nAlignCode);
2586 
2587     if (nAlignCode == LVA_DEFAULT)
2588     {
2589         if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2590         else nAlignCode = LVA_ALIGNTOP;
2591     }
2592    
2593     switch (nAlignCode)
2594     {
2595     case LVA_ALIGNLEFT:  next_pos = LISTVIEW_NextIconPosLeft; break;
2596     case LVA_ALIGNTOP:   next_pos = LISTVIEW_NextIconPosTop;  break;
2597     case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop;  break; /* FIXME */
2598     default: return FALSE;
2599     }
2600     
2601     infoPtr->bAutoarrange = TRUE;
2602     infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2603     for (i = 0; i < infoPtr->nItemCount; i++)
2604     {
2605         next_pos(infoPtr, &pos);
2606         LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2607     }
2608 
2609     return TRUE;
2610 }
2611   
2612 /***
2613  * DESCRIPTION:
2614  * Retrieves the bounding rectangle of all the items, not offset by Origin.
2615  * For LVS_REPORT always returns empty rectangle.
2616  *
2617  * PARAMETER(S):
2618  * [I] infoPtr : valid pointer to the listview structure
2619  * [O] lprcView : bounding rectangle
2620  *
2621  * RETURN:
2622  *   SUCCESS : TRUE
2623  *   FAILURE : FALSE
2624  */
2625 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2626 {
2627     INT i, x, y;
2628 
2629     SetRectEmpty(lprcView);
2630 
2631     switch (infoPtr->uView)
2632     {
2633     case LV_VIEW_ICON:
2634     case LV_VIEW_SMALLICON:
2635         for (i = 0; i < infoPtr->nItemCount; i++)
2636         {
2637             x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2638             y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2639             lprcView->right = max(lprcView->right, x);
2640             lprcView->bottom = max(lprcView->bottom, y);
2641         }
2642         if (infoPtr->nItemCount > 0)
2643         {
2644             lprcView->right += infoPtr->nItemWidth;
2645             lprcView->bottom += infoPtr->nItemHeight;
2646         }
2647         break;
2648 
2649     case LV_VIEW_LIST:
2650         y = LISTVIEW_GetCountPerColumn(infoPtr);
2651         x = infoPtr->nItemCount / y;
2652         if (infoPtr->nItemCount % y) x++;
2653         lprcView->right = x * infoPtr->nItemWidth;
2654         lprcView->bottom = y * infoPtr->nItemHeight;
2655         break;
2656     }
2657 }
2658 
2659 /***
2660  * DESCRIPTION:
2661  * Retrieves the bounding rectangle of all the items.
2662  *
2663  * PARAMETER(S):
2664  * [I] infoPtr : valid pointer to the listview structure
2665  * [O] lprcView : bounding rectangle
2666  *
2667  * RETURN:
2668  *   SUCCESS : TRUE
2669  *   FAILURE : FALSE
2670  */
2671 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2672 {
2673     POINT ptOrigin;
2674 
2675     TRACE("(lprcView=%p)\n", lprcView);
2676 
2677     if (!lprcView) return FALSE;
2678 
2679     LISTVIEW_GetAreaRect(infoPtr, lprcView);
2680 
2681     if (infoPtr->uView != LV_VIEW_DETAILS)
2682     {
2683         LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2684         OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2685     }
2686 
2687     TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView));
2688 
2689     return TRUE;
2690 }
2691 
2692 /***
2693  * DESCRIPTION:
2694  * Retrieves the subitem pointer associated with the subitem index.
2695  *
2696  * PARAMETER(S):
2697  * [I] hdpaSubItems : DPA handle for a specific item
2698  * [I] nSubItem : index of subitem
2699  *
2700  * RETURN:
2701  *   SUCCESS : subitem pointer
2702  *   FAILURE : NULL
2703  */
2704 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2705 {
2706     SUBITEM_INFO *lpSubItem;
2707     INT i;
2708 
2709     /* we should binary search here if need be */
2710     for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2711     {
2712         lpSubItem = DPA_GetPtr(hdpaSubItems, i);
2713         if (lpSubItem->iSubItem == nSubItem)
2714             return lpSubItem;
2715     }
2716 
2717     return NULL;
2718 }
2719 
2720 
2721 /***
2722  * DESCRIPTION:
2723  * Calculates the desired item width.
2724  *
2725  * PARAMETER(S):
2726  * [I] infoPtr : valid pointer to the listview structure
2727  *
2728  * RETURN:
2729  *  The desired item width.
2730  */
2731 static INT LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO *infoPtr)
2732 {
2733     INT nItemWidth = 0;
2734 
2735     TRACE("uView=%d\n", infoPtr->uView);
2736 
2737     if (infoPtr->uView == LV_VIEW_ICON)
2738         nItemWidth = infoPtr->iconSpacing.cx;
2739     else if (infoPtr->uView == LV_VIEW_DETAILS)
2740     {
2741         if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2742         {
2743             RECT rcHeader;
2744             INT index;
2745 
2746             index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
2747                                  DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
2748 
2749             LISTVIEW_GetHeaderRect(infoPtr, index, &rcHeader);
2750             nItemWidth = rcHeader.right;
2751         }
2752     }
2753     else /* LV_VIEW_SMALLICON, or LV_VIEW_LIST */
2754     {
2755         INT i;
2756         
2757         for (i = 0; i < infoPtr->nItemCount; i++)
2758             nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2759 
2760         if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx; 
2761         if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2762 
2763         nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2764     }
2765 
2766     return nItemWidth;
2767 }
2768 
2769 /***
2770  * DESCRIPTION:
2771  * Calculates the desired item height.
2772  *
2773  * PARAMETER(S):
2774  * [I] infoPtr : valid pointer to the listview structure
2775  *
2776  * RETURN:
2777  *  The desired item height.
2778  */
2779 static INT LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO *infoPtr)
2780 {
2781     INT nItemHeight;
2782 
2783     TRACE("uView=%d\n", infoPtr->uView);
2784 
2785     if (infoPtr->uView == LV_VIEW_ICON)
2786         nItemHeight = infoPtr->iconSpacing.cy;
2787     else
2788     {
2789         nItemHeight = infoPtr->ntmHeight; 
2790         if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
2791             nItemHeight++;
2792         if (infoPtr->himlState)
2793             nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2794         if (infoPtr->himlSmall)
2795             nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2796         if (infoPtr->himlState || infoPtr->himlSmall)
2797             nItemHeight += HEIGHT_PADDING;
2798     if (infoPtr->nMeasureItemHeight > 0)
2799         nItemHeight = infoPtr->nMeasureItemHeight;
2800     }
2801 
2802     return max(nItemHeight, 1);
2803 }
2804 
2805 /***
2806  * DESCRIPTION:
2807  * Updates the width, and height of an item.
2808  *
2809  * PARAMETER(S):
2810  * [I] infoPtr : valid pointer to the listview structure
2811  *
2812  * RETURN:
2813  *  None.
2814  */
2815 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2816 {
2817     infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2818     infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2819 }
2820 
2821 
2822 /***
2823  * DESCRIPTION:
2824  * Retrieves and saves important text metrics info for the current
2825  * Listview font.
2826  *
2827  * PARAMETER(S):
2828  * [I] infoPtr : valid pointer to the listview structure
2829  *
2830  */
2831 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2832 {
2833     HDC hdc = GetDC(infoPtr->hwndSelf);
2834     HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2835     HFONT hOldFont = SelectObject(hdc, hFont);
2836     TEXTMETRICW tm;
2837     SIZE sz;
2838 
2839     if (GetTextMetricsW(hdc, &tm))
2840     {
2841         infoPtr->ntmHeight = tm.tmHeight;
2842         infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
2843     }
2844 
2845     if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
2846         infoPtr->nEllipsisWidth = sz.cx;
2847         
2848     SelectObject(hdc, hOldFont);
2849     ReleaseDC(infoPtr->hwndSelf, hdc);
2850     
2851     TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2852 }
2853 
2854 /***
2855  * DESCRIPTION:
2856  * A compare function for ranges
2857  *
2858  * PARAMETER(S)
2859  * [I] range1 : pointer to range 1;
2860  * [I] range2 : pointer to range 2;
2861  * [I] flags : flags
2862  *
2863  * RETURNS:
2864  * > 0 : if range 1 > range 2
2865  * < 0 : if range 2 > range 1
2866  * = 0 : if range intersects range 2
2867  */
2868 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2869 {
2870     INT cmp;
2871     
2872     if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower) 
2873         cmp = -1;
2874     else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower) 
2875         cmp = 1;
2876     else 
2877         cmp = 0;
2878 
2879     TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1), debugrange(range2), cmp);
2880 
2881     return cmp;
2882 }
2883 
2884 #if DEBUG_RANGES
2885 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2886 #else
2887 #define ranges_check(ranges, desc) do { } while(0)
2888 #endif
2889 
2890 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2891 {
2892     INT i;
2893     RANGE *prev, *curr;
2894     
2895     TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2896     assert (ranges);
2897     assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2898     ranges_dump(ranges);
2899     if (DPA_GetPtrCount(ranges->hdpa) > 0)
2900     {
2901         prev = DPA_GetPtr(ranges->hdpa, 0);
2902         assert (prev->lower >= 0 && prev->lower < prev->upper);
2903         for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2904         {
2905             curr = DPA_GetPtr(ranges->hdpa, i);
2906             assert (prev->upper <= curr->lower);
2907             assert (curr->lower < curr->upper);
2908             prev = curr;
2909         }
2910     }
2911     TRACE("--- Done checking---\n");
2912 }
2913 
2914 static RANGES ranges_create(int count)
2915 {
2916     RANGES ranges = Alloc(sizeof(struct tagRANGES));
2917     if (!ranges) return NULL;
2918     ranges->hdpa = DPA_Create(count);
2919     if (ranges->hdpa) return ranges;
2920     Free(ranges);
2921     return NULL;
2922 }
2923 
2924 static void ranges_clear(RANGES ranges)
2925 {
2926     INT i;
2927         
2928     for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2929         Free(DPA_GetPtr(ranges->hdpa, i));
2930     DPA_DeleteAllPtrs(ranges->hdpa);
2931 }
2932 
2933 
2934 static void ranges_destroy(RANGES ranges)
2935 {
2936     if (!ranges) return;
2937     ranges_clear(ranges);
2938     DPA_Destroy(ranges->hdpa);
2939     Free(ranges);
2940 }
2941 
2942 static RANGES ranges_clone(RANGES ranges)
2943 {
2944     RANGES clone;
2945     INT i;
2946            
2947     if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2948 
2949     for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2950     {
2951         RANGE *newrng = Alloc(sizeof(RANGE));
2952         if (!newrng) goto fail;
2953         *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2954         DPA_SetPtr(clone->hdpa, i, newrng);
2955     }
2956     return clone;
2957     
2958 fail:
2959     TRACE ("clone failed\n");
2960     ranges_destroy(clone);
2961     return NULL;
2962 }
2963 
2964 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2965 {
2966     INT i;
2967 
2968     for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2969         ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2970 
2971     return ranges;
2972 }
2973 
2974 static void ranges_dump(RANGES ranges)
2975 {
2976     INT i;
2977 
2978     for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2979         TRACE("   %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2980 }
2981 
2982 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2983 {
2984     RANGE srchrng = { nItem, nItem + 1 };
2985 
2986     TRACE("(nItem=%d)\n", nItem);
2987     ranges_check(ranges, "before contain");
2988     return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2989 }
2990 
2991 static INT ranges_itemcount(RANGES ranges)
2992 {
2993     INT i, count = 0;
2994     
2995     for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2996     {
2997         RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2998         count += sel->upper - sel->lower;
2999     }
3000 
3001     return count;
3002 }
3003 
3004 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
3005 {
3006     RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
3007     INT index;
3008 
3009     index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
3010     if (index == -1) return TRUE;
3011 
3012     for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
3013     {
3014         chkrng = DPA_GetPtr(ranges->hdpa, index);
3015         if (chkrng->lower >= nItem)
3016             chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
3017         if (chkrng->upper > nItem)
3018             chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
3019     }
3020     return TRUE;
3021 }
3022 
3023 static BOOL ranges_add(RANGES ranges, RANGE range)
3024 {
3025     RANGE srchrgn;
3026     INT index;
3027 
3028     TRACE("(%s)\n", debugrange(&range));
3029     ranges_check(ranges, "before add");
3030 
3031     /* try find overlapping regions first */
3032     srchrgn.lower = range.lower - 1;
3033     srchrgn.upper = range.upper + 1;
3034     index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
3035    
3036     if (index == -1)
3037     {
3038         RANGE *newrgn;
3039 
3040         TRACE("Adding new range\n");
3041 
3042         /* create the brand new range to insert */      
3043         newrgn = Alloc(sizeof(RANGE));
3044         if(!newrgn) goto fail;
3045         *newrgn = range;
3046         
3047         /* figure out where to insert it */
3048         index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
3049         TRACE("index=%d\n", index);
3050         if (index == -1) index = 0;
3051         
3052         /* and get it over with */
3053         if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
3054         {
3055             Free(newrgn);
3056             goto fail;
3057         }
3058     }
3059     else
3060     {
3061         RANGE *chkrgn, *mrgrgn;
3062         INT fromindex, mergeindex;
3063 
3064         chkrgn = DPA_GetPtr(ranges->hdpa, index);
3065         TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
3066 
3067         chkrgn->lower = min(range.lower, chkrgn->lower);
3068         chkrgn->upper = max(range.upper, chkrgn->upper);
3069         
3070         TRACE("New range %s @%d\n", debugrange(chkrgn), index);
3071 
3072         /* merge now common ranges */
3073         fromindex = 0;
3074         srchrgn.lower = chkrgn->lower - 1;
3075         srchrgn.upper = chkrgn->upper + 1;
3076             
3077         do
3078         {
3079             mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
3080             if (mergeindex == -1) break;
3081             if (mergeindex == index) 
3082             {
3083                 fromindex = index + 1;
3084                 continue;
3085             }
3086           
3087             TRACE("Merge with index %i\n", mergeindex);
3088             
3089             mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
3090             chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
3091             chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
3092             Free(mrgrgn);
3093             DPA_DeletePtr(ranges->hdpa, mergeindex);
3094             if (mergeindex < index) index --;
3095         } while(1);
3096     }
3097 
3098     ranges_check(ranges, "after add");
3099     return TRUE;
3100     
3101 fail:
3102     ranges_check(ranges, "failed add");
3103     return FALSE;
3104 }
3105 
3106 static BOOL ranges_del(RANGES ranges, RANGE range)
3107 {
3108     RANGE *chkrgn;
3109     INT index;
3110 
3111     TRACE("(%s)\n", debugrange(&range));
3112     ranges_check(ranges, "before del");
3113     
3114     /* we don't use DPAS_SORTED here, since we need *
3115      * to find the first overlapping range          */
3116     index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
3117     while(index != -1) 
3118     {
3119         chkrgn = DPA_GetPtr(ranges->hdpa, index);
3120         
3121         TRACE("Matches range %s @%d\n", debugrange(chkrgn), index); 
3122 
3123         /* case 1: Same range */
3124         if ( (chkrgn->upper == range.upper) &&
3125              (chkrgn->lower == range.lower) )
3126         {
3127             DPA_DeletePtr(ranges->hdpa, index);
3128             break;
3129         }
3130         /* case 2: engulf */
3131         else if ( (chkrgn->upper <= range.upper) &&
3132                   (chkrgn->lower >= range.lower) ) 
3133         {
3134             DPA_DeletePtr(ranges->hdpa, index);
3135         }
3136         /* case 3: overlap upper */
3137         else if ( (chkrgn->upper <= range.upper) &&
3138                   (chkrgn->lower < range.lower) )
3139         {
3140             chkrgn->upper = range.lower;
3141         }
3142         /* case 4: overlap lower */
3143         else if ( (chkrgn->upper > range.upper) &&
3144                   (chkrgn->lower >= range.lower) )
3145         {
3146             chkrgn->lower = range.upper;
3147             break;
3148         }
3149         /* case 5: fully internal */
3150         else
3151         {
3152             RANGE tmprgn = *chkrgn, *newrgn;
3153 
3154             if (!(newrgn = Alloc(sizeof(RANGE)))) goto fail;
3155             newrgn->lower = chkrgn->lower;
3156             newrgn->upper = range.lower;
3157             chkrgn->lower = range.upper;
3158             if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
3159             {
3160                 Free(newrgn);
3161                 goto fail;
3162             }
3163             chkrgn = &tmprgn;
3164             break;
3165         }
3166 
3167         index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
3168     }
3169 
3170     ranges_check(ranges, "after del");
3171     return TRUE;
3172 
3173 fail:
3174     ranges_check(ranges, "failed del");
3175     return FALSE;
3176 }
3177 
3178 /***
3179 * DESCRIPTION:
3180 * Removes all selection ranges
3181 *
3182 * Parameters(s):
3183 * [I] infoPtr : valid pointer to the listview structure
3184 * [I] toSkip : item range to skip removing the selection
3185 *
3186 * RETURNS:
3187 *   SUCCESS : TRUE
3188 *   FAILURE : FALSE
3189 */
3190 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
3191 {
3192     LVITEMW lvItem;
3193     ITERATOR i;
3194     RANGES clone;
3195 
3196     TRACE("()\n");
3197 
3198     lvItem.state = 0;
3199     lvItem.stateMask = LVIS_SELECTED;
3200     
3201     /* need to clone the DPA because callbacks can change it */
3202     if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
3203     iterator_rangesitems(&i, ranges_diff(clone, toSkip));
3204     while(iterator_next(&i))
3205         LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
3206     /* note that the iterator destructor will free the cloned range */
3207     iterator_destroy(&i);
3208 
3209     return TRUE;
3210 }
3211 
3212 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
3213 {
3214     RANGES toSkip;
3215    
3216     if (!(toSkip = ranges_create(1))) return FALSE;
3217     if (nItem != -1) ranges_additem(toSkip, nItem);
3218     LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
3219     ranges_destroy(toSkip);
3220     return TRUE;
3221 }
3222 
3223 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
3224 {
3225     return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
3226 }
3227 
3228 /***
3229  * DESCRIPTION:
3230  * Retrieves the number of items that are marked as selected.
3231  *
3232  * PARAMETER(S):
3233  * [I] infoPtr : valid pointer to the listview structure
3234  *
3235  * RETURN:
3236  * Number of items selected.
3237  */
3238 static INT LISTVIEW_GetSelectedCount(const LISTVIEW_INFO *infoPtr)
3239 {
3240     INT nSelectedCount = 0;
3241 
3242     if (infoPtr->uCallbackMask & LVIS_SELECTED)
3243     {
3244         INT i;
3245         for (i = 0; i < infoPtr->nItemCount; i++)
3246         {
3247             if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
3248                 nSelectedCount++;
3249         }
3250     }
3251     else
3252         nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
3253 
3254     TRACE("nSelectedCount=%d\n", nSelectedCount);
3255     return nSelectedCount;
3256 }
3257 
3258 /***
3259  * DESCRIPTION:
3260  * Manages the item focus.
3261  *
3262  * PARAMETER(S):
3263  * [I] infoPtr : valid pointer to the listview structure
3264  * [I] nItem : item index
3265  *
3266  * RETURN:
3267  *   TRUE : focused item changed
3268  *   FALSE : focused item has NOT changed
3269  */
3270 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
3271 {
3272     INT oldFocus = infoPtr->nFocusedItem;
3273     LVITEMW lvItem;
3274 
3275     if (nItem == infoPtr->nFocusedItem) return FALSE;
3276     
3277     lvItem.state =  nItem == -1 ? 0 : LVIS_FOCUSED;
3278     lvItem.stateMask = LVIS_FOCUSED;
3279     LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
3280 
3281     return oldFocus != infoPtr->nFocusedItem;
3282 }
3283 
3284 /* Helper function for LISTVIEW_ShiftIndices *only* */
3285 static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
3286 {
3287     if (nShiftItem < nItem) return nShiftItem;
3288 
3289     if (nShiftItem > nItem) return nShiftItem + direction;
3290 
3291     if (direction > 0) return nShiftItem + direction;
3292 
3293     return min(nShiftItem, infoPtr->nItemCount - 1);
3294 }
3295 
3296 /**
3297 * DESCRIPTION:
3298 * Updates the various indices after an item has been inserted or deleted.
3299 *
3300 * PARAMETER(S):
3301 * [I] infoPtr : valid pointer to the listview structure
3302 * [I] nItem : item index
3303 * [I] direction : Direction of shift, +1 or -1.
3304 *
3305 * RETURN:
3306 * None
3307 */
3308 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
3309 {
3310     INT nNewFocus;
3311     BOOL bOldChange;
3312 
3313     /* temporarily disable change notification while shifting items */
3314     bOldChange = infoPtr->bDoChangeNotify;
3315     infoPtr->bDoChangeNotify = FALSE;
3316 
3317     TRACE("Shifting %iu, %i steps\n", nItem, direction);
3318 
3319     ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
3320 
3321     assert(abs(direction) == 1);
3322 
3323     infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
3324 
3325     nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
3326     if (nNewFocus != infoPtr->nFocusedItem)
3327         LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
3328     
3329     /* But we are not supposed to modify nHotItem! */
3330 
3331     infoPtr->bDoChangeNotify = bOldChange;
3332 }
3333 
3334 
3335 /**
3336  * DESCRIPTION:
3337  * Adds a block of selections.
3338  *
3339  * PARAMETER(S):
3340  * [I] infoPtr : valid pointer to the listview structure
3341  * [I] nItem : item index
3342  *
3343  * RETURN:
3344  * Whether the window is still valid.
3345  */
3346 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3347 {
3348     INT nFirst = min(infoPtr->nSelectionMark, nItem);
3349     INT nLast = max(infoPtr->nSelectionMark, nItem);
3350     HWND hwndSelf = infoPtr->hwndSelf;
3351     NMLVODSTATECHANGE nmlv;
3352     LVITEMW item;
3353     BOOL bOldChange;
3354     INT i;
3355 
3356     /* Temporarily disable change notification
3357      * If the control is LVS_OWNERDATA, we need to send
3358      * only one LVN_ODSTATECHANGED notification.
3359      * See MSDN documentation for LVN_ITEMCHANGED.
3360      */
3361     bOldChange = infoPtr->bDoChangeNotify;
3362     if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3363 
3364     if (nFirst == -1) nFirst = nItem;
3365 
3366     item.state = LVIS_SELECTED;
3367     item.stateMask = LVIS_SELECTED;
3368 
3369     for (i = nFirst; i <= nLast; i++)
3370         LISTVIEW_SetItemState(infoPtr,i,&item);
3371 
3372     ZeroMemory(&nmlv, sizeof(nmlv));
3373     nmlv.iFrom = nFirst;
3374     nmlv.iTo = nLast;
3375     nmlv.uNewState = 0;
3376     nmlv.uOldState = item.state;
3377 
3378     notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3379     if (!IsWindow(hwndSelf))
3380         return FALSE;
3381     infoPtr->bDoChangeNotify = bOldChange;
3382     return TRUE;
3383 }
3384 
3385 
3386 /***
3387  * DESCRIPTION:
3388  * Sets a single group selection.
3389  *
3390  * PARAMETER(S):
3391  * [I] infoPtr : valid pointer to the listview structure
3392  * [I] nItem : item index
3393  *
3394  * RETURN:
3395  * None
3396  */
3397 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3398 {
3399     RANGES selection;
3400     LVITEMW item;
3401     ITERATOR i;
3402     BOOL bOldChange;
3403 
3404     if (!(selection = ranges_create(100))) return;
3405 
3406     item.state = LVIS_SELECTED; 
3407     item.stateMask = LVIS_SELECTED;
3408 
3409     if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
3410     {
3411         if (infoPtr->nSelectionMark == -1)
3412         {
3413             infoPtr->nSelectionMark = nItem;
3414             ranges_additem(selection, nItem);
3415         }
3416         else
3417         {
3418             RANGE sel;
3419             
3420             sel.lower = min(infoPtr->nSelectionMark, nItem);
3421             sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3422             ranges_add(selection, sel);
3423         }
3424     }
3425     else
3426     {
3427         RECT rcItem, rcSel, rcSelMark;
3428         POINT ptItem;
3429         
3430         rcItem.left = LVIR_BOUNDS;
3431         if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3432         rcSelMark.left = LVIR_BOUNDS;
3433         if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3434         UnionRect(&rcSel, &rcItem, &rcSelMark);
3435         iterator_frameditems(&i, infoPtr, &rcSel);
3436         while(iterator_next(&i))
3437         {
3438             LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3439             if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3440         }
3441         iterator_destroy(&i);
3442     }
3443 
3444     /* disable per item notifications on LVS_OWNERDATA style
3445        FIXME: single LVN_ODSTATECHANGED should be used */
3446     bOldChange = infoPtr->bDoChangeNotify;
3447     if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3448 
3449     LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3450 
3451 
3452     iterator_rangesitems(&i, selection);
3453     while(iterator_next(&i))
3454         LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3455     /* this will also destroy the selection */
3456     iterator_destroy(&i);
3457 
3458     infoPtr->bDoChangeNotify = bOldChange;
3459     
3460     LISTVIEW_SetItemFocus(infoPtr, nItem);
3461 }
3462 
3463 /***
3464  * DESCRIPTION:
3465  * Sets a single selection.
3466  *
3467  * PARAMETER(S):
3468  * [I] infoPtr : valid pointer to the listview structure
3469  * [I] nItem : item index
3470  *
3471  * RETURN:
3472  * None
3473  */
3474 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3475 {
3476     LVITEMW lvItem;
3477 
3478     TRACE("nItem=%d\n", nItem);
3479     
3480     LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3481 
3482     lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3483     lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3484     LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3485 
3486     infoPtr->nSelectionMark = nItem;
3487 }
3488 
3489 /***
3490  * DESCRIPTION:
3491  * Set selection(s) with keyboard.
3492  *
3493  * PARAMETER(S):
3494  * [I] infoPtr : valid pointer to the listview structure
3495  * [I] nItem : item index
3496  * [I] space : VK_SPACE code sent
3497  *
3498  * RETURN:
3499  *   SUCCESS : TRUE (needs to be repainted)
3500  *   FAILURE : FALSE (nothing has changed)
3501  */
3502 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem, BOOL space)
3503 {
3504   /* FIXME: pass in the state */
3505   WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3506   WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3507   BOOL bResult = FALSE;
3508 
3509   TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3510   if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3511   {
3512     bResult = TRUE;
3513 
3514     if (infoPtr->dwStyle & LVS_SINGLESEL || (wShift == 0 && wCtrl == 0))
3515       LISTVIEW_SetSelection(infoPtr, nItem);
3516     else
3517     {
3518       if (wShift)
3519         LISTVIEW_SetGroupSelection(infoPtr, nItem);
3520       else if (wCtrl)
3521       {
3522         LVITEMW lvItem;
3523         lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3524         lvItem.stateMask = LVIS_SELECTED;
3525         if (space)
3526         {
3527             LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3528             if (lvItem.state & LVIS_SELECTED)
3529                 infoPtr->nSelectionMark = nItem;
3530         }
3531         bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3532       }
3533     }
3534     LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3535   }
3536 
3537   UpdateWindow(infoPtr->hwndSelf); /* update client area */
3538   return bResult;
3539 }
3540 
3541 static BOOL LISTVIEW_GetItemAtPt(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3542 {
3543     LVHITTESTINFO lvHitTestInfo;
3544 
3545     ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3546     lvHitTestInfo.pt.x = pt.x;
3547     lvHitTestInfo.pt.y = pt.y;
3548 
3549     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3550 
3551     lpLVItem->mask = LVIF_PARAM;
3552     lpLVItem->iItem = lvHitTestInfo.iItem;
3553     lpLVItem->iSubItem = 0;
3554 
3555     return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3556 }
3557 
3558 static inline BOOL LISTVIEW_isHotTracking(const LISTVIEW_INFO *infoPtr)
3559 {
3560     return ((infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) ||
3561             (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE) ||
3562             (infoPtr->dwLvExStyle & LVS_EX_TWOCLICKACTIVATE));
3563 }
3564 
3565 /***
3566  * DESCRIPTION:
3567  * Called when the mouse is being actively tracked and has hovered for a specified
3568  * amount of time
3569  *
3570  * PARAMETER(S):
3571  * [I] infoPtr : valid pointer to the listview structure
3572  * [I] fwKeys : key indicator
3573  * [I] x,y : mouse position
3574  *
3575  * RETURN:
3576  *   0 if the message was processed, non-zero if there was an error
3577  *
3578  * INFO:
3579  * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3580  * over the item for a certain period of time.
3581  *
3582  */
3583 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3584 {
3585     if (LISTVIEW_isHotTracking(infoPtr))
3586     {
3587         LVITEMW item;
3588         POINT pt;
3589 
3590         pt.x = x;
3591         pt.y = y;
3592 
3593         if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3594             LISTVIEW_SetSelection(infoPtr, item.iItem);
3595     }
3596 
3597     return 0;
3598 }
3599 
3600 #define SCROLL_LEFT   0x1
3601 #define SCROLL_RIGHT  0x2
3602 #define SCROLL_UP     0x4
3603 #define SCROLL_DOWN   0x8
3604 
3605 /***
3606  * DESCRIPTION:
3607  * Utility routine to draw and highlight items within a marquee selection rectangle.
3608  *
3609  * PARAMETER(S):
3610  * [I] infoPtr     : valid pointer to the listview structure
3611  * [I] coords_orig : original co-ordinates of the cursor
3612  * [I] coords_offs : offsetted coordinates of the cursor
3613  * [I] offset      : offset amount
3614  * [I] scroll      : Bitmask of which directions we should scroll, if at all
3615  *
3616  * RETURN:
3617  *   None.
3618  */
3619 static void LISTVIEW_MarqueeHighlight(LISTVIEW_INFO *infoPtr, LPPOINT coords_orig, LPPOINT coords_offs, LPPOINT offset, INT scroll)
3620 {
3621     BOOL controlDown = FALSE;
3622     LVITEMW item;
3623     ITERATOR i;
3624     RECT rect;
3625 
3626     if (coords_offs->x > infoPtr->marqueeOrigin.x)
3627     {
3628         rect.left = infoPtr->marqueeOrigin.x;
3629         rect.right = coords_offs->x;
3630     }
3631     else
3632     {
3633         rect.left = coords_offs->x;
3634         rect.right = infoPtr->marqueeOrigin.x;
3635     }
3636 
3637     if (coords_offs->y > infoPtr->marqueeOrigin.y)
3638     {
3639         rect.top = infoPtr->marqueeOrigin.y;
3640         rect.bottom = coords_offs->y;
3641     }
3642     else
3643     {
3644         rect.top = coords_offs->y;
3645         rect.bottom = infoPtr->marqueeOrigin.y;
3646     }
3647 
3648     /* Cancel out the old marquee rectangle and draw the new one */
3649     LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeDrawRect);
3650 
3651     /* Scroll by the appropriate distance if applicable - speed up scrolling as
3652        the cursor is further away */
3653 
3654     if ((scroll & SCROLL_LEFT) && (coords_orig->x <= 0))
3655         LISTVIEW_Scroll(infoPtr, coords_orig->x, 0);
3656 
3657     if ((scroll & SCROLL_RIGHT) && (coords_orig->x >= infoPtr->rcList.right))
3658         LISTVIEW_Scroll(infoPtr, (coords_orig->x - infoPtr->rcList.right), 0);
3659 
3660     if ((scroll & SCROLL_UP) && (coords_orig->y <= 0))
3661         LISTVIEW_Scroll(infoPtr, 0, coords_orig->y);
3662 
3663     if ((scroll & SCROLL_DOWN) && (coords_orig->y >= infoPtr->rcList.bottom))
3664         LISTVIEW_Scroll(infoPtr, 0, (coords_orig->y - infoPtr->rcList.bottom));
3665 
3666     /* Invert the items in the old marquee rectangle */
3667     iterator_frameditems_absolute(&i, infoPtr, &infoPtr->marqueeRect);
3668 
3669     while (iterator_next(&i))
3670     {
3671         if (i.nItem > -1)
3672         {
3673             if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED) == LVIS_SELECTED)
3674                 item.state = 0;
3675             else
3676                 item.state = LVIS_SELECTED;
3677 
3678             item.stateMask = LVIS_SELECTED;
3679 
3680             LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3681         }
3682     }
3683 
3684     iterator_destroy(&i);
3685 
3686     CopyRect(&infoPtr->marqueeRect, &rect);
3687 
3688     CopyRect(&infoPtr->marqueeDrawRect, &rect);
3689     OffsetRect(&infoPtr->marqueeDrawRect, offset->x, offset->y);
3690 
3691     /* Iterate over the items within our marquee rectangle */
3692     iterator_frameditems_absolute(&i, infoPtr, &infoPtr->marqueeRect);
3693 
3694     if (GetKeyState(VK_CONTROL) & 0x8000)
3695         controlDown = TRUE;
3696 
3697     while (iterator_next(&i))
3698     {
3699         if (i.nItem > -1)
3700         {
3701             /* If CTRL is pressed, invert. If not, always select the item. */
3702             if ((controlDown) && (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED)))
3703                 item.state = 0;
3704             else
3705                 item.state = LVIS_SELECTED;
3706 
3707             item.stateMask = LVIS_SELECTED;
3708 
3709             LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3710         }
3711     }
3712 
3713     iterator_destroy(&i);
3714     LISTVIEW_InvalidateRect(infoPtr, &rect);
3715 }
3716 
3717 /***
3718  * DESCRIPTION:
3719  * Called when we are in a marquee selection that involves scrolling the listview (ie,
3720  * the cursor is outside the bounds of the client area). This is a TIMERPROC.
3721  *
3722  * PARAMETER(S):
3723  * [I] hwnd : Handle to the listview
3724  * [I] uMsg : WM_TIMER (ignored)
3725  * [I] idEvent : The timer ID interpreted as a pointer to a LISTVIEW_INFO struct
3726  * [I] dwTimer : The elapsed time (ignored)
3727  *
3728  * RETURN:
3729  *   None.
3730  */
3731 static VOID CALLBACK LISTVIEW_ScrollTimer(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
3732 {
3733     LISTVIEW_INFO *infoPtr;
3734     SCROLLINFO scrollInfo;
3735     POINT coords_orig;
3736     POINT coords_offs;
3737     POINT offset;
3738     INT scroll = 0;
3739 
3740     infoPtr = (LISTVIEW_INFO *) idEvent;
3741 
3742     if (!infoPtr)
3743         return;
3744 
3745     /* Get the current cursor position and convert to client coordinates */
3746     GetCursorPos(&coords_orig);
3747     ScreenToClient(hWnd, &coords_orig);
3748 
3749     /* Ensure coordinates are within client bounds */
3750     coords_offs.x = max(min(coords_orig.x, infoPtr->rcList.right), 0);
3751     coords_offs.y = max(min(coords_orig.y, infoPtr->rcList.bottom), 0);
3752 
3753     /* Get offset */
3754     LISTVIEW_GetOrigin(infoPtr, &offset);
3755 
3756     /* Offset coordinates by the appropriate amount */
3757     coords_offs.x -= offset.x;
3758     coords_offs.y -= offset.y;
3759 
3760     scrollInfo.cbSize = sizeof(SCROLLINFO);
3761     scrollInfo.fMask = SIF_ALL;
3762 
3763     /* Work out in which directions we can scroll */
3764     if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3765     {
3766         if (scrollInfo.nPos != scrollInfo.nMin)
3767             scroll |= SCROLL_UP;
3768 
3769         if (((scrollInfo.nPage + scrollInfo.nPos) - 1) != scrollInfo.nMax)
3770             scroll |= SCROLL_DOWN;
3771     }
3772 
3773     if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3774     {
3775         if (scrollInfo.nPos != scrollInfo.nMin)
3776             scroll |= SCROLL_LEFT;
3777 
3778         if (((scrollInfo.nPage + scrollInfo.nPos) - 1) != scrollInfo.nMax)
3779             scroll |= SCROLL_RIGHT;
3780     }
3781 
3782     if (((coords_orig.x <= 0) && (scroll & SCROLL_LEFT)) ||
3783         ((coords_orig.y <= 0) && (scroll & SCROLL_UP))   ||
3784         ((coords_orig.x >= infoPtr->rcList.right) && (scroll & SCROLL_RIGHT)) ||
3785         ((coords_orig.y >= infoPtr->rcList.bottom) && (scroll & SCROLL_DOWN)))
3786     {
3787         LISTVIEW_MarqueeHighlight(infoPtr, &coords_orig, &coords_offs, &offset, scroll);
3788     }
3789 }
3790 
3791 /***
3792  * DESCRIPTION:
3793  * Called whenever WM_MOUSEMOVE is received.
3794  *
3795  * PARAMETER(S):
3796  * [I] infoPtr : valid pointer to the listview structure
3797  * [I] fwKeys : key indicator
3798  * [I] x,y : mouse position
3799  *
3800  * RETURN:
3801  *   0 if the message is processed, non-zero if there was an error
3802  */
3803 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3804 {
3805     TRACKMOUSEEVENT trackinfo;
3806 
3807     if (!(fwKeys & MK_LBUTTON))
3808         infoPtr->bLButtonDown = FALSE;
3809 
3810     if (infoPtr->bLButtonDown)
3811     {
3812         POINT tmp;
3813         RECT rect;
3814         LVHITTESTINFO lvHitTestInfo;
3815         WORD wDragWidth = GetSystemMetrics(SM_CXDRAG);
3816         WORD wDragHeight= GetSystemMetrics(SM_CYDRAG);
3817 
3818         if (infoPtr->bMarqueeSelect)
3819         {
3820             POINT coords_orig;
3821             POINT coords_offs;
3822             POINT offset;
3823 
3824             coords_orig.x = x;
3825             coords_orig.y = y;
3826 
3827             /* Get offset */
3828             LISTVIEW_GetOrigin(infoPtr, &offset);
3829 
3830             /* Ensure coordinates are within client bounds */
3831             coords_offs.x = max(min(x, infoPtr->rcList.right), 0);
3832             coords_offs.y = max(min(y, infoPtr->rcList.bottom), 0);
3833 
3834             /* Offset coordinates by the appropriate amount */
3835             coords_offs.x -= offset.x;
3836             coords_offs.y -= offset.y;
3837 
3838             /* Enable the timer if we're going outside our bounds, in case the user doesn't
3839                move the mouse again */
3840 
3841             if ((x <= 0) || (y <= 0) || (x >= infoPtr->rcList.right) ||
3842                 (y >= infoPtr->rcList.bottom))
3843             {
3844                 if (!infoPtr->bScrolling)
3845                 {
3846                     infoPtr->bScrolling = TRUE;
3847                     SetTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr, 1, LISTVIEW_ScrollTimer);
3848                 }
3849             }
3850             else
3851             {
3852                 infoPtr->bScrolling = FALSE;
3853                 KillTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr);
3854             }
3855 
3856             LISTVIEW_MarqueeHighlight(infoPtr, &coords_orig, &coords_offs, &offset, 0);
3857             return 0;
3858         }
3859 
3860         rect.left = infoPtr->ptClickPos.x - wDragWidth;
3861         rect.right = infoPtr->ptClickPos.x + wDragWidth;
3862         rect.top = infoPtr->ptClickPos.y - wDragHeight;
3863         rect.bottom = infoPtr->ptClickPos.y + wDragHeight;
3864 
3865         tmp.x = x;
3866         tmp.y = y;
3867 
3868         lvHitTestInfo.pt = tmp;
3869         LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3870 
3871         /* reset item marker */
3872         if (infoPtr->nLButtonDownItem != lvHitTestInfo.iItem)
3873             infoPtr->nLButtonDownItem = -1;
3874 
3875         if (!PtInRect(&rect, tmp))
3876         {
3877             /* this path covers the following:
3878                1. WM_LBUTTONDOWN over selected item (sets focus on it)
3879                2. change focus with keys
3880                3. move mouse over item from step 1 selects it and moves focus on it */
3881             if (infoPtr->nLButtonDownItem != -1 &&
3882                !LISTVIEW_GetItemState(infoPtr, infoPtr->nLButtonDownItem, LVIS_SELECTED))
3883             {
3884                 LVITEMW lvItem;
3885 
3886                 lvItem.state =  LVIS_FOCUSED | LVIS_SELECTED;
3887                 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3888 
3889                 LISTVIEW_SetItemState(infoPtr, infoPtr->nLButtonDownItem, &lvItem);
3890                 infoPtr->nLButtonDownItem = -1;
3891             }
3892 
3893             if (!infoPtr->bDragging)
3894             {
3895                 lvHitTestInfo.pt = infoPtr->ptClickPos;
3896                 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3897 
3898                 /* If the click is outside the range of an item, begin a
3899                    highlight. If not, begin an item drag. */
3900                 if (lvHitTestInfo.iItem == -1)
3901                 {
3902                     NMHDR hdr;
3903 
3904                     /* If we're allowing multiple selections, send notification.
3905                        If return value is non-zero, cancel. */
3906                     if (!(infoPtr->dwStyle & LVS_SINGLESEL) && (notify_hdr(infoPtr, LVN_MARQUEEBEGIN, &hdr) == 0))
3907                     {
3908                         /* Store the absolute coordinates of the click */
3909                         POINT offset;
3910                         LISTVIEW_GetOrigin(infoPtr, &offset);
3911 
3912                         infoPtr->marqueeOrigin.x = infoPtr->ptClickPos.x - offset.x;
3913                         infoPtr->marqueeOrigin.y = infoPtr->ptClickPos.y - offset.y;
3914 
3915                         /* Begin selection and capture mouse */
3916                         infoPtr->bMarqueeSelect = TRUE;
3917                         SetCapture(infoPtr->hwndSelf);
3918                     }
3919                 }
3920                 else
3921                 {
3922                     NMLISTVIEW nmlv;
3923 
3924                     ZeroMemory(&nmlv, sizeof(nmlv));
3925                     nmlv.iItem = lvHitTestInfo.iItem;
3926                     nmlv.ptAction = infoPtr->ptClickPos;
3927 
3928                     notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3929                     infoPtr->bDragging = TRUE;
3930                 }
3931             }
3932 
3933             return 0;
3934         }
3935     }
3936 
3937     /* see if we are supposed to be tracking mouse hovering */
3938     if (LISTVIEW_isHotTracking(infoPtr)) {
3939         /* fill in the trackinfo struct */
3940         trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3941         trackinfo.dwFlags = TME_QUERY;
3942         trackinfo.hwndTrack = infoPtr->hwndSelf;
3943         trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3944 
3945         /* see if we are already tracking this hwnd */
3946         _TrackMouseEvent(&trackinfo);
3947 
3948         if(!(trackinfo.dwFlags & TME_HOVER)) {
3949             trackinfo.dwFlags = TME_HOVER;
3950 
3951             /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3952             _TrackMouseEvent(&trackinfo);
3953         }
3954     }
3955 
3956     return 0;
3957 }
3958 
3959 
3960 /***
3961  * Tests whether the item is assignable to a list with style lStyle
3962  */
3963 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3964 {
3965     if ( (lpLVItem->mask & LVIF_TEXT) && 
3966         (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3967         (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3968     
3969     return TRUE;
3970 }
3971 
3972 
3973 /***
3974  * DESCRIPTION:
3975  * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3976  *
3977  * PARAMETER(S):
3978  * [I] infoPtr : valid pointer to the listview structure
3979  * [I] lpLVItem : valid pointer to new item attributes
3980  * [I] isNew : the item being set is being inserted
3981  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3982  * [O] bChanged : will be set to TRUE if the item really changed
3983  *
3984  * RETURN:
3985  *   SUCCESS : TRUE
3986  *   FAILURE : FALSE
3987  */
3988 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3989 {
3990     ITEM_INFO *lpItem;
3991     NMLISTVIEW nmlv;
3992     UINT uChanged = 0;
3993     LVITEMW item;
3994     /* stateMask is ignored for LVM_INSERTITEM */
3995     UINT stateMask = isNew ? ~0 : lpLVItem->stateMask;
3996 
3997     TRACE("()\n");
3998 
3999     assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
4000     
4001     if (lpLVItem->mask == 0) return TRUE;   
4002 
4003     if (infoPtr->dwStyle & LVS_OWNERDATA)
4004     {
4005         /* a virtual listview only stores selection and focus */
4006         if (lpLVItem->mask & ~LVIF_STATE)
4007             return FALSE;
4008         lpItem = NULL;
4009     }
4010     else
4011     {
4012         HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4013         lpItem = DPA_GetPtr(hdpaSubItems, 0);
4014         assert (lpItem);
4015     }
4016 
4017     /* we need to get the lParam and state of the item */
4018     item.iItem = lpLVItem->iItem;
4019     item.iSubItem = lpLVItem->iSubItem;
4020     item.mask = LVIF_STATE | LVIF_PARAM;
4021     item.stateMask = (infoPtr->dwStyle & LVS_OWNERDATA) ? LVIS_FOCUSED | LVIS_SELECTED : ~0;
4022 
4023     item.state = 0;
4024     item.lParam = 0;
4025     if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
4026 
4027     TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
4028     /* determine what fields will change */    
4029     if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & stateMask & ~infoPtr->uCallbackMask))
4030         uChanged |= LVIF_STATE;
4031 
4032     if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
4033         uChanged |= LVIF_IMAGE;
4034 
4035     if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
4036         uChanged |= LVIF_PARAM;
4037 
4038     if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
4039         uChanged |= LVIF_INDENT;
4040 
4041     if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
4042         uChanged |= LVIF_TEXT;
4043    
4044     TRACE("uChanged=0x%x\n", uChanged); 
4045     if (!uChanged) return TRUE;
4046     *bChanged = TRUE;
4047     
4048     ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4049     nmlv.iItem = lpLVItem->iItem;
4050     nmlv.uNewState = (item.state & ~stateMask) | (lpLVItem->state & stateMask);
4051     nmlv.uOldState = item.state;
4052     nmlv.uChanged = uChanged;
4053     nmlv.lParam = item.lParam;
4054     
4055     /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
4056     /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
4057     /* are enabled */
4058     if(lpItem && !isNew && infoPtr->bDoChangeNotify)
4059     {
4060       HWND hwndSelf = infoPtr->hwndSelf;
4061 
4062       if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
4063         return FALSE;
4064       if (!IsWindow(hwndSelf))
4065         return FALSE;
4066     }
4067 
4068     /* copy information */
4069     if (lpLVItem->mask & LVIF_TEXT)
4070         textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
4071 
4072     if (lpLVItem->mask & LVIF_IMAGE)
4073         lpItem->hdr.iImage = lpLVItem->iImage;
4074 
4075     if (lpLVItem->mask & LVIF_PARAM)
4076         lpItem->lParam = lpLVItem->lParam;