~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/comctl32/listview.c

Version: ~ [ wine-1.1.33 ] ~ [ wine-1.1.32 ] ~ [ wine-1.1.31 ] ~ [ wine-1.1.30 ] ~ [ wine-1.1.29 ] ~ [ wine-1.1.28 ] ~ [ wine-1.1.27 ] ~ [ wine-1.1.26 ] ~ [ wine-1.1.25 ] ~ [ wine-1.1.24 ] ~ [ wine-1.1.23 ] ~ [ wine-1.1.22 ] ~ [ wine-1.1.21 ] ~ [ wine-1.1.20 ] ~ [ wine-1.1.19 ] ~ [ wine-1.1.18 ] ~ [ wine-1.1.17 ] ~ [ wine-1.1.16 ] ~ [ wine-1.1.15 ] ~ [ wine-1.1.14 ] ~ [ wine-1.1.13 ] ~ [ wine-1.1.12 ] ~ [ wine-1.1.11 ] ~ [ wine-1.1.10 ] ~ [ wine-1.1.9 ] ~ [ wine-1.1.8 ] ~ [ wine-1.1.7 ] ~ [ wine-1.0.1 ] ~ [ wine-1.1.6 ] ~ [ wine-1.1.5 ] ~ [ wine-1.1.4 ] ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~

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