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