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