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