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