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

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

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

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