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