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

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

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