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