| /* | 
 |  * Listview control | 
 |  * | 
 |  * Copyright 1998, 1999 Eric Kohl | 
 |  * Copyright 1999 Luc Tourangeau | 
 |  * Copyright 2000 Jason Mawdsley | 
 |  * Copyright 2001 CodeWeavers Inc. | 
 |  * Copyright 2002 Dimitrie O. Paun | 
 |  * | 
 |  * This library is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU Lesser General Public | 
 |  * License as published by the Free Software Foundation; either | 
 |  * version 2.1 of the License, or (at your option) any later version. | 
 |  * | 
 |  * This library is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 |  * Lesser General Public License for more details. | 
 |  * | 
 |  * You should have received a copy of the GNU Lesser General Public | 
 |  * License along with this library; if not, write to the Free Software | 
 |  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
 |  * | 
 |  * NOTES | 
 |  * | 
 |  * This code was audited for completeness against the documented features | 
 |  * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins. | 
 |  *  | 
 |  * Unless otherwise noted, we believe this code to be complete, as per | 
 |  * the specification mentioned above. | 
 |  * If you discover missing features, or bugs, please note them below. | 
 |  *  | 
 |  * TODO: | 
 |  * | 
 |  * Default Message Processing | 
 |  *   -- EN_KILLFOCUS should be handled in WM_COMMAND | 
 |  *   -- WM_CREATE: create the icon and small icon image lists at this point only if | 
 |  *      the LVS_SHAREIMAGELISTS style is not specified. | 
 |  *   -- WM_ERASEBKGND: forward this message to the parent window if the bkgnd | 
 |  *      color is CLR_NONE. | 
 |  *   -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon | 
 |  *      or small icon and the LVS_AUTOARRANGE style is specified. | 
 |  *   -- WM_TIMER | 
 |  *   -- WM_WININICHANGE | 
 |  * | 
 |  * Features | 
 |  *   -- Hot item handling, mouse hovering | 
 |  *   -- Workareas support | 
 |  *   -- Tilemode support | 
 |  *   -- Groups support | 
 |  * | 
 |  * Bugs | 
 |  *   -- Expand large item in ICON mode when the cursor is flying over the icon or text. | 
 |  *   -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs). | 
 |  *   -- LVA_SNAPTOGRID not implemented | 
 |  *   -- LISTVIEW_ApproximateViewRect partially implemented | 
 |  *   -- LISTVIEW_[GS]etColumnOrderArray stubs | 
 |  *   -- LISTVIEW_SetColumnWidth ignores header images & bitmap | 
 |  *   -- LISTVIEW_SetIconSpacing is incomplete | 
 |  *   -- LISTVIEW_SortItems is broken | 
 |  *   -- LISTVIEW_StyleChanged doesn't handle some changes too well | 
 |  * | 
 |  * Speedups | 
 |  *   -- LISTVIEW_GetNextItem needs to be rewritten. It is currently | 
 |  *      linear in the number of items in the list, and this is | 
 |  *      unacceptable for large lists. | 
 |  *   -- in sorted mode, LISTVIEW_InsertItemT sorts the array, | 
 |  *      instead of inserting in the right spot | 
 |  *   -- we should keep an ordered array of coordinates in iconic mode | 
 |  *      this would allow to frame items (iterator_frameditems), | 
 |  *      and find nearest item (LVFI_NEARESTXY) a lot more efficiently | 
 |  * | 
 |  * Flags | 
 |  *   -- LVIF_COLUMNS | 
 |  *   -- LVIF_GROUPID | 
 |  *   -- LVIF_NORECOMPUTE | 
 |  * | 
 |  * States | 
 |  *   -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0) | 
 |  *   -- LVIS_CUT | 
 |  *   -- LVIS_DROPHILITED | 
 |  *   -- LVIS_OVERLAYMASK | 
 |  * | 
 |  * Styles | 
 |  *   -- LVS_NOLABELWRAP | 
 |  *   -- LVS_NOSCROLL (see Q137520) | 
 |  *   -- LVS_SORTASCENDING, LVS_SORTDESCENDING | 
 |  *   -- LVS_ALIGNTOP | 
 |  *   -- LVS_TYPESTYLEMASK | 
 |  * | 
 |  * Extended Styles | 
 |  *   -- LVS_EX_BORDERSELECT | 
 |  *   -- LVS_EX_FLATSB | 
 |  *   -- LVS_EX_GRIDLINES | 
 |  *   -- LVS_EX_HEADERDRAGDROP | 
 |  *   -- LVS_EX_INFOTIP | 
 |  *   -- LVS_EX_LABELTIP | 
 |  *   -- LVS_EX_MULTIWORKAREAS | 
 |  *   -- LVS_EX_ONECLICKACTIVATE | 
 |  *   -- LVS_EX_REGIONAL | 
 |  *   -- LVS_EX_SIMPLESELECT | 
 |  *   -- LVS_EX_TRACKSELECT | 
 |  *   -- LVS_EX_TWOCLICKACTIVATE | 
 |  *   -- LVS_EX_UNDERLINECOLD | 
 |  *   -- LVS_EX_UNDERLINEHOT | 
 |  *    | 
 |  * Notifications: | 
 |  *   -- LVN_BEGINSCROLL, LVN_ENDSCROLL | 
 |  *   -- LVN_GETINFOTIP | 
 |  *   -- LVN_HOTTRACK | 
 |  *   -- LVN_MARQUEEBEGIN | 
 |  *   -- LVN_ODFINDITEM | 
 |  *   -- LVN_SETDISPINFO | 
 |  *   -- NM_HOVER | 
 |  *   -- LVN_BEGINRDRAG | 
 |  * | 
 |  * Messages: | 
 |  *   -- LVM_CANCELEDITLABEL | 
 |  *   -- LVM_ENABLEGROUPVIEW | 
 |  *   -- LVM_GETBKIMAGE, LVM_SETBKIMAGE | 
 |  *   -- LVM_GETGROUPINFO, LVM_SETGROUPINFO | 
 |  *   -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS | 
 |  *   -- LVM_GETINSERTMARK, LVM_SETINSERTMARK | 
 |  *   -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR | 
 |  *   -- LVM_GETINSERTMARKRECT | 
 |  *   -- LVM_GETNUMBEROFWORKAREAS | 
 |  *   -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR | 
 |  *   -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN | 
 |  *   -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA | 
 |  *   -- LVM_GETTILEINFO, LVM_SETTILEINFO | 
 |  *   -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO | 
 |  *   -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT | 
 |  *   -- LVM_GETVIEW, LVM_SETVIEW | 
 |  *   -- LVM_GETWORKAREAS, LVM_SETWORKAREAS | 
 |  *   -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS | 
 |  *   -- LVM_INSERTGROUPSORTED | 
 |  *   -- LVM_INSERTMARKHITTEST | 
 |  *   -- LVM_ISGROUPVIEWENABLED | 
 |  *   -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID | 
 |  *   -- LVM_MOVEGROUP | 
 |  *   -- LVM_MOVEITEMTOGROUP | 
 |  *   -- LVM_SETINFOTIP | 
 |  *   -- LVM_SETTILEWIDTH | 
 |  *   -- LVM_SORTGROUPS | 
 |  *   -- LVM_SORTITEMSEX | 
 |  * | 
 |  * Macros: | 
 |  *   -- ListView_GetCheckSate, ListView_SetCheckState | 
 |  *   -- ListView_GetHoverTime, ListView_SetHoverTime | 
 |  *   -- ListView_GetISearchString | 
 |  *   -- ListView_GetNumberOfWorkAreas | 
 |  *   -- ListView_GetOrigin | 
 |  *   -- ListView_GetTextBkColor | 
 |  *   -- ListView_GetUnicodeFormat, ListView_SetUnicodeFormat | 
 |  *   -- ListView_GetWorkAreas, ListView_SetWorkAreas | 
 |  *   -- ListView_SortItemsEx | 
 |  * | 
 |  * Functions: | 
 |  *   -- LVGroupComparE | 
 |  * | 
 |  * Known differences in message stream from native control (not known if | 
 |  * these differences cause problems): | 
 |  *   LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases. | 
 |  *   LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED. | 
 |  *   WM_CREATE does not issue WM_QUERYUISTATE and associated registry | 
 |  *     processing for "USEDOUBLECLICKTIME". | 
 |  */ | 
 |  | 
 | #include "config.h" | 
 | #include "wine/port.h" | 
 |  | 
 | #include <assert.h> | 
 | #include <ctype.h> | 
 | #include <string.h> | 
 | #include <stdlib.h> | 
 | #include <stdarg.h> | 
 | #include <stdio.h> | 
 |  | 
 | #include "windef.h" | 
 | #include "winbase.h" | 
 | #include "winnt.h" | 
 | #include "wingdi.h" | 
 | #include "winuser.h" | 
 | #include "winnls.h" | 
 | #include "commctrl.h" | 
 | #include "comctl32.h" | 
 | #include "uxtheme.h" | 
 |  | 
 | #include "wine/debug.h" | 
 | #include "wine/unicode.h" | 
 |  | 
 | WINE_DEFAULT_DEBUG_CHANNEL(listview); | 
 |  | 
 | /* make sure you set this to 0 for production use! */ | 
 | #define DEBUG_RANGES 1 | 
 |  | 
 | typedef struct tagCOLUMN_INFO | 
 | { | 
 |   RECT rcHeader;	/* tracks the header's rectangle */ | 
 |   int fmt;		/* same as LVCOLUMN.fmt */ | 
 | } COLUMN_INFO; | 
 |  | 
 | typedef struct tagITEMHDR | 
 | { | 
 |   LPWSTR pszText; | 
 |   INT iImage; | 
 | } ITEMHDR, *LPITEMHDR; | 
 |  | 
 | typedef struct tagSUBITEM_INFO | 
 | { | 
 |   ITEMHDR hdr; | 
 |   INT iSubItem; | 
 | } SUBITEM_INFO; | 
 |  | 
 | typedef struct tagITEM_INFO | 
 | { | 
 |   ITEMHDR hdr; | 
 |   UINT state; | 
 |   LPARAM lParam; | 
 |   INT iIndent; | 
 | } ITEM_INFO; | 
 |  | 
 | typedef struct tagRANGE | 
 | { | 
 |   INT lower; | 
 |   INT upper; | 
 | } RANGE; | 
 |  | 
 | typedef struct tagRANGES | 
 | { | 
 |   HDPA hdpa; | 
 | } *RANGES; | 
 |  | 
 | typedef struct tagITERATOR | 
 | { | 
 |   INT nItem; | 
 |   INT nSpecial; | 
 |   RANGE range; | 
 |   RANGES ranges; | 
 |   INT index; | 
 | } ITERATOR; | 
 |  | 
 | typedef struct tagLISTVIEW_INFO | 
 | { | 
 |   HWND hwndSelf; | 
 |   HBRUSH hBkBrush; | 
 |   COLORREF clrBk; | 
 |   COLORREF clrText; | 
 |   COLORREF clrTextBk; | 
 |   COLORREF clrTextBkDefault; | 
 |   HIMAGELIST himlNormal; | 
 |   HIMAGELIST himlSmall; | 
 |   HIMAGELIST himlState; | 
 |   BOOL bLButtonDown; | 
 |   BOOL bRButtonDown; | 
 |   POINT ptClickPos;         /* point where the user clicked */  | 
 |   BOOL bNoItemMetrics;		/* flags if item metrics are not yet computed */ | 
 |   INT nItemHeight; | 
 |   INT nItemWidth; | 
 |   RANGES selectionRanges; | 
 |   INT nSelectionMark; | 
 |   INT nHotItem; | 
 |   SHORT notifyFormat; | 
 |   HWND hwndNotify; | 
 |   RECT rcList;                 /* This rectangle is really the window | 
 | 				* client rectangle possibly reduced by the  | 
 | 				* horizontal scroll bar and/or header - see  | 
 | 				* LISTVIEW_UpdateSize. This rectangle offset | 
 | 				* by the LISTVIEW_GetOrigin value is in | 
 | 				* client coordinates   */ | 
 |   SIZE iconSize; | 
 |   SIZE iconSpacing; | 
 |   SIZE iconStateSize; | 
 |   UINT uCallbackMask; | 
 |   HWND hwndHeader; | 
 |   HCURSOR hHotCursor; | 
 |   HFONT hDefaultFont; | 
 |   HFONT hFont; | 
 |   INT ntmHeight;		/* Some cached metrics of the font used */ | 
 |   INT ntmMaxCharWidth;		/* by the listview to draw items */ | 
 |   INT nEllipsisWidth; | 
 |   BOOL bRedraw;  		/* Turns on/off repaints & invalidations */ | 
 |   BOOL bAutoarrange;		/* Autoarrange flag when NOT in LVS_AUTOARRANGE */ | 
 |   BOOL bFocus; | 
 |   BOOL bDoChangeNotify;         /* send change notification messages? */ | 
 |   INT nFocusedItem; | 
 |   RECT rcFocus; | 
 |   DWORD dwStyle;		/* the cached window GWL_STYLE */ | 
 |   DWORD dwLvExStyle;		/* extended listview style */ | 
 |   INT nItemCount;		/* the number of items in the list */ | 
 |   HDPA hdpaItems;               /* array ITEM_INFO pointers */ | 
 |   HDPA hdpaPosX;		/* maintains the (X, Y) coordinates of the */ | 
 |   HDPA hdpaPosY;		/* items in LVS_ICON, and LVS_SMALLICON modes */ | 
 |   HDPA hdpaColumns;		/* array of COLUMN_INFO pointers */ | 
 |   POINT currIconPos;		/* this is the position next icon will be placed */ | 
 |   PFNLVCOMPARE pfnCompare; | 
 |   LPARAM lParamSort; | 
 |   HWND hwndEdit; | 
 |   WNDPROC EditWndProc; | 
 |   INT nEditLabelItem; | 
 |   DWORD dwHoverTime; | 
 |   HWND hwndToolTip; | 
 |  | 
 |   DWORD cditemmode;             /* Keep the custom draw flags for an item/row */ | 
 |  | 
 |   DWORD lastKeyPressTimestamp; | 
 |   WPARAM charCode; | 
 |   INT nSearchParamLength; | 
 |   WCHAR szSearchParam[ MAX_PATH ]; | 
 |   BOOL bIsDrawing; | 
 |   INT nMeasureItemHeight; | 
 | } LISTVIEW_INFO; | 
 |  | 
 | /* | 
 |  * constants | 
 |  */ | 
 | /* How many we debug buffer to allocate */ | 
 | #define DEBUG_BUFFERS 20 | 
 | /* The size of a single debug bbuffer */ | 
 | #define DEBUG_BUFFER_SIZE 256 | 
 |  | 
 | /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */ | 
 | #define SB_INTERNAL      -1 | 
 |  | 
 | /* maximum size of a label */ | 
 | #define DISP_TEXT_SIZE 512 | 
 |  | 
 | /* padding for items in list and small icon display modes */ | 
 | #define WIDTH_PADDING 12 | 
 |  | 
 | /* padding for items in list, report and small icon display modes */ | 
 | #define HEIGHT_PADDING 1 | 
 |  | 
 | /* offset of items in report display mode */ | 
 | #define REPORT_MARGINX 2 | 
 |  | 
 | /* padding for icon in large icon display mode | 
 |  *   ICON_TOP_PADDING_NOTHITABLE - space between top of box and area | 
 |  *                                 that HITTEST will see. | 
 |  *   ICON_TOP_PADDING_HITABLE - spacing between above and icon. | 
 |  *   ICON_TOP_PADDING - sum of the two above. | 
 |  *   ICON_BOTTOM_PADDING - between bottom of icon and top of text | 
 |  *   LABEL_HOR_PADDING - between text and sides of box | 
 |  *   LABEL_VERT_PADDING - between bottom of text and end of box | 
 |  * | 
 |  *   ICON_LR_PADDING - additional width above icon size. | 
 |  *   ICON_LR_HALF - half of the above value | 
 |  */ | 
 | #define ICON_TOP_PADDING_NOTHITABLE  2 | 
 | #define ICON_TOP_PADDING_HITABLE     2 | 
 | #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE) | 
 | #define ICON_BOTTOM_PADDING          4 | 
 | #define LABEL_HOR_PADDING            5 | 
 | #define LABEL_VERT_PADDING           7 | 
 | #define ICON_LR_PADDING              16 | 
 | #define ICON_LR_HALF                 (ICON_LR_PADDING/2) | 
 |  | 
 | /* default label width for items in list and small icon display modes */ | 
 | #define DEFAULT_LABEL_WIDTH 40 | 
 |  | 
 | /* default column width for items in list display mode */ | 
 | #define DEFAULT_COLUMN_WIDTH 128 | 
 |  | 
 | /* Size of "line" scroll for V & H scrolls */ | 
 | #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37 | 
 |  | 
 | /* Padding betwen image and label */ | 
 | #define IMAGE_PADDING  2 | 
 |  | 
 | /* Padding behind the label */ | 
 | #define TRAILING_LABEL_PADDING  12 | 
 | #define TRAILING_HEADER_PADDING  11 | 
 |  | 
 | /* Border for the icon caption */ | 
 | #define CAPTION_BORDER  2 | 
 |  | 
 | /* Standard DrawText flags */ | 
 | #define LV_ML_DT_FLAGS  (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS) | 
 | #define LV_FL_DT_FLAGS  (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP) | 
 | #define LV_SL_DT_FLAGS  (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS) | 
 |  | 
 | /* Image index from state */ | 
 | #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12) | 
 |  | 
 | /* The time in milliseconds to reset the search in the list */ | 
 | #define KEY_DELAY       450 | 
 |  | 
 | /* Dump the LISTVIEW_INFO structure to the debug channel */ | 
 | #define LISTVIEW_DUMP(iP) do { \ | 
 |   TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \ | 
 |         iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \ | 
 |         iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \ | 
 |   TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \ | 
 |         iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \ | 
 |         iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \ | 
 |   TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \ | 
 |         iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \ | 
 |         iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \ | 
 |   TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \ | 
 | } while(0) | 
 |  | 
 | static const WCHAR themeClass[] = {'L','i','s','t','V','i','e','w',0}; | 
 |  | 
 | /* | 
 |  * forward declarations | 
 |  */ | 
 | static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL); | 
 | static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT); | 
 | static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT); | 
 | static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT); | 
 | static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT); | 
 | static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT); | 
 | static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT); | 
 | static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT); | 
 | static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT); | 
 | static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, const LVITEMW *, BOOL); | 
 | static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *); | 
 | static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT); | 
 | static void LISTVIEW_UpdateSize(LISTVIEW_INFO *); | 
 | static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL); | 
 | static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM); | 
 | static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM); | 
 | static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL); | 
 | static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT); | 
 | static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT); | 
 | static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *); | 
 | static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND); | 
 | static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND); | 
 | static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *); | 
 | static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL); | 
 | static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL); | 
 | static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST); | 
 | static INT LISTVIEW_HitTest(LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL); | 
 |  | 
 | /******** Text handling functions *************************************/ | 
 |  | 
 | /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a | 
 |  * text string. The string may be ANSI or Unicode, in which case | 
 |  * the boolean isW tells us the type of the string. | 
 |  * | 
 |  * The name of the function tell what type of strings it expects: | 
 |  *   W: Unicode, T: ANSI/Unicode - function of isW | 
 |  */ | 
 |  | 
 | static inline BOOL is_textW(LPCWSTR text) | 
 | { | 
 |     return text != NULL && text != LPSTR_TEXTCALLBACKW; | 
 | } | 
 |  | 
 | static inline BOOL is_textT(LPCWSTR text, BOOL isW) | 
 | { | 
 |     /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */ | 
 |     return is_textW(text); | 
 | } | 
 |  | 
 | static inline int textlenT(LPCWSTR text, BOOL isW) | 
 | { | 
 |     return !is_textT(text, isW) ? 0 : | 
 | 	   isW ? lstrlenW(text) : lstrlenA((LPCSTR)text); | 
 | } | 
 |  | 
 | static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max) | 
 | { | 
 |     if (isDestW) | 
 | 	if (isSrcW) lstrcpynW(dest, src, max); | 
 | 	else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max); | 
 |     else | 
 | 	if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL); | 
 | 	else lstrcpynA((LPSTR)dest, (LPCSTR)src, max); | 
 | } | 
 |  | 
 | static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW) | 
 | { | 
 |     LPWSTR wstr = (LPWSTR)text; | 
 |  | 
 |     if (!isW && is_textT(text, isW)) | 
 |     { | 
 | 	INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0); | 
 | 	wstr = Alloc(len * sizeof(WCHAR)); | 
 | 	if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len); | 
 |     } | 
 |     TRACE("   wstr=%s\n", text == LPSTR_TEXTCALLBACKW ?  "(callback)" : debugstr_w(wstr)); | 
 |     return wstr; | 
 | } | 
 |  | 
 | static inline void textfreeT(LPWSTR wstr, BOOL isW) | 
 | { | 
 |     if (!isW && is_textT(wstr, isW)) Free (wstr); | 
 | } | 
 |  | 
 | /* | 
 |  * dest is a pointer to a Unicode string | 
 |  * src is a pointer to a string (Unicode if isW, ANSI if !isW) | 
 |  */ | 
 | static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW) | 
 | { | 
 |     BOOL bResult = TRUE; | 
 |      | 
 |     if (src == LPSTR_TEXTCALLBACKW) | 
 |     { | 
 | 	if (is_textW(*dest)) Free(*dest); | 
 | 	*dest = LPSTR_TEXTCALLBACKW; | 
 |     } | 
 |     else | 
 |     { | 
 | 	LPWSTR pszText = textdupTtoW(src, isW); | 
 | 	if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL; | 
 | 	bResult = Str_SetPtrW(dest, pszText); | 
 | 	textfreeT(pszText, isW); | 
 |     } | 
 |     return bResult; | 
 | } | 
 |  | 
 | /* | 
 |  * compares a Unicode to a Unicode/ANSI text string | 
 |  */ | 
 | static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW) | 
 | { | 
 |     if (!aw) return bt ? -1 : 0; | 
 |     if (!bt) return aw ? 1 : 0; | 
 |     if (aw == LPSTR_TEXTCALLBACKW) | 
 | 	return bt == LPSTR_TEXTCALLBACKW ? 0 : -1; | 
 |     if (bt != LPSTR_TEXTCALLBACKW) | 
 |     { | 
 | 	LPWSTR bw = textdupTtoW(bt, isW); | 
 | 	int r = bw ? lstrcmpW(aw, bw) : 1; | 
 | 	textfreeT(bw, isW); | 
 | 	return r; | 
 |     }	     | 
 | 	     | 
 |     return 1; | 
 | } | 
 |      | 
 | static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n) | 
 | { | 
 |     int res; | 
 |  | 
 |     n = min(min(n, strlenW(s1)), strlenW(s2)); | 
 |     res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n); | 
 |     return res ? res - sizeof(WCHAR) : res; | 
 | } | 
 |  | 
 | /******** Debugging functions *****************************************/ | 
 |  | 
 | static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW) | 
 | { | 
 |     if (text == LPSTR_TEXTCALLBACKW) return "(callback)"; | 
 |     return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text); | 
 | } | 
 |  | 
 | static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n) | 
 | { | 
 |     if (text == LPSTR_TEXTCALLBACKW) return "(callback)"; | 
 |     n = min(textlenT(text, isW), n); | 
 |     return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n); | 
 | } | 
 |  | 
 | static char* debug_getbuf(void) | 
 | { | 
 |     static int index = 0; | 
 |     static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE]; | 
 |     return buffers[index++ % DEBUG_BUFFERS]; | 
 | } | 
 |  | 
 | static inline const char* debugrange(const RANGE *lprng) | 
 | { | 
 |     if (!lprng) return "(null)"; | 
 |     return wine_dbg_sprintf("[%d, %d)", lprng->lower, lprng->upper); | 
 | } | 
 |  | 
 | static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo) | 
 | { | 
 |     char* buf = debug_getbuf(), *text = buf; | 
 |     int len, size = DEBUG_BUFFER_SIZE; | 
 |  | 
 |     if (pScrollInfo == NULL) return "(null)"; | 
 |     len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize); | 
 |     if (len == -1) goto end; buf += len; size -= len; | 
 |     if (pScrollInfo->fMask & SIF_RANGE) | 
 | 	len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax); | 
 |     else len = 0; | 
 |     if (len == -1) goto end; buf += len; size -= len; | 
 |     if (pScrollInfo->fMask & SIF_PAGE) | 
 | 	len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage); | 
 |     else len = 0; | 
 |     if (len == -1) goto end; buf += len; size -= len; | 
 |     if (pScrollInfo->fMask & SIF_POS) | 
 | 	len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos); | 
 |     else len = 0; | 
 |     if (len == -1) goto end; buf += len; size -= len; | 
 |     if (pScrollInfo->fMask & SIF_TRACKPOS) | 
 | 	len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos); | 
 |     else len = 0; | 
 |     if (len == -1) goto end; buf += len; size -= len; | 
 |     goto undo; | 
 | end: | 
 |     buf = text + strlen(text); | 
 | undo: | 
 |     if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; } | 
 |     return text; | 
 | }  | 
 |  | 
 | static const char* debugnmlistview(const NMLISTVIEW *plvnm) | 
 | { | 
 |     if (!plvnm) return "(null)"; | 
 |     return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x," | 
 | 	         " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n", | 
 | 	         plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState, | 
 | 		 plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam); | 
 | } | 
 |  | 
 | static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW) | 
 | { | 
 |     char* buf = debug_getbuf(), *text = buf; | 
 |     int len, size = DEBUG_BUFFER_SIZE; | 
 |      | 
 |     if (lpLVItem == NULL) return "(null)"; | 
 |     len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem); | 
 |     if (len == -1) goto end; buf += len; size -= len; | 
 |     if (lpLVItem->mask & LVIF_STATE) | 
 | 	len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask); | 
 |     else len = 0; | 
 |     if (len == -1) goto end; buf += len; size -= len; | 
 |     if (lpLVItem->mask & LVIF_TEXT) | 
 | 	len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax); | 
 |     else len = 0; | 
 |     if (len == -1) goto end; buf += len; size -= len; | 
 |     if (lpLVItem->mask & LVIF_IMAGE) | 
 | 	len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage); | 
 |     else len = 0; | 
 |     if (len == -1) goto end; buf += len; size -= len; | 
 |     if (lpLVItem->mask & LVIF_PARAM) | 
 | 	len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam); | 
 |     else len = 0; | 
 |     if (len == -1) goto end; buf += len; size -= len; | 
 |     if (lpLVItem->mask & LVIF_INDENT) | 
 | 	len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent); | 
 |     else len = 0; | 
 |     if (len == -1) goto end; buf += len; size -= len; | 
 |     goto undo; | 
 | end: | 
 |     buf = text + strlen(text); | 
 | undo: | 
 |     if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; } | 
 |     return text; | 
 | } | 
 |  | 
 | static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW) | 
 | { | 
 |     char* buf = debug_getbuf(), *text = buf; | 
 |     int len, size = DEBUG_BUFFER_SIZE; | 
 |      | 
 |     if (lpColumn == NULL) return "(null)"; | 
 |     len = snprintf(buf, size, "{"); | 
 |     if (len == -1) goto end; buf += len; size -= len; | 
 |     if (lpColumn->mask & LVCF_SUBITEM) | 
 | 	len = snprintf(buf, size, "iSubItem=%d, ",  lpColumn->iSubItem); | 
 |     else len = 0; | 
 |     if (len == -1) goto end; buf += len; size -= len; | 
 |     if (lpColumn->mask & LVCF_FMT) | 
 | 	len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt); | 
 |     else len = 0; | 
 |     if (len == -1) goto end; buf += len; size -= len; | 
 |     if (lpColumn->mask & LVCF_WIDTH) | 
 | 	len = snprintf(buf, size, "cx=%d, ", lpColumn->cx); | 
 |     else len = 0; | 
 |     if (len == -1) goto end; buf += len; size -= len; | 
 |     if (lpColumn->mask & LVCF_TEXT) | 
 | 	len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax); | 
 |     else len = 0; | 
 |     if (len == -1) goto end; buf += len; size -= len; | 
 |     if (lpColumn->mask & LVCF_IMAGE) | 
 | 	len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage); | 
 |     else len = 0; | 
 |     if (len == -1) goto end; buf += len; size -= len; | 
 |     if (lpColumn->mask & LVCF_ORDER) | 
 | 	len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder); | 
 |     else len = 0; | 
 |     if (len == -1) goto end; buf += len; size -= len; | 
 |     goto undo; | 
 | end: | 
 |     buf = text + strlen(text); | 
 | undo: | 
 |     if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; } | 
 |     return text; | 
 | } | 
 |  | 
 | static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht) | 
 | { | 
 |     if (!lpht) return "(null)"; | 
 |  | 
 |     return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}", | 
 | 		 wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem); | 
 | } | 
 |  | 
 | /* Return the corresponding text for a given scroll value */ | 
 | static inline LPCSTR debugscrollcode(int nScrollCode) | 
 | { | 
 |   switch(nScrollCode) | 
 |   { | 
 |   case SB_LINELEFT: return "SB_LINELEFT"; | 
 |   case SB_LINERIGHT: return "SB_LINERIGHT"; | 
 |   case SB_PAGELEFT: return "SB_PAGELEFT"; | 
 |   case SB_PAGERIGHT: return "SB_PAGERIGHT"; | 
 |   case SB_THUMBPOSITION: return "SB_THUMBPOSITION"; | 
 |   case SB_THUMBTRACK: return "SB_THUMBTRACK"; | 
 |   case SB_ENDSCROLL: return "SB_ENDSCROLL"; | 
 |   case SB_INTERNAL: return "SB_INTERNAL"; | 
 |   default: return "unknown"; | 
 |   } | 
 | } | 
 |  | 
 |  | 
 | /******** Notification functions i************************************/ | 
 |  | 
 | static LRESULT notify_forward_header(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh) | 
 | { | 
 |     return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, | 
 |                         (WPARAM)lpnmh->hdr.idFrom, (LPARAM)lpnmh); | 
 | } | 
 |  | 
 | static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh) | 
 | { | 
 |     LRESULT result; | 
 |      | 
 |     TRACE("(code=%d)\n", code); | 
 |  | 
 |     pnmh->hwndFrom = infoPtr->hwndSelf; | 
 |     pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID); | 
 |     pnmh->code = code; | 
 |     result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, | 
 | 			  (WPARAM)pnmh->idFrom, (LPARAM)pnmh); | 
 |  | 
 |     TRACE("  <= %ld\n", result); | 
 |  | 
 |     return result; | 
 | } | 
 |  | 
 | static inline BOOL notify(LISTVIEW_INFO *infoPtr, INT code) | 
 | { | 
 |     NMHDR nmh; | 
 |     HWND hwnd = infoPtr->hwndSelf; | 
 |     notify_hdr(infoPtr, code, &nmh); | 
 |     return IsWindow(hwnd); | 
 | } | 
 |  | 
 | static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr, LVHITTESTINFO *htInfo) | 
 | { | 
 |     NMITEMACTIVATE nmia; | 
 |     LVITEMW item; | 
 |  | 
 |     if (htInfo) { | 
 |       nmia.uNewState = 0; | 
 |       nmia.uOldState = 0; | 
 |       nmia.uChanged  = 0; | 
 |       nmia.uKeyFlags = 0; | 
 |        | 
 |       item.mask = LVIF_PARAM|LVIF_STATE; | 
 |       item.iItem = htInfo->iItem; | 
 |       item.iSubItem = 0; | 
 |       if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) { | 
 | 	  nmia.lParam = item.lParam; | 
 |  	  nmia.uOldState = item.state; | 
 | 	  nmia.uNewState = item.state | LVIS_ACTIVATING; | 
 | 	  nmia.uChanged  = LVIF_STATE; | 
 |       } | 
 |        | 
 |       nmia.iItem = htInfo->iItem; | 
 |       nmia.iSubItem = htInfo->iSubItem; | 
 |       nmia.ptAction = htInfo->pt;      | 
 |        | 
 |       if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT; | 
 |       if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL; | 
 |       if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT; | 
 |     } | 
 |     notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia); | 
 | } | 
 |  | 
 | static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm) | 
 | { | 
 |     TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm)); | 
 |     return notify_hdr(infoPtr, code, (LPNMHDR)plvnm); | 
 | } | 
 |  | 
 | static BOOL notify_click(LISTVIEW_INFO *infoPtr,  INT code, LVHITTESTINFO *lvht) | 
 | { | 
 |     NMLISTVIEW nmlv; | 
 |     LVITEMW item; | 
 |     HWND hwnd = infoPtr->hwndSelf; | 
 |  | 
 |     TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));  | 
 |     ZeroMemory(&nmlv, sizeof(nmlv)); | 
 |     nmlv.iItem = lvht->iItem; | 
 |     nmlv.iSubItem = lvht->iSubItem; | 
 |     nmlv.ptAction = lvht->pt; | 
 |     item.mask = LVIF_PARAM; | 
 |     item.iItem = lvht->iItem; | 
 |     item.iSubItem = 0; | 
 |     if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam; | 
 |     notify_listview(infoPtr, code, &nmlv); | 
 |     return IsWindow(hwnd); | 
 | } | 
 |  | 
 | static BOOL notify_deleteitem(LISTVIEW_INFO *infoPtr, INT nItem) | 
 | { | 
 |     NMLISTVIEW nmlv; | 
 |     LVITEMW item; | 
 |     HWND hwnd = infoPtr->hwndSelf; | 
 |  | 
 |     ZeroMemory(&nmlv, sizeof (NMLISTVIEW)); | 
 |     nmlv.iItem = nItem; | 
 |     item.mask = LVIF_PARAM; | 
 |     item.iItem = nItem; | 
 |     item.iSubItem = 0; | 
 |     if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam; | 
 |     notify_listview(infoPtr, LVN_DELETEITEM, &nmlv); | 
 |     return IsWindow(hwnd); | 
 | } | 
 |  | 
 | static int get_ansi_notification(INT unicodeNotificationCode) | 
 | { | 
 |     switch (unicodeNotificationCode) | 
 |     { | 
 |     case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA; | 
 |     case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA; | 
 |     case LVN_GETDISPINFOW: return LVN_GETDISPINFOA; | 
 |     case LVN_SETDISPINFOW: return LVN_SETDISPINFOA; | 
 |     case LVN_ODFINDITEMW: return LVN_ODFINDITEMA; | 
 |     case LVN_GETINFOTIPW: return LVN_GETINFOTIPA; | 
 |     } | 
 |     ERR("unknown notification %x\n", unicodeNotificationCode); | 
 |     assert(FALSE); | 
 |     return 0; | 
 | } | 
 |  | 
 | /* | 
 |   Send notification. depends on dispinfoW having same | 
 |   structure as dispinfoA. | 
 |   infoPtr : listview struct | 
 |   notificationCode : *Unicode* notification code | 
 |   pdi : dispinfo structure (can be unicode or ansi) | 
 |   isW : TRUE if dispinfo is Unicode | 
 | */ | 
 | static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW) | 
 | { | 
 |     BOOL bResult = FALSE; | 
 |     BOOL convertToAnsi = FALSE, convertToUnicode = FALSE; | 
 |     INT cchTempBufMax = 0, savCchTextMax = 0, realNotifCode; | 
 |     LPWSTR pszTempBuf = NULL, savPszText = NULL; | 
 |  | 
 |     if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW)) | 
 |     { | 
 | 	convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI); | 
 | 	convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE); | 
 |     } | 
 |  | 
 |     if (convertToAnsi || convertToUnicode) | 
 |     { | 
 | 	if (notificationCode != LVN_GETDISPINFOW) | 
 |  	{ | 
 |  	    cchTempBufMax = convertToUnicode ? | 
 |        		MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0): | 
 |        		WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL); | 
 | 	} | 
 |  	else | 
 |  	{ | 
 |  	    cchTempBufMax = pdi->item.cchTextMax; | 
 |  	    *pdi->item.pszText = 0; /* make sure we don't process garbage */ | 
 |  	} | 
 |  | 
 | 	pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax); | 
 |         if (!pszTempBuf) return FALSE; | 
 |  | 
 | 	if (convertToUnicode) | 
 | 	    MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, | 
 | 	                        pszTempBuf, cchTempBufMax); | 
 | 	else | 
 | 	    WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf, | 
 | 	                        cchTempBufMax, NULL, NULL); | 
 |  | 
 |         savCchTextMax = pdi->item.cchTextMax; | 
 |         savPszText = pdi->item.pszText; | 
 |         pdi->item.pszText = pszTempBuf; | 
 |         pdi->item.cchTextMax = cchTempBufMax; | 
 |     } | 
 |  | 
 |     if (infoPtr->notifyFormat == NFR_ANSI) | 
 | 	realNotifCode = get_ansi_notification(notificationCode); | 
 |     else | 
 | 	realNotifCode = notificationCode; | 
 |     TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI)); | 
 |     bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr); | 
 |  | 
 |     if (convertToUnicode || convertToAnsi) | 
 |     { | 
 | 	if (convertToUnicode) /* note : pointer can be changed by app ! */ | 
 |  	    WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText, | 
 |                                 savCchTextMax, NULL, NULL); | 
 | 	else | 
 | 	    MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1, | 
 | 	                        savPszText, savCchTextMax); | 
 |         pdi->item.pszText = savPszText; /* restores our buffer */ | 
 |         pdi->item.cchTextMax = savCchTextMax; | 
 |         Free (pszTempBuf); | 
 |     } | 
 |     return bResult; | 
 | } | 
 |  | 
 | static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc,  | 
 | 			    const RECT *rcBounds, const LVITEMW *lplvItem) | 
 | { | 
 |     ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW)); | 
 |     lpnmlvcd->nmcd.hdc = hdc; | 
 |     lpnmlvcd->nmcd.rc = *rcBounds; | 
 |     lpnmlvcd->clrTextBk = infoPtr->clrTextBk; | 
 |     lpnmlvcd->clrText   = infoPtr->clrText; | 
 |     if (!lplvItem) return; | 
 |     lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1; | 
 |     lpnmlvcd->iSubItem = lplvItem->iSubItem; | 
 |     if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED; | 
 |     if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS; | 
 |     if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT; | 
 |     lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam; | 
 | } | 
 |  | 
 | static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd) | 
 | { | 
 |     BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0); | 
 |     DWORD result; | 
 |  | 
 |     lpnmlvcd->nmcd.dwDrawStage = dwDrawStage; | 
 |     if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;  | 
 |     if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM; | 
 |     if (isForItem) lpnmlvcd->nmcd.dwItemSpec--; | 
 |     result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr); | 
 |     if (isForItem) lpnmlvcd->nmcd.dwItemSpec++; | 
 |     return result; | 
 | } | 
 |  | 
 | static void prepaint_setup (LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd) | 
 | { | 
 |     /* apprently, for selected items, we have to override the returned values */ | 
 |     if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED) | 
 |     { | 
 | 	if (infoPtr->bFocus) | 
 | 	{ | 
 | 	    lpnmlvcd->clrTextBk = comctl32_color.clrHighlight; | 
 | 	    lpnmlvcd->clrText   = comctl32_color.clrHighlightText; | 
 |         } | 
 | 	else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS) | 
 | 	{ | 
 | 	    lpnmlvcd->clrTextBk = comctl32_color.clr3dFace; | 
 | 	    lpnmlvcd->clrText   = comctl32_color.clrBtnText; | 
 | 	} | 
 |     } | 
 |  | 
 |     /* Set the text attributes */ | 
 |     if (lpnmlvcd->clrTextBk != CLR_NONE) | 
 |     { | 
 | 	SetBkMode(hdc, OPAQUE); | 
 | 	if (lpnmlvcd->clrTextBk == CLR_DEFAULT) | 
 | 	    SetBkColor(hdc, infoPtr->clrTextBkDefault); | 
 | 	else | 
 | 	    SetBkColor(hdc,lpnmlvcd->clrTextBk); | 
 |     } | 
 |     else | 
 | 	SetBkMode(hdc, TRANSPARENT); | 
 |     SetTextColor(hdc, lpnmlvcd->clrText); | 
 | } | 
 |  | 
 | static inline DWORD notify_postpaint (LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd) | 
 | { | 
 |     return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd); | 
 | } | 
 |  | 
 | /******** Item iterator functions **********************************/ | 
 |  | 
 | static RANGES ranges_create(int count); | 
 | static void ranges_destroy(RANGES ranges); | 
 | static BOOL ranges_add(RANGES ranges, RANGE range); | 
 | static BOOL ranges_del(RANGES ranges, RANGE range); | 
 | static void ranges_dump(RANGES ranges); | 
 |  | 
 | static inline BOOL ranges_additem(RANGES ranges, INT nItem) | 
 | { | 
 |     RANGE range = { nItem, nItem + 1 }; | 
 |  | 
 |     return ranges_add(ranges, range); | 
 | } | 
 |  | 
 | static inline BOOL ranges_delitem(RANGES ranges, INT nItem) | 
 | { | 
 |     RANGE range = { nItem, nItem + 1 }; | 
 |  | 
 |     return ranges_del(ranges, range); | 
 | } | 
 |  | 
 | /*** | 
 |  * ITERATOR DOCUMENTATION | 
 |  * | 
 |  * The iterator functions allow for easy, and convenient iteration | 
 |  * over items of iterest in the list. Typically, you create a | 
 |  * iterator, use it, and destroy it, as such: | 
 |  *   ITERATOR i; | 
 |  * | 
 |  *   iterator_xxxitems(&i, ...); | 
 |  *   while (iterator_{prev,next}(&i) | 
 |  *   { | 
 |  *       //code which uses i.nItem | 
 |  *   } | 
 |  *   iterator_destroy(&i); | 
 |  * | 
 |  *   where xxx is either: framed, or visible. | 
 |  * Note that it is important that the code destroys the iterator | 
 |  * after it's done with it, as the creation of the iterator may | 
 |  * allocate memory, which thus needs to be freed. | 
 |  *  | 
 |  * You can iterate both forwards, and backwards through the list, | 
 |  * by using iterator_next or iterator_prev respectively. | 
 |  *  | 
 |  * Lower numbered items are draw on top of higher number items in | 
 |  * LVS_ICON, and LVS_SMALLICON (which are the only modes where | 
 |  * items may overlap). So, to test items, you should use | 
 |  *    iterator_next | 
 |  * which lists the items top to bottom (in Z-order). | 
 |  * For drawing items, you should use | 
 |  *    iterator_prev | 
 |  * which lists the items bottom to top (in Z-order). | 
 |  * If you keep iterating over the items after the end-of-items | 
 |  * marker (-1) is returned, the iterator will start from the | 
 |  * beginning. Typically, you don't need to test for -1, | 
 |  * because iterator_{next,prev} will return TRUE if more items | 
 |  * are to be iterated over, or FALSE otherwise. | 
 |  * | 
 |  * Note: the iterator is defined to be bidirectional. That is, | 
 |  *       any number of prev followed by any number of next, or | 
 |  *       five versa, should leave the iterator at the same item: | 
 |  *           prev * n, next * n = next * n, prev * n | 
 |  * | 
 |  * The iterator has a notion of an out-of-order, special item, | 
 |  * which sits at the start of the list. This is used in | 
 |  * LVS_ICON, and LVS_SMALLICON mode to handle the focused item, | 
 |  * which needs to be first, as it may overlap other items. | 
 |  *            | 
 |  * The code is a bit messy because we have: | 
 |  *   - a special item to deal with | 
 |  *   - simple range, or composite range | 
 |  *   - empty range. | 
 |  * If you find bugs, or want to add features, please make sure you | 
 |  * always check/modify *both* iterator_prev, and iterator_next. | 
 |  */ | 
 |  | 
 | /**** | 
 |  * This function iterates through the items in increasing order, | 
 |  * but prefixed by the special item, then -1. That is: | 
 |  *    special, 1, 2, 3, ..., n, -1. | 
 |  * Each item is listed only once. | 
 |  */ | 
 | static inline BOOL iterator_next(ITERATOR* i) | 
 | { | 
 |     if (i->nItem == -1) | 
 |     { | 
 | 	i->nItem = i->nSpecial; | 
 | 	if (i->nItem != -1) return TRUE; | 
 |     } | 
 |     if (i->nItem == i->nSpecial) | 
 |     { | 
 | 	if (i->ranges) i->index = 0; | 
 | 	goto pickarange; | 
 |     } | 
 |  | 
 |     i->nItem++; | 
 | testitem: | 
 |     if (i->nItem == i->nSpecial) i->nItem++; | 
 |     if (i->nItem < i->range.upper) return TRUE; | 
 |  | 
 | pickarange: | 
 |     if (i->ranges) | 
 |     { | 
 | 	if (i->index < DPA_GetPtrCount(i->ranges->hdpa)) | 
 | 	    i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++); | 
 | 	else goto end; | 
 |     } | 
 |     else if (i->nItem >= i->range.upper) goto end; | 
 |  | 
 |     i->nItem = i->range.lower; | 
 |     if (i->nItem >= 0) goto testitem; | 
 | end: | 
 |     i->nItem = -1; | 
 |     return FALSE; | 
 | } | 
 |  | 
 | /**** | 
 |  * This function iterates through the items in decreasing order, | 
 |  * followed by the special item, then -1. That is: | 
 |  *    n, n-1, ..., 3, 2, 1, special, -1. | 
 |  * Each item is listed only once. | 
 |  */ | 
 | static inline BOOL iterator_prev(ITERATOR* i) | 
 | { | 
 |     BOOL start = FALSE; | 
 |  | 
 |     if (i->nItem == -1) | 
 |     { | 
 | 	start = TRUE; | 
 | 	if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa); | 
 | 	goto pickarange; | 
 |     } | 
 |     if (i->nItem == i->nSpecial) | 
 |     { | 
 | 	i->nItem = -1; | 
 | 	return FALSE; | 
 |     } | 
 |  | 
 | testitem: | 
 |     i->nItem--; | 
 |     if (i->nItem == i->nSpecial) i->nItem--; | 
 |     if (i->nItem >= i->range.lower) return TRUE; | 
 |  | 
 | pickarange: | 
 |     if (i->ranges) | 
 |     { | 
 | 	if (i->index > 0) | 
 | 	    i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index); | 
 | 	else goto end; | 
 |     } | 
 |     else if (!start && i->nItem < i->range.lower) goto end; | 
 |  | 
 |     i->nItem = i->range.upper; | 
 |     if (i->nItem > 0) goto testitem; | 
 | end: | 
 |     return (i->nItem = i->nSpecial) != -1; | 
 | } | 
 |  | 
 | static RANGE iterator_range(ITERATOR* i) | 
 | { | 
 |     RANGE range; | 
 |  | 
 |     if (!i->ranges) return i->range; | 
 |  | 
 |     if (DPA_GetPtrCount(i->ranges->hdpa) > 0) | 
 |     { | 
 |         range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower; | 
 |         range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper; | 
 |     } | 
 |     else range.lower = range.upper = 0; | 
 |  | 
 |     return range; | 
 | } | 
 |  | 
 | /*** | 
 |  * Releases resources associated with this ierator. | 
 |  */ | 
 | static inline void iterator_destroy(ITERATOR* i) | 
 | { | 
 |     ranges_destroy(i->ranges); | 
 | } | 
 |  | 
 | /*** | 
 |  * Create an empty iterator. | 
 |  */ | 
 | static inline BOOL iterator_empty(ITERATOR* i) | 
 | { | 
 |     ZeroMemory(i, sizeof(*i)); | 
 |     i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1; | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /*** | 
 |  * Create an iterator over a range. | 
 |  */ | 
 | static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range) | 
 | { | 
 |     iterator_empty(i); | 
 |     i->range = range; | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /*** | 
 |  * Create an iterator over a bunch of ranges. | 
 |  * Please note that the iterator will take ownership of the ranges, | 
 |  * and will free them upon destruction. | 
 |  */ | 
 | static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges) | 
 | { | 
 |     iterator_empty(i); | 
 |     i->ranges = ranges; | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /*** | 
 |  * Creates an iterator over the items which intersect lprc. | 
 |  */ | 
 | static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT *lprc) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     RECT frame = *lprc, rcItem, rcTemp; | 
 |     POINT Origin; | 
 |      | 
 |     /* in case we fail, we want to return an empty iterator */ | 
 |     if (!iterator_empty(i)) return FALSE; | 
 |  | 
 |     LISTVIEW_GetOrigin(infoPtr, &Origin); | 
 |  | 
 |     TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc)); | 
 |     OffsetRect(&frame, -Origin.x, -Origin.y); | 
 |  | 
 |     if (uView == LVS_ICON || uView == LVS_SMALLICON) | 
 |     { | 
 | 	INT nItem; | 
 | 	 | 
 | 	if (uView == LVS_ICON && infoPtr->nFocusedItem != -1) | 
 | 	{ | 
 | 	    LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem); | 
 | 	    if (IntersectRect(&rcTemp, &rcItem, lprc)) | 
 | 		i->nSpecial = infoPtr->nFocusedItem; | 
 | 	} | 
 | 	if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE; | 
 | 	/* to do better here, we need to have PosX, and PosY sorted */ | 
 | 	TRACE("building icon ranges:\n"); | 
 | 	for (nItem = 0; nItem < infoPtr->nItemCount; nItem++) | 
 | 	{ | 
 |             rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem); | 
 | 	    rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem); | 
 | 	    rcItem.right = rcItem.left + infoPtr->nItemWidth; | 
 | 	    rcItem.bottom = rcItem.top + infoPtr->nItemHeight; | 
 | 	    if (IntersectRect(&rcTemp, &rcItem, &frame)) | 
 | 		ranges_additem(i->ranges, nItem); | 
 | 	} | 
 | 	return TRUE; | 
 |     } | 
 |     else if (uView == LVS_REPORT) | 
 |     { | 
 | 	RANGE range; | 
 | 	 | 
 | 	if (frame.left >= infoPtr->nItemWidth) return TRUE; | 
 | 	if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE; | 
 | 	 | 
 | 	range.lower = max(frame.top / infoPtr->nItemHeight, 0); | 
 | 	range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1; | 
 | 	if (range.upper <= range.lower) return TRUE; | 
 | 	if (!iterator_rangeitems(i, range)) return FALSE; | 
 | 	TRACE("    report=%s\n", debugrange(&i->range)); | 
 |     } | 
 |     else | 
 |     { | 
 | 	INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1); | 
 | 	INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0); | 
 | 	INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1); | 
 | 	INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0); | 
 | 	INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol); | 
 | 	INT lower = nFirstCol * nPerCol + nFirstRow; | 
 | 	RANGE item_range; | 
 | 	INT nCol; | 
 |  | 
 | 	TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n", | 
 | 	      nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower); | 
 | 	 | 
 | 	if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE; | 
 |  | 
 | 	if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE; | 
 | 	TRACE("building list ranges:\n"); | 
 | 	for (nCol = nFirstCol; nCol <= nLastCol; nCol++) | 
 | 	{ | 
 | 	    item_range.lower = nCol * nPerCol + nFirstRow; | 
 | 	    if(item_range.lower >= infoPtr->nItemCount) break; | 
 | 	    item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount); | 
 | 	    TRACE("   list=%s\n", debugrange(&item_range)); | 
 | 	    ranges_add(i->ranges, item_range); | 
 | 	} | 
 |     } | 
 |  | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /*** | 
 |  * Creates an iterator over the items which intersect the visible region of hdc. | 
 |  */ | 
 | static BOOL iterator_visibleitems(ITERATOR *i, LISTVIEW_INFO *infoPtr, HDC  hdc) | 
 | { | 
 |     POINT Origin, Position; | 
 |     RECT rcItem, rcClip; | 
 |     INT rgntype; | 
 |      | 
 |     rgntype = GetClipBox(hdc, &rcClip); | 
 |     if (rgntype == NULLREGION) return iterator_empty(i); | 
 |     if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE; | 
 |     if (rgntype == SIMPLEREGION) return TRUE; | 
 |  | 
 |     /* first deal with the special item */ | 
 |     if (i->nSpecial != -1) | 
 |     { | 
 | 	LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem); | 
 | 	if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1; | 
 |     } | 
 |      | 
 |     /* if we can't deal with the region, we'll just go with the simple range */ | 
 |     LISTVIEW_GetOrigin(infoPtr, &Origin); | 
 |     TRACE("building visible range:\n"); | 
 |     if (!i->ranges && i->range.lower < i->range.upper) | 
 |     { | 
 | 	if (!(i->ranges = ranges_create(50))) return TRUE; | 
 | 	if (!ranges_add(i->ranges, i->range)) | 
 |         { | 
 | 	    ranges_destroy(i->ranges); | 
 | 	    i->ranges = 0; | 
 | 	    return TRUE; | 
 |         } | 
 |     } | 
 |  | 
 |     /* now delete the invisible items from the list */ | 
 |     while(iterator_next(i)) | 
 |     { | 
 | 	LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position); | 
 | 	rcItem.left = Position.x + Origin.x; | 
 | 	rcItem.top = Position.y + Origin.y; | 
 | 	rcItem.right = rcItem.left + infoPtr->nItemWidth; | 
 | 	rcItem.bottom = rcItem.top + infoPtr->nItemHeight; | 
 | 	if (!RectVisible(hdc, &rcItem)) | 
 | 	    ranges_delitem(i->ranges, i->nItem); | 
 |     } | 
 |     /* the iterator should restart on the next iterator_next */ | 
 |     TRACE("done\n"); | 
 |      | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /******** Misc helper functions ************************************/ | 
 |  | 
 | static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg, | 
 | 		                      WPARAM wParam, LPARAM lParam, BOOL isW) | 
 | { | 
 |     if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam); | 
 |     else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam); | 
 | } | 
 |  | 
 | static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |      | 
 |     return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) && | 
 | 	   (uView == LVS_ICON || uView == LVS_SMALLICON); | 
 | } | 
 |  | 
 | /******** Internal API functions ************************************/ | 
 |  | 
 | static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem) | 
 | { | 
 |     static COLUMN_INFO mainItem; | 
 |  | 
 |     if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem; | 
 |     assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns)); | 
 |     return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem); | 
 | } | 
 | 	 | 
 | static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc) | 
 | { | 
 |     *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader; | 
 | } | 
 | 	 | 
 | static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem) | 
 | { | 
 |     return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE); | 
 | } | 
 |  | 
 | /* Listview invalidation functions: use _only_ these functions to invalidate */ | 
 |  | 
 | static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr) | 
 | { | 
 |     return infoPtr->bRedraw; | 
 | } | 
 |  | 
 | static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT* rect) | 
 | { | 
 |     if(!is_redrawing(infoPtr)) return;  | 
 |     TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect)); | 
 |     InvalidateRect(infoPtr->hwndSelf, rect, TRUE); | 
 | } | 
 |  | 
 | static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem) | 
 | { | 
 |     RECT rcBox; | 
 |  | 
 |     if(!is_redrawing(infoPtr)) return;  | 
 |     LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox); | 
 |     LISTVIEW_InvalidateRect(infoPtr, &rcBox); | 
 | } | 
 |  | 
 | static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem) | 
 | { | 
 |     POINT Origin, Position; | 
 |     RECT rcBox; | 
 |      | 
 |     if(!is_redrawing(infoPtr)) return;  | 
 |     assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT); | 
 |     LISTVIEW_GetOrigin(infoPtr, &Origin); | 
 |     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position); | 
 |     LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox); | 
 |     rcBox.top = 0; | 
 |     rcBox.bottom = infoPtr->nItemHeight; | 
 |     OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y); | 
 |     LISTVIEW_InvalidateRect(infoPtr, &rcBox); | 
 | } | 
 |  | 
 | static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr) | 
 | { | 
 |     LISTVIEW_InvalidateRect(infoPtr, NULL); | 
 | } | 
 |  | 
 | static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn) | 
 | { | 
 |     RECT rcCol; | 
 |      | 
 |     if(!is_redrawing(infoPtr)) return;  | 
 |     LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol); | 
 |     rcCol.top = infoPtr->rcList.top; | 
 |     rcCol.bottom = infoPtr->rcList.bottom; | 
 |     LISTVIEW_InvalidateRect(infoPtr, &rcCol); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Retrieves the number of items that can fit vertically in the client area. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * | 
 |  * RETURN: | 
 |  * Number of items per row. | 
 |  */ | 
 | static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr) | 
 | { | 
 |     INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left; | 
 |  | 
 |     return max(nListWidth/infoPtr->nItemWidth, 1); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Retrieves the number of items that can fit horizontally in the client | 
 |  * area. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * | 
 |  * RETURN: | 
 |  * Number of items per column. | 
 |  */ | 
 | static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr) | 
 | { | 
 |     INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top; | 
 |  | 
 |     return max(nListHeight / infoPtr->nItemHeight, 1); | 
 | } | 
 |  | 
 |  | 
 | /************************************************************************* | 
 |  *		LISTVIEW_ProcessLetterKeys | 
 |  * | 
 |  *  Processes keyboard messages generated by pressing the letter keys | 
 |  *  on the keyboard. | 
 |  *  What this does is perform a case insensitive search from the | 
 |  *  current position with the following quirks: | 
 |  *  - If two chars or more are pressed in quick succession we search | 
 |  *    for the corresponding string (e.g. 'abc'). | 
 |  *  - If there is a delay we wipe away the current search string and | 
 |  *    restart with just that char. | 
 |  *  - If the user keeps pressing the same character, whether slowly or | 
 |  *    fast, so that the search string is entirely composed of this | 
 |  *    character ('aaaaa' for instance), then we search for first item | 
 |  *    that starting with that character. | 
 |  *  - If the user types the above character in quick succession, then | 
 |  *    we must also search for the corresponding string ('aaaaa'), and | 
 |  *    go to that string if there is a match. | 
 |  * | 
 |  * PARAMETERS | 
 |  *   [I] hwnd : handle to the window | 
 |  *   [I] charCode : the character code, the actual character | 
 |  *   [I] keyData : key data | 
 |  * | 
 |  * RETURNS | 
 |  * | 
 |  *  Zero. | 
 |  * | 
 |  * BUGS | 
 |  * | 
 |  *  - The current implementation has a list of characters it will | 
 |  *    accept and it ignores averything else. In particular it will | 
 |  *    ignore accentuated characters which seems to match what | 
 |  *    Windows does. But I'm not sure it makes sense to follow | 
 |  *    Windows there. | 
 |  *  - We don't sound a beep when the search fails. | 
 |  * | 
 |  * SEE ALSO | 
 |  * | 
 |  *  TREEVIEW_ProcessLetterKeys | 
 |  */ | 
 | static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData) | 
 | { | 
 |     INT nItem; | 
 |     INT endidx,idx; | 
 |     LVITEMW item; | 
 |     WCHAR buffer[MAX_PATH]; | 
 |     DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp; | 
 |  | 
 |     /* simple parameter checking */ | 
 |     if (!charCode || !keyData) return 0; | 
 |  | 
 |     /* only allow the valid WM_CHARs through */ | 
 |     if (!isalnum(charCode) && | 
 |         charCode != '.' && charCode != '`' && charCode != '!' && | 
 |         charCode != '@' && charCode != '#' && charCode != '$' && | 
 |         charCode != '%' && charCode != '^' && charCode != '&' && | 
 |         charCode != '*' && charCode != '(' && charCode != ')' && | 
 |         charCode != '-' && charCode != '_' && charCode != '+' && | 
 |         charCode != '=' && charCode != '\\'&& charCode != ']' && | 
 |         charCode != '}' && charCode != '[' && charCode != '{' && | 
 |         charCode != '/' && charCode != '?' && charCode != '>' && | 
 |         charCode != '<' && charCode != ',' && charCode != '~') | 
 |         return 0; | 
 |  | 
 |     /* if there's one item or less, there is no where to go */ | 
 |     if (infoPtr->nItemCount <= 1) return 0; | 
 |  | 
 |     /* update the search parameters */ | 
 |     infoPtr->lastKeyPressTimestamp = GetTickCount(); | 
 |     if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) { | 
 |         if (infoPtr->nSearchParamLength < MAX_PATH) | 
 |             infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode; | 
 |         if (infoPtr->charCode != charCode) | 
 |             infoPtr->charCode = charCode = 0; | 
 |     } else { | 
 |         infoPtr->charCode=charCode; | 
 |         infoPtr->szSearchParam[0]=charCode; | 
 |         infoPtr->nSearchParamLength=1; | 
 |         /* Redundant with the 1 char string */ | 
 |         charCode=0; | 
 |     } | 
 |  | 
 |     /* and search from the current position */ | 
 |     nItem=-1; | 
 |     if (infoPtr->nFocusedItem >= 0) { | 
 |         endidx=infoPtr->nFocusedItem; | 
 |         idx=endidx; | 
 |         /* if looking for single character match, | 
 |          * then we must always move forward | 
 |          */ | 
 |         if (infoPtr->nSearchParamLength == 1) | 
 |             idx++; | 
 |     } else { | 
 |         endidx=infoPtr->nItemCount; | 
 |         idx=0; | 
 |     } | 
 |     do { | 
 |         if (idx == infoPtr->nItemCount) { | 
 |             if (endidx == infoPtr->nItemCount || endidx == 0) | 
 |                 break; | 
 |             idx=0; | 
 |         } | 
 |  | 
 |         /* get item */ | 
 |         item.mask = LVIF_TEXT; | 
 |         item.iItem = idx; | 
 |         item.iSubItem = 0; | 
 |         item.pszText = buffer; | 
 |         item.cchTextMax = MAX_PATH; | 
 |         if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0; | 
 |  | 
 |         /* check for a match */ | 
 |         if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) { | 
 |             nItem=idx; | 
 |             break; | 
 |         } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) && | 
 |                     (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) { | 
 |             /* This would work but we must keep looking for a longer match */ | 
 |             nItem=idx; | 
 |         } | 
 |         idx++; | 
 |     } while (idx != endidx); | 
 |  | 
 |     if (nItem != -1) | 
 |         LISTVIEW_KeySelection(infoPtr, nItem); | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | /************************************************************************* | 
 |  * LISTVIEW_UpdateHeaderSize [Internal] | 
 |  * | 
 |  * Function to resize the header control | 
 |  * | 
 |  * PARAMS | 
 |  * [I]  hwnd : handle to a window | 
 |  * [I]  nNewScrollPos : scroll pos to set | 
 |  * | 
 |  * RETURNS | 
 |  * None. | 
 |  */ | 
 | static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos) | 
 | { | 
 |     RECT winRect; | 
 |     POINT point[2]; | 
 |  | 
 |     TRACE("nNewScrollPos=%d\n", nNewScrollPos); | 
 |  | 
 |     GetWindowRect(infoPtr->hwndHeader, &winRect); | 
 |     point[0].x = winRect.left; | 
 |     point[0].y = winRect.top; | 
 |     point[1].x = winRect.right; | 
 |     point[1].y = winRect.bottom; | 
 |  | 
 |     MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2); | 
 |     point[0].x = -nNewScrollPos; | 
 |     point[1].x += nNewScrollPos; | 
 |  | 
 |     SetWindowPos(infoPtr->hwndHeader,0, | 
 |         point[0].x,point[0].y,point[1].x,point[1].y, | 
 |         SWP_NOZORDER | SWP_NOACTIVATE); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Update the scrollbars. This functions should be called whenever | 
 |  * the content, size or view changes. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * | 
 |  * RETURN: | 
 |  * None | 
 |  */ | 
 | static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     SCROLLINFO horzInfo, vertInfo; | 
 |  | 
 |     if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return; | 
 |  | 
 |     ZeroMemory(&horzInfo, sizeof(SCROLLINFO)); | 
 |     horzInfo.cbSize = sizeof(SCROLLINFO); | 
 |     horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left; | 
 |  | 
 |     /* for now, we'll set info.nMax to the _count_, and adjust it later */ | 
 |     if (uView == LVS_LIST) | 
 |     { | 
 | 	INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr); | 
 | 	horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol; | 
 |  | 
 | 	/* scroll by at least one column per page */ | 
 | 	if(horzInfo.nPage < infoPtr->nItemWidth) | 
 | 		horzInfo.nPage = infoPtr->nItemWidth; | 
 |  | 
 | 	horzInfo.nPage /= infoPtr->nItemWidth; | 
 |     } | 
 |     else if (uView == LVS_REPORT) | 
 |     { | 
 | 	horzInfo.nMax = infoPtr->nItemWidth; | 
 |     } | 
 |     else /* LVS_ICON, or LVS_SMALLICON */ | 
 |     { | 
 | 	RECT rcView; | 
 |  | 
 | 	if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left; | 
 |     } | 
 |    | 
 |     horzInfo.fMask = SIF_RANGE | SIF_PAGE; | 
 |     horzInfo.nMax = max(horzInfo.nMax - 1, 0); | 
 |     SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE); | 
 |     TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo)); | 
 |  | 
 |     /* Setting the horizontal scroll can change the listview size | 
 |      * (and potentially everything else) so we need to recompute | 
 |      * everything again for the vertical scroll | 
 |      */ | 
 |  | 
 |     ZeroMemory(&vertInfo, sizeof(SCROLLINFO)); | 
 |     vertInfo.cbSize = sizeof(SCROLLINFO); | 
 |     vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top; | 
 |  | 
 |     if (uView == LVS_REPORT) | 
 |     { | 
 | 	vertInfo.nMax = infoPtr->nItemCount; | 
 | 	 | 
 | 	/* scroll by at least one page */ | 
 | 	if(vertInfo.nPage < infoPtr->nItemHeight) | 
 | 	  vertInfo.nPage = infoPtr->nItemHeight; | 
 |  | 
 | 	vertInfo.nPage /= infoPtr->nItemHeight; | 
 |     } | 
 |     else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */ | 
 |     { | 
 | 	RECT rcView; | 
 |  | 
 | 	if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top; | 
 |     } | 
 |  | 
 |     vertInfo.fMask = SIF_RANGE | SIF_PAGE; | 
 |     vertInfo.nMax = max(vertInfo.nMax - 1, 0); | 
 |     SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE); | 
 |     TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo)); | 
 |  | 
 |     /* Update the Header Control */ | 
 |     if (uView == LVS_REPORT) | 
 |     { | 
 | 	horzInfo.fMask = SIF_POS; | 
 | 	GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo); | 
 | 	LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos); | 
 |     } | 
 | } | 
 |  | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Shows/hides the focus rectangle.  | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] fShow : TRUE to show the focus, FALSE to hide it. | 
 |  * | 
 |  * RETURN: | 
 |  * None | 
 |  */ | 
 | static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     HDC hdc; | 
 |  | 
 |     TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem); | 
 |  | 
 |     if (infoPtr->nFocusedItem < 0) return; | 
 |  | 
 |     /* we need some gymnastics in ICON mode to handle large items */ | 
 |     if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON ) | 
 |     { | 
 | 	RECT rcBox; | 
 |  | 
 | 	LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);  | 
 | 	if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight) | 
 | 	{ | 
 | 	    LISTVIEW_InvalidateRect(infoPtr, &rcBox); | 
 | 	    return; | 
 | 	} | 
 |     } | 
 |  | 
 |     if (!(hdc = GetDC(infoPtr->hwndSelf))) return; | 
 |  | 
 |     /* for some reason, owner draw should work only in report mode */ | 
 |     if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT)) | 
 |     { | 
 | 	DRAWITEMSTRUCT dis; | 
 | 	LVITEMW item; | 
 |  | 
 |         item.iItem = infoPtr->nFocusedItem; | 
 | 	item.iSubItem = 0; | 
 |         item.mask = LVIF_PARAM; | 
 | 	if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done; | 
 | 	    | 
 | 	ZeroMemory(&dis, sizeof(dis));  | 
 | 	dis.CtlType = ODT_LISTVIEW; | 
 | 	dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID); | 
 | 	dis.itemID = item.iItem; | 
 | 	dis.itemAction = ODA_FOCUS; | 
 | 	if (fShow) dis.itemState |= ODS_FOCUS; | 
 | 	dis.hwndItem = infoPtr->hwndSelf; | 
 | 	dis.hDC = hdc; | 
 | 	LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem); | 
 | 	dis.itemData = item.lParam; | 
 |  | 
 | 	SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis); | 
 |     } | 
 |     else | 
 |     { | 
 | 	DrawFocusRect(hdc, &infoPtr->rcFocus); | 
 |     } | 
 | done: | 
 |     ReleaseDC(infoPtr->hwndSelf, hdc); | 
 | } | 
 |  | 
 | /*** | 
 |  * Invalidates all visible selected items. | 
 |  */ | 
 | static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr) | 
 | { | 
 |     ITERATOR i;  | 
 |     | 
 |     iterator_frameditems(&i, infoPtr, &infoPtr->rcList);  | 
 |     while(iterator_next(&i)) | 
 |     { | 
 | 	if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED)) | 
 | 	    LISTVIEW_InvalidateItem(infoPtr, i.nItem); | 
 |     } | 
 |     iterator_destroy(&i); | 
 | } | 
 |  | 
 | 	     | 
 | /*** | 
 |  * DESCRIPTION:            [INTERNAL] | 
 |  * Computes an item's (left,top) corner, relative to rcView. | 
 |  * That is, the position has NOT been made relative to the Origin. | 
 |  * This is deliberate, to avoid computing the Origin over, and | 
 |  * over again, when this function is call in a loop. Instead, | 
 |  * one ca factor the computation of the Origin before the loop, | 
 |  * and offset the value retured by this function, on every iteration. | 
 |  *  | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nItem  : item number | 
 |  * [O] lpptOrig : item top, left corner | 
 |  * | 
 |  * RETURN: | 
 |  *   None. | 
 |  */ | 
 | static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |  | 
 |     assert(nItem >= 0 && nItem < infoPtr->nItemCount); | 
 |  | 
 |     if ((uView == LVS_SMALLICON) || (uView == LVS_ICON)) | 
 |     { | 
 | 	lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem); | 
 | 	lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem); | 
 |     } | 
 |     else if (uView == LVS_LIST) | 
 |     { | 
 |         INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr); | 
 | 	lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth; | 
 | 	lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight; | 
 |     } | 
 |     else /* LVS_REPORT */ | 
 |     { | 
 | 	lpptPosition->x = 0; | 
 | 	lpptPosition->y = nItem * infoPtr->nItemHeight; | 
 |     } | 
 | } | 
 |      | 
 | /*** | 
 |  * DESCRIPTION:            [INTERNAL] | 
 |  * Compute the rectangles of an item.  This is to localize all | 
 |  * the computations in one place. If you are not interested in some | 
 |  * of these values, simply pass in a NULL -- the fucntion is smart | 
 |  * enough to compute only what's necessary. The function computes | 
 |  * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard | 
 |  * one, the BOX rectangle. This rectangle is very cheap to compute, | 
 |  * and is guaranteed to contain all the other rectangles. Computing | 
 |  * the ICON rect is also cheap, but all the others are potentaily | 
 |  * expensive. This gives an easy and effective optimization when | 
 |  * searching (like point inclusion, or rectangle intersection): | 
 |  * first test against the BOX, and if TRUE, test agains the desired | 
 |  * rectangle. | 
 |  * If the function does not have all the necessary information | 
 |  * to computed the requested rectangles, will crash with a | 
 |  * failed assertion. This is done so we catch all programming | 
 |  * errors, given that the function is called only from our code. | 
 |  * | 
 |  * We have the following 'special' meanings for a few fields: | 
 |  *   * If LVIS_FOCUSED is set, we assume the item has the focus | 
 |  *     This is important in ICON mode, where it might get a larger | 
 |  *     then usual rectange | 
 |  * | 
 |  * Please note that subitem support works only in REPORT mode. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] lpLVItem : item to compute the measures for | 
 |  * [O] lprcBox : ptr to Box rectangle | 
 |  *                The internal LVIR_BOX rectangle | 
 |  * [0] lprcState : ptr to State icon rectangle | 
 |  *  		  The internal LVIR_STATE rectangle | 
 |  * [O] lprcIcon : ptr to Icon rectangle | 
 |  *                Same as LVM_GETITEMRECT with LVIR_ICON | 
 |  * [O] lprcLabel : ptr to Label rectangle | 
 |  *                Same as LVM_GETITEMRECT with LVIR_LABEL | 
 |  * | 
 |  * RETURN: | 
 |  *   None. | 
 |  */ | 
 | static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, | 
 | 				    LPRECT lprcBox, LPRECT lprcState,  | 
 | 				    LPRECT lprcIcon, LPRECT lprcLabel) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE; | 
 |     RECT Box, State, Icon, Label; | 
 |     COLUMN_INFO *lpColumnInfo = NULL; | 
 |  | 
 |     TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE)); | 
 | 	 | 
 |     /* Be smart and try to figure out the minimum we have to do */ | 
 |     if (lpLVItem->iSubItem) assert(uView == LVS_REPORT); | 
 |     if (uView == LVS_ICON && (lprcBox || lprcLabel)) | 
 |     { | 
 | 	assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED)); | 
 | 	if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE; | 
 |     } | 
 |     if (lprcLabel) doLabel = TRUE; | 
 |     if (doLabel || lprcIcon) doIcon = TRUE; | 
 |     if (doIcon || lprcState) doState = TRUE; | 
 |      | 
 |     /************************************************************/ | 
 |     /* compute the box rectangle (it should be cheap to do)     */ | 
 |     /************************************************************/ | 
 |     if (lpLVItem->iSubItem || uView == LVS_REPORT) | 
 | 	lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem); | 
 |  | 
 |     if (lpLVItem->iSubItem)     | 
 |     { | 
 | 	Box = lpColumnInfo->rcHeader; | 
 |     } | 
 |     else | 
 |     { | 
 | 	Box.left = 0; | 
 | 	Box.right = infoPtr->nItemWidth; | 
 |     } | 
 |     Box.top = 0; | 
 |     Box.bottom = infoPtr->nItemHeight; | 
 | 		 | 
 |     /************************************************************/ | 
 |     /* compute STATEICON bounding box                           */ | 
 |     /************************************************************/ | 
 |     if (doState) | 
 |     { | 
 | 	if (uView == LVS_ICON) | 
 | 	{ | 
 |      	    State.left = Box.left - infoPtr->iconStateSize.cx - 2; | 
 | 	    if (infoPtr->himlNormal)  | 
 | 		State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2; | 
 | 	    State.top  = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4; | 
 | 	} | 
 | 	else | 
 | 	{ | 
 | 	    /* we need the ident in report mode, if we don't have it, we fail */ | 
 | 	    State.left = Box.left; | 
 | 	    if (uView == LVS_REPORT)  | 
 | 	    { | 
 | 		if (lpLVItem->iSubItem == 0) | 
 | 		{ | 
 | 		    State.left += REPORT_MARGINX; | 
 | 		    assert(lpLVItem->mask & LVIF_INDENT); | 
 | 		    State.left += infoPtr->iconSize.cx * lpLVItem->iIndent; | 
 | 		} | 
 | 	    } | 
 | 	    State.top  = Box.top; | 
 | 	}	 | 
 | 	State.right    = State.left; | 
 | 	State.bottom   = State.top; | 
 | 	if (infoPtr->himlState && lpLVItem->iSubItem == 0) | 
 | 	{ | 
 | 	    State.right  += infoPtr->iconStateSize.cx; | 
 | 	    State.bottom += infoPtr->iconStateSize.cy; | 
 | 	} | 
 | 	if (lprcState) *lprcState = State; | 
 | 	TRACE("    - state=%s\n", wine_dbgstr_rect(&State)); | 
 |     } | 
 |     else  State.right = 0; | 
 |  | 
 |     /************************************************************/ | 
 |     /* compute ICON bounding box (ala LVM_GETITEMRECT)          */ | 
 |     /************************************************************/ | 
 |     if (doIcon) | 
 |     { | 
 | 	if (uView == LVS_ICON) | 
 | 	{ | 
 | 	    Icon.left   = Box.left; | 
 | 	    if (infoPtr->himlNormal)  | 
 | 		Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2; | 
 | 	    Icon.top    = Box.top + ICON_TOP_PADDING; | 
 | 	    Icon.right  = Icon.left; | 
 | 	    Icon.bottom = Icon.top; | 
 | 	    if (infoPtr->himlNormal) | 
 | 	    { | 
 | 		Icon.right  += infoPtr->iconSize.cx; | 
 | 		Icon.bottom += infoPtr->iconSize.cy; | 
 | 	    } | 
 | 	} | 
 | 	else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */ | 
 | 	{ | 
 | 	    Icon.left   = State.right; | 
 | 	    Icon.top    = Box.top; | 
 | 	    Icon.right  = Icon.left; | 
 | 	    if (infoPtr->himlSmall && | 
 |                 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) || | 
 |                  ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK))) | 
 | 		Icon.right += infoPtr->iconSize.cx; | 
 | 	    Icon.bottom = Icon.top + infoPtr->nItemHeight; | 
 | 	} | 
 | 	if(lprcIcon) *lprcIcon = Icon; | 
 | 	TRACE("    - icon=%s\n", wine_dbgstr_rect(&Icon)); | 
 |      } | 
 |      else Icon.right = 0; | 
 |  | 
 |     /************************************************************/ | 
 |     /* compute LABEL bounding box (ala LVM_GETITEMRECT)         */ | 
 |     /************************************************************/ | 
 |     if (doLabel) | 
 |     { | 
 | 	SIZE labelSize = { 0, 0 }; | 
 |  | 
 | 	/* calculate how far to the right can the label strech */ | 
 | 	Label.right = Box.right; | 
 | 	if (uView == LVS_REPORT) | 
 | 	{ | 
 | 	    if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader; | 
 | 	} | 
 |  | 
 | 	if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT)) | 
 | 	{ | 
 | 	   labelSize.cx = infoPtr->nItemWidth; | 
 | 	   labelSize.cy = infoPtr->nItemHeight; | 
 | 	   goto calc_label; | 
 | 	} | 
 | 	 | 
 | 	/* we need the text in non owner draw mode */ | 
 | 	assert(lpLVItem->mask & LVIF_TEXT); | 
 | 	if (is_textT(lpLVItem->pszText, TRUE)) | 
 |         { | 
 |     	    HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont; | 
 |     	    HDC hdc = GetDC(infoPtr->hwndSelf); | 
 |     	    HFONT hOldFont = SelectObject(hdc, hFont); | 
 | 	    UINT uFormat; | 
 | 	    RECT rcText; | 
 |  | 
 | 	    /* compute rough rectangle where the label will go */ | 
 | 	    SetRectEmpty(&rcText); | 
 | 	    rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING; | 
 | 	    rcText.bottom = infoPtr->nItemHeight; | 
 | 	    if (uView == LVS_ICON)  | 
 | 		rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING; | 
 |  | 
 | 	    /* now figure out the flags */ | 
 | 	    if (uView == LVS_ICON) | 
 | 		uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS; | 
 | 	    else | 
 | 		uFormat = LV_SL_DT_FLAGS; | 
 | 	     | 
 |     	    DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT); | 
 |  | 
 | 	    labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth); | 
 | 	    labelSize.cy = rcText.bottom - rcText.top; | 
 |  | 
 |     	    SelectObject(hdc, hOldFont); | 
 |     	    ReleaseDC(infoPtr->hwndSelf, hdc); | 
 | 	} | 
 |  | 
 | calc_label: | 
 | 	if (uView == LVS_ICON) | 
 | 	{ | 
 | 	    Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2; | 
 | 	    Label.top  = Box.top + ICON_TOP_PADDING_HITABLE + | 
 | 		         infoPtr->iconSize.cy + ICON_BOTTOM_PADDING; | 
 | 	    Label.right = Label.left + labelSize.cx; | 
 | 	    Label.bottom = Label.top + infoPtr->nItemHeight; | 
 | 	    if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight) | 
 | 	    { | 
 | 		labelSize.cy = min(Box.bottom - Label.top, labelSize.cy); | 
 | 		labelSize.cy /= infoPtr->ntmHeight; | 
 | 		labelSize.cy = max(labelSize.cy, 1); | 
 | 		labelSize.cy *= infoPtr->ntmHeight; | 
 | 	     } | 
 | 	     Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING; | 
 | 	} | 
 | 	else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */ | 
 | 	{ | 
 | 	    Label.left = Icon.right; | 
 | 	    Label.top = Box.top; | 
 | 	    Label.right = min(Label.left + labelSize.cx, Label.right); | 
 | 	    Label.bottom = Label.top + infoPtr->nItemHeight; | 
 | 	} | 
 |    | 
 | 	if (lprcLabel) *lprcLabel = Label; | 
 | 	TRACE("    - label=%s\n", wine_dbgstr_rect(&Label)); | 
 |     } | 
 |  | 
 |     /* Fix the Box if necessary */ | 
 |     if (lprcBox) | 
 |     { | 
 | 	if (oversizedBox) UnionRect(lprcBox, &Box, &Label); | 
 | 	else *lprcBox = Box; | 
 |     } | 
 |     TRACE("    - box=%s\n", wine_dbgstr_rect(&Box)); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION:            [INTERNAL] | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nItem : item number | 
 |  * [O] lprcBox : ptr to Box rectangle | 
 |  * | 
 |  * RETURN: | 
 |  *   None. | 
 |  */ | 
 | static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' }; | 
 |     POINT Position, Origin; | 
 |     LVITEMW lvItem; | 
 |  | 
 |     LISTVIEW_GetOrigin(infoPtr, &Origin); | 
 |     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position); | 
 |  | 
 |     /* Be smart and try to figure out the minimum we have to do */ | 
 |     lvItem.mask = 0; | 
 |     if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED)) | 
 | 	lvItem.mask |= LVIF_TEXT; | 
 |     lvItem.iItem = nItem; | 
 |     lvItem.iSubItem = 0; | 
 |     lvItem.pszText = szDispText; | 
 |     lvItem.cchTextMax = DISP_TEXT_SIZE; | 
 |     if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem); | 
 |     if (uView == LVS_ICON) | 
 |     { | 
 | 	lvItem.mask |= LVIF_STATE; | 
 | 	lvItem.stateMask = LVIS_FOCUSED; | 
 | 	lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0); | 
 |     } | 
 |     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0); | 
 |  | 
 |     OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y); | 
 | } | 
 |  | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Returns the current icon position, and advances it along the top. | 
 |  * The returned position is not offset by Origin. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [O] lpPos : will get the current icon position | 
 |  * | 
 |  * RETURN: | 
 |  * None | 
 |  */ | 
 | static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos) | 
 | { | 
 |     INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left; | 
 |      | 
 |     *lpPos = infoPtr->currIconPos; | 
 |      | 
 |     infoPtr->currIconPos.x += infoPtr->nItemWidth; | 
 |     if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return; | 
 |  | 
 |     infoPtr->currIconPos.x  = 0; | 
 |     infoPtr->currIconPos.y += infoPtr->nItemHeight; | 
 | } | 
 |  | 
 |      | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Returns the current icon position, and advances it down the left edge. | 
 |  * The returned position is not offset by Origin. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [O] lpPos : will get the current icon position | 
 |  * | 
 |  * RETURN: | 
 |  * None | 
 |  */ | 
 | static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos) | 
 | { | 
 |     INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top; | 
 |      | 
 |     *lpPos = infoPtr->currIconPos; | 
 |      | 
 |     infoPtr->currIconPos.y += infoPtr->nItemHeight; | 
 |     if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return; | 
 |  | 
 |     infoPtr->currIconPos.x += infoPtr->nItemWidth; | 
 |     infoPtr->currIconPos.y  = 0; | 
 | } | 
 |  | 
 |      | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Moves an icon to the specified position. | 
 |  * It takes care of invalidating the item, etc. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nItem : the item to move | 
 |  * [I] lpPos : the new icon position | 
 |  * [I] isNew : flags the item as being new | 
 |  * | 
 |  * RETURN: | 
 |  *   Success: TRUE | 
 |  *   Failure: FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew) | 
 | { | 
 |     POINT old; | 
 |      | 
 |     if (!isNew) | 
 |     {  | 
 |         old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem); | 
 |         old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem); | 
 |      | 
 |         if (lppt->x == old.x && lppt->y == old.y) return TRUE; | 
 | 	LISTVIEW_InvalidateItem(infoPtr, nItem); | 
 |     } | 
 |  | 
 |     /* Allocating a POINTER for every item is too resource intensive, | 
 |      * so we'll keep the (x,y) in different arrays */ | 
 |     if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE; | 
 |     if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE; | 
 |  | 
 |     LISTVIEW_InvalidateItem(infoPtr, nItem); | 
 |  | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Arranges listview items in icon display mode. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nAlignCode : alignment code | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     void (*next_pos)(LISTVIEW_INFO *, LPPOINT); | 
 |     POINT pos; | 
 |     INT i; | 
 |  | 
 |     if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE; | 
 |    | 
 |     TRACE("nAlignCode=%d\n", nAlignCode); | 
 |  | 
 |     if (nAlignCode == LVA_DEFAULT) | 
 |     { | 
 | 	if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT; | 
 |         else nAlignCode = LVA_ALIGNTOP; | 
 |     } | 
 |     | 
 |     switch (nAlignCode) | 
 |     { | 
 |     case LVA_ALIGNLEFT:  next_pos = LISTVIEW_NextIconPosLeft; break; | 
 |     case LVA_ALIGNTOP:   next_pos = LISTVIEW_NextIconPosTop;  break; | 
 |     case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop;  break; /* FIXME */ | 
 |     default: return FALSE; | 
 |     } | 
 |      | 
 |     infoPtr->bAutoarrange = TRUE; | 
 |     infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0; | 
 |     for (i = 0; i < infoPtr->nItemCount; i++) | 
 |     { | 
 | 	next_pos(infoPtr, &pos); | 
 | 	LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE); | 
 |     } | 
 |  | 
 |     return TRUE; | 
 | } | 
 |    | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Retrieves the bounding rectangle of all the items, not offset by Origin. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [O] lprcView : bounding rectangle | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView) | 
 | { | 
 |     INT i, x, y; | 
 |  | 
 |     SetRectEmpty(lprcView); | 
 |  | 
 |     switch (infoPtr->dwStyle & LVS_TYPEMASK) | 
 |     { | 
 |     case LVS_ICON: | 
 |     case LVS_SMALLICON: | 
 | 	for (i = 0; i < infoPtr->nItemCount; i++) | 
 | 	{ | 
 | 	    x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i); | 
 |             y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i); | 
 | 	    lprcView->right = max(lprcView->right, x); | 
 | 	    lprcView->bottom = max(lprcView->bottom, y); | 
 | 	} | 
 | 	if (infoPtr->nItemCount > 0) | 
 | 	{ | 
 | 	    lprcView->right += infoPtr->nItemWidth; | 
 | 	    lprcView->bottom += infoPtr->nItemHeight; | 
 | 	} | 
 | 	break; | 
 |  | 
 |     case LVS_LIST: | 
 | 	y = LISTVIEW_GetCountPerColumn(infoPtr); | 
 | 	x = infoPtr->nItemCount / y; | 
 | 	if (infoPtr->nItemCount % y) x++; | 
 | 	lprcView->right = x * infoPtr->nItemWidth; | 
 | 	lprcView->bottom = y * infoPtr->nItemHeight; | 
 | 	break; | 
 | 	 | 
 |     case LVS_REPORT:	     | 
 |     	lprcView->right = infoPtr->nItemWidth; | 
 | 	lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight; | 
 | 	break; | 
 |     } | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Retrieves the bounding rectangle of all the items. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [O] lprcView : bounding rectangle | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView) | 
 | { | 
 |     POINT ptOrigin; | 
 |  | 
 |     TRACE("(lprcView=%p)\n", lprcView); | 
 |  | 
 |     if (!lprcView) return FALSE; | 
 |   | 
 |     LISTVIEW_GetOrigin(infoPtr, &ptOrigin); | 
 |     LISTVIEW_GetAreaRect(infoPtr, lprcView);  | 
 |     OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);  | 
 |  | 
 |     TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView)); | 
 |  | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Retrieves the subitem pointer associated with the subitem index. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] hdpaSubItems : DPA handle for a specific item | 
 |  * [I] nSubItem : index of subitem | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : subitem pointer | 
 |  *   FAILURE : NULL | 
 |  */ | 
 | static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem) | 
 | { | 
 |     SUBITEM_INFO *lpSubItem; | 
 |     INT i; | 
 |  | 
 |     /* we should binary search here if need be */ | 
 |     for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++) | 
 |     { | 
 | 	lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i); | 
 | 	if (lpSubItem->iSubItem == nSubItem) | 
 | 	    return lpSubItem; | 
 |     } | 
 |  | 
 |     return NULL; | 
 | } | 
 |  | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Caclulates the desired item width. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * | 
 |  * RETURN: | 
 |  *  The desired item width. | 
 |  */ | 
 | static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     INT nItemWidth = 0; | 
 |  | 
 |     TRACE("uView=%d\n", uView); | 
 |  | 
 |     if (uView == LVS_ICON) | 
 | 	nItemWidth = infoPtr->iconSpacing.cx; | 
 |     else if (uView == LVS_REPORT) | 
 |     { | 
 | 	RECT rcHeader; | 
 |  | 
 | 	if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0) | 
 | 	{ | 
 | 	    LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader); | 
 |             nItemWidth = rcHeader.right; | 
 | 	} | 
 |     } | 
 |     else /* LVS_SMALLICON, or LVS_LIST */ | 
 |     { | 
 | 	INT i; | 
 | 	 | 
 | 	for (i = 0; i < infoPtr->nItemCount; i++) | 
 | 	    nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth); | 
 |  | 
 |         if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;  | 
 |         if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx; | 
 |  | 
 | 	nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING); | 
 |     } | 
 |  | 
 |     return max(nItemWidth, 1); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Caclulates the desired item height. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * | 
 |  * RETURN: | 
 |  *  The desired item height. | 
 |  */ | 
 | static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     INT nItemHeight; | 
 |  | 
 |     TRACE("uView=%d\n", uView); | 
 |  | 
 |     if (uView == LVS_ICON) | 
 | 	nItemHeight = infoPtr->iconSpacing.cy; | 
 |     else | 
 |     { | 
 | 	nItemHeight = infoPtr->ntmHeight;  | 
 | 	if (infoPtr->himlState) | 
 | 	    nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy); | 
 | 	if (infoPtr->himlSmall) | 
 | 	    nItemHeight = max(nItemHeight, infoPtr->iconSize.cy); | 
 | 	if (infoPtr->himlState || infoPtr->himlSmall) | 
 | 	    nItemHeight += HEIGHT_PADDING; | 
 |     if (infoPtr->nMeasureItemHeight > 0) | 
 |         nItemHeight = infoPtr->nMeasureItemHeight; | 
 |     } | 
 |  | 
 |     return max(nItemHeight, 1); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Updates the width, and height of an item. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * | 
 |  * RETURN: | 
 |  *  None. | 
 |  */ | 
 | static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr) | 
 | { | 
 |     infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr); | 
 |     infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr); | 
 | } | 
 |  | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Retrieves and saves important text metrics info for the current | 
 |  * Listview font. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * | 
 |  */ | 
 | static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr) | 
 | { | 
 |     HDC hdc = GetDC(infoPtr->hwndSelf); | 
 |     HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont; | 
 |     HFONT hOldFont = SelectObject(hdc, hFont); | 
 |     TEXTMETRICW tm; | 
 |     SIZE sz; | 
 |  | 
 |     if (GetTextMetricsW(hdc, &tm)) | 
 |     { | 
 | 	infoPtr->ntmHeight = tm.tmHeight; | 
 | 	infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth; | 
 |     } | 
 |  | 
 |     if (GetTextExtentPoint32A(hdc, "...", 3, &sz)) | 
 | 	infoPtr->nEllipsisWidth = sz.cx; | 
 | 	 | 
 |     SelectObject(hdc, hOldFont); | 
 |     ReleaseDC(infoPtr->hwndSelf, hdc); | 
 |      | 
 |     TRACE("tmHeight=%d\n", infoPtr->ntmHeight); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * A compare function for ranges | 
 |  * | 
 |  * PARAMETER(S) | 
 |  * [I] range1 : pointer to range 1; | 
 |  * [I] range2 : pointer to range 2; | 
 |  * [I] flags : flags | 
 |  * | 
 |  * RETURNS: | 
 |  * > 0 : if range 1 > range 2 | 
 |  * < 0 : if range 2 > range 1 | 
 |  * = 0 : if range intersects range 2 | 
 |  */ | 
 | static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags) | 
 | { | 
 |     INT cmp; | 
 |      | 
 |     if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)  | 
 | 	cmp = -1; | 
 |     else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)  | 
 | 	cmp = 1; | 
 |     else  | 
 | 	cmp = 0; | 
 |      | 
 |     TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp); | 
 |  | 
 |     return cmp; | 
 | } | 
 |  | 
 | #if DEBUG_RANGES | 
 | #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__) | 
 | #else | 
 | #define ranges_check(ranges, desc) do { } while(0) | 
 | #endif | 
 |  | 
 | static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line) | 
 | { | 
 |     INT i; | 
 |     RANGE *prev, *curr; | 
 |      | 
 |     TRACE("*** Checking %s:%d:%s ***\n", func, line, desc); | 
 |     assert (ranges); | 
 |     assert (DPA_GetPtrCount(ranges->hdpa) >= 0); | 
 |     ranges_dump(ranges); | 
 |     prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0); | 
 |     if (DPA_GetPtrCount(ranges->hdpa) > 0) | 
 | 	assert (prev->lower >= 0 && prev->lower < prev->upper); | 
 |     for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++) | 
 |     { | 
 | 	curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i); | 
 | 	assert (prev->upper <= curr->lower); | 
 | 	assert (curr->lower < curr->upper); | 
 | 	prev = curr; | 
 |     } | 
 |     TRACE("--- Done checking---\n"); | 
 | } | 
 |  | 
 | static RANGES ranges_create(int count) | 
 | { | 
 |     RANGES ranges = (RANGES)Alloc(sizeof(struct tagRANGES)); | 
 |     if (!ranges) return NULL; | 
 |     ranges->hdpa = DPA_Create(count); | 
 |     if (ranges->hdpa) return ranges; | 
 |     Free(ranges); | 
 |     return NULL; | 
 | } | 
 |  | 
 | static void ranges_clear(RANGES ranges) | 
 | { | 
 |     INT i; | 
 | 	 | 
 |     for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++) | 
 | 	Free(DPA_GetPtr(ranges->hdpa, i)); | 
 |     DPA_DeleteAllPtrs(ranges->hdpa); | 
 | } | 
 |  | 
 |  | 
 | static void ranges_destroy(RANGES ranges) | 
 | { | 
 |     if (!ranges) return; | 
 |     ranges_clear(ranges); | 
 |     DPA_Destroy(ranges->hdpa); | 
 |     Free(ranges); | 
 | } | 
 |  | 
 | static RANGES ranges_clone(RANGES ranges) | 
 | { | 
 |     RANGES clone; | 
 |     INT i; | 
 | 	    | 
 |     if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail; | 
 |  | 
 |     for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++) | 
 |     { | 
 |         RANGE *newrng = (RANGE *)Alloc(sizeof(RANGE)); | 
 | 	if (!newrng) goto fail; | 
 | 	*newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i)); | 
 | 	DPA_SetPtr(clone->hdpa, i, newrng); | 
 |     } | 
 |     return clone; | 
 |      | 
 | fail: | 
 |     TRACE ("clone failed\n"); | 
 |     ranges_destroy(clone); | 
 |     return NULL; | 
 | } | 
 |  | 
 | static RANGES ranges_diff(RANGES ranges, RANGES sub) | 
 | { | 
 |     INT i; | 
 |  | 
 |     for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++) | 
 | 	ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i))); | 
 |  | 
 |     return ranges; | 
 | } | 
 |  | 
 | static void ranges_dump(RANGES ranges) | 
 | { | 
 |     INT i; | 
 |  | 
 |     for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++) | 
 |     	TRACE("   %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i))); | 
 | } | 
 |  | 
 | static inline BOOL ranges_contain(RANGES ranges, INT nItem) | 
 | { | 
 |     RANGE srchrng = { nItem, nItem + 1 }; | 
 |  | 
 |     TRACE("(nItem=%d)\n", nItem); | 
 |     ranges_check(ranges, "before contain"); | 
 |     return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1; | 
 | } | 
 |  | 
 | static INT ranges_itemcount(RANGES ranges) | 
 | { | 
 |     INT i, count = 0; | 
 |      | 
 |     for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++) | 
 |     { | 
 | 	RANGE *sel = DPA_GetPtr(ranges->hdpa, i); | 
 | 	count += sel->upper - sel->lower; | 
 |     } | 
 |  | 
 |     return count; | 
 | } | 
 |  | 
 | static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper) | 
 | { | 
 |     RANGE srchrng = { nItem, nItem + 1 }, *chkrng; | 
 |     INT index; | 
 |  | 
 |     index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER); | 
 |     if (index == -1) return TRUE; | 
 |  | 
 |     for (; index < DPA_GetPtrCount(ranges->hdpa); index++) | 
 |     { | 
 | 	chkrng = DPA_GetPtr(ranges->hdpa, index); | 
 |     	if (chkrng->lower >= nItem) | 
 | 	    chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0); | 
 |         if (chkrng->upper > nItem) | 
 | 	    chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0); | 
 |     } | 
 |     return TRUE; | 
 | } | 
 |  | 
 | static BOOL ranges_add(RANGES ranges, RANGE range) | 
 | { | 
 |     RANGE srchrgn; | 
 |     INT index; | 
 |  | 
 |     TRACE("(%s)\n", debugrange(&range)); | 
 |     ranges_check(ranges, "before add"); | 
 |  | 
 |     /* try find overlapping regions first */ | 
 |     srchrgn.lower = range.lower - 1; | 
 |     srchrgn.upper = range.upper + 1; | 
 |     index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED); | 
 |     | 
 |     if (index == -1) | 
 |     { | 
 | 	RANGE *newrgn; | 
 |  | 
 | 	TRACE("Adding new range\n"); | 
 |  | 
 | 	/* create the brand new range to insert */	 | 
 |         newrgn = (RANGE *)Alloc(sizeof(RANGE)); | 
 | 	if(!newrgn) goto fail; | 
 | 	*newrgn = range; | 
 | 	 | 
 | 	/* figure out where to insert it */ | 
 | 	index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER); | 
 | 	TRACE("index=%d\n", index); | 
 | 	if (index == -1) index = 0; | 
 | 	 | 
 | 	/* and get it over with */ | 
 | 	if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1) | 
 | 	{ | 
 | 	    Free(newrgn); | 
 | 	    goto fail; | 
 | 	} | 
 |     } | 
 |     else | 
 |     { | 
 | 	RANGE *chkrgn, *mrgrgn; | 
 | 	INT fromindex, mergeindex; | 
 |  | 
 | 	chkrgn = DPA_GetPtr(ranges->hdpa, index); | 
 | 	TRACE("Merge with %s @%d\n", debugrange(chkrgn), index); | 
 |  | 
 | 	chkrgn->lower = min(range.lower, chkrgn->lower); | 
 | 	chkrgn->upper = max(range.upper, chkrgn->upper); | 
 | 	 | 
 | 	TRACE("New range %s @%d\n", debugrange(chkrgn), index); | 
 |  | 
 |         /* merge now common anges */ | 
 | 	fromindex = 0; | 
 | 	srchrgn.lower = chkrgn->lower - 1; | 
 | 	srchrgn.upper = chkrgn->upper + 1; | 
 | 	     | 
 | 	do | 
 | 	{ | 
 | 	    mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0); | 
 | 	    if (mergeindex == -1) break; | 
 | 	    if (mergeindex == index)  | 
 | 	    { | 
 | 		fromindex = index + 1; | 
 | 		continue; | 
 | 	    } | 
 | 	   | 
 | 	    TRACE("Merge with index %i\n", mergeindex); | 
 | 	     | 
 | 	    mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex); | 
 | 	    chkrgn->lower = min(chkrgn->lower, mrgrgn->lower); | 
 | 	    chkrgn->upper = max(chkrgn->upper, mrgrgn->upper); | 
 | 	    Free(mrgrgn); | 
 | 	    DPA_DeletePtr(ranges->hdpa, mergeindex); | 
 | 	    if (mergeindex < index) index --; | 
 | 	} while(1); | 
 |     } | 
 |  | 
 |     ranges_check(ranges, "after add"); | 
 |     return TRUE; | 
 |      | 
 | fail: | 
 |     ranges_check(ranges, "failed add"); | 
 |     return FALSE; | 
 | } | 
 |  | 
 | static BOOL ranges_del(RANGES ranges, RANGE range) | 
 | { | 
 |     RANGE *chkrgn; | 
 |     INT index; | 
 |  | 
 |     TRACE("(%s)\n", debugrange(&range)); | 
 |     ranges_check(ranges, "before del"); | 
 |      | 
 |     /* we don't use DPAS_SORTED here, since we need * | 
 |      * to find the first overlapping range          */ | 
 |     index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0); | 
 |     while(index != -1)  | 
 |     { | 
 | 	chkrgn = DPA_GetPtr(ranges->hdpa, index); | 
 | 	 | 
 |         TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);  | 
 |  | 
 | 	/* case 1: Same range */ | 
 | 	if ( (chkrgn->upper == range.upper) && | 
 | 	     (chkrgn->lower == range.lower) ) | 
 | 	{ | 
 | 	    DPA_DeletePtr(ranges->hdpa, index); | 
 | 	    break; | 
 | 	} | 
 | 	/* case 2: engulf */ | 
 | 	else if ( (chkrgn->upper <= range.upper) && | 
 | 		  (chkrgn->lower >= range.lower) )  | 
 | 	{ | 
 | 	    DPA_DeletePtr(ranges->hdpa, index); | 
 | 	} | 
 | 	/* case 3: overlap upper */ | 
 | 	else if ( (chkrgn->upper <= range.upper) && | 
 | 		  (chkrgn->lower < range.lower) ) | 
 | 	{ | 
 | 	    chkrgn->upper = range.lower; | 
 | 	} | 
 | 	/* case 4: overlap lower */ | 
 | 	else if ( (chkrgn->upper > range.upper) && | 
 | 		  (chkrgn->lower >= range.lower) ) | 
 | 	{ | 
 | 	    chkrgn->lower = range.upper; | 
 | 	    break; | 
 | 	} | 
 | 	/* case 5: fully internal */ | 
 | 	else | 
 | 	{ | 
 | 	    RANGE tmprgn = *chkrgn, *newrgn; | 
 |  | 
 | 	    if (!(newrgn = (RANGE *)Alloc(sizeof(RANGE)))) goto fail; | 
 | 	    newrgn->lower = chkrgn->lower; | 
 | 	    newrgn->upper = range.lower; | 
 | 	    chkrgn->lower = range.upper; | 
 | 	    if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1) | 
 | 	    { | 
 | 		Free(newrgn); | 
 | 		goto fail; | 
 | 	    } | 
 | 	    chkrgn = &tmprgn; | 
 | 	    break; | 
 | 	} | 
 |  | 
 | 	index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0); | 
 |     } | 
 |  | 
 |     ranges_check(ranges, "after del"); | 
 |     return TRUE; | 
 |  | 
 | fail: | 
 |     ranges_check(ranges, "failed del"); | 
 |     return FALSE; | 
 | } | 
 |  | 
 | /*** | 
 | * DESCRIPTION: | 
 | * Removes all selection ranges | 
 | * | 
 | * Parameters(s): | 
 | * [I] infoPtr : valid pointer to the listview structure | 
 | * [I] toSkip : item range to skip removing the selection | 
 | * | 
 | * RETURNS: | 
 | *   SUCCESS : TRUE | 
 | *   FAILURE : TRUE | 
 | */ | 
 | static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip) | 
 | { | 
 |     LVITEMW lvItem; | 
 |     ITERATOR i; | 
 |     RANGES clone; | 
 |  | 
 |     TRACE("()\n"); | 
 |  | 
 |     lvItem.state = 0; | 
 |     lvItem.stateMask = LVIS_SELECTED; | 
 |      | 
 |     /* need to clone the DPA because callbacks can change it */ | 
 |     if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE; | 
 |     iterator_rangesitems(&i, ranges_diff(clone, toSkip)); | 
 |     while(iterator_next(&i)) | 
 | 	LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem); | 
 |     /* note that the iterator destructor will free the cloned range */ | 
 |     iterator_destroy(&i); | 
 |  | 
 |     return TRUE; | 
 | } | 
 |  | 
 | static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem) | 
 | { | 
 |     RANGES toSkip; | 
 |     | 
 |     if (!(toSkip = ranges_create(1))) return FALSE; | 
 |     if (nItem != -1) ranges_additem(toSkip, nItem); | 
 |     LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip); | 
 |     ranges_destroy(toSkip); | 
 |     return TRUE; | 
 | } | 
 |  | 
 | static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr) | 
 | { | 
 |     return LISTVIEW_DeselectAllSkipItem(infoPtr, -1); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Retrieves the number of items that are marked as selected. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * | 
 |  * RETURN: | 
 |  * Number of items selected. | 
 |  */ | 
 | static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr) | 
 | { | 
 |     INT nSelectedCount = 0; | 
 |  | 
 |     if (infoPtr->uCallbackMask & LVIS_SELECTED) | 
 |     { | 
 |         INT i; | 
 | 	for (i = 0; i < infoPtr->nItemCount; i++) | 
 |   	{ | 
 | 	    if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED)) | 
 | 		nSelectedCount++; | 
 | 	} | 
 |     } | 
 |     else | 
 | 	nSelectedCount = ranges_itemcount(infoPtr->selectionRanges); | 
 |  | 
 |     TRACE("nSelectedCount=%d\n", nSelectedCount); | 
 |     return nSelectedCount; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Manages the item focus. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nItem : item index | 
 |  * | 
 |  * RETURN: | 
 |  *   TRUE : focused item changed | 
 |  *   FALSE : focused item has NOT changed | 
 |  */ | 
 | static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem) | 
 | { | 
 |     INT oldFocus = infoPtr->nFocusedItem; | 
 |     LVITEMW lvItem; | 
 |  | 
 |     if (nItem == infoPtr->nFocusedItem) return FALSE; | 
 |      | 
 |     lvItem.state =  nItem == -1 ? 0 : LVIS_FOCUSED; | 
 |     lvItem.stateMask = LVIS_FOCUSED; | 
 |     LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem); | 
 |  | 
 |     return oldFocus != infoPtr->nFocusedItem; | 
 | } | 
 |  | 
 | /* Helper function for LISTVIEW_ShiftIndices *only* */ | 
 | static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction) | 
 | { | 
 |     if (nShiftItem < nItem) return nShiftItem; | 
 |  | 
 |     if (nShiftItem > nItem) return nShiftItem + direction; | 
 |  | 
 |     if (direction > 0) return nShiftItem + direction; | 
 |  | 
 |     return min(nShiftItem, infoPtr->nItemCount - 1); | 
 | } | 
 |  | 
 | /** | 
 | * DESCRIPTION: | 
 | * Updates the various indices after an item has been inserted or deleted. | 
 | * | 
 | * PARAMETER(S): | 
 | * [I] infoPtr : valid pointer to the listview structure | 
 | * [I] nItem : item index | 
 | * [I] direction : Direction of shift, +1 or -1. | 
 | * | 
 | * RETURN: | 
 | * None | 
 | */ | 
 | static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction) | 
 | { | 
 |     INT nNewFocus; | 
 |     BOOL bOldChange; | 
 |  | 
 |     /* temporarily disable change notification while shifting items */ | 
 |     bOldChange = infoPtr->bDoChangeNotify; | 
 |     infoPtr->bDoChangeNotify = FALSE; | 
 |  | 
 |     TRACE("Shifting %iu, %i steps\n", nItem, direction); | 
 |  | 
 |     ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount); | 
 |  | 
 |     assert(abs(direction) == 1); | 
 |  | 
 |     infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction); | 
 |  | 
 |     nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction); | 
 |     if (nNewFocus != infoPtr->nFocusedItem) | 
 |         LISTVIEW_SetItemFocus(infoPtr, nNewFocus); | 
 |      | 
 |     /* But we are not supposed to modify nHotItem! */ | 
 |  | 
 |     infoPtr->bDoChangeNotify = bOldChange; | 
 | } | 
 |  | 
 |  | 
 | /** | 
 |  * DESCRIPTION: | 
 |  * Adds a block of selections. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nItem : item index | 
 |  * | 
 |  * RETURN: | 
 |  * Whether the window is still valid. | 
 |  */ | 
 | static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem) | 
 | { | 
 |     INT nFirst = min(infoPtr->nSelectionMark, nItem); | 
 |     INT nLast = max(infoPtr->nSelectionMark, nItem); | 
 |     HWND hwndSelf = infoPtr->hwndSelf; | 
 |     NMLVODSTATECHANGE nmlv; | 
 |     LVITEMW item; | 
 |     BOOL bOldChange; | 
 |     INT i; | 
 |  | 
 |     /* Temporarily disable change notification | 
 |      * If the control is LVS_OWNERDATA, we need to send | 
 |      * only one LVN_ODSTATECHANGED notification. | 
 |      * See MSDN documentation for LVN_ITEMCHANGED. | 
 |      */ | 
 |     bOldChange = infoPtr->bDoChangeNotify; | 
 |     if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE; | 
 |  | 
 |     if (nFirst == -1) nFirst = nItem; | 
 |  | 
 |     item.state = LVIS_SELECTED; | 
 |     item.stateMask = LVIS_SELECTED; | 
 |  | 
 |     for (i = nFirst; i <= nLast; i++) | 
 | 	LISTVIEW_SetItemState(infoPtr,i,&item); | 
 |  | 
 |     ZeroMemory(&nmlv, sizeof(nmlv)); | 
 |     nmlv.iFrom = nFirst; | 
 |     nmlv.iTo = nLast; | 
 |     nmlv.uNewState = 0; | 
 |     nmlv.uOldState = item.state; | 
 |  | 
 |     notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv); | 
 |     if (!IsWindow(hwndSelf)) | 
 |         return FALSE; | 
 |     infoPtr->bDoChangeNotify = bOldChange; | 
 |     return TRUE; | 
 | } | 
 |  | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Sets a single group selection. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nItem : item index | 
 |  * | 
 |  * RETURN: | 
 |  * None | 
 |  */ | 
 | static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     RANGES selection; | 
 |     LVITEMW item; | 
 |     ITERATOR i; | 
 |  | 
 |     if (!(selection = ranges_create(100))) return; | 
 |  | 
 |     item.state = LVIS_SELECTED;  | 
 |     item.stateMask = LVIS_SELECTED; | 
 |  | 
 |     if ((uView == LVS_LIST) || (uView == LVS_REPORT)) | 
 |     { | 
 | 	if (infoPtr->nSelectionMark == -1) | 
 | 	{ | 
 | 	    infoPtr->nSelectionMark = nItem; | 
 | 	    ranges_additem(selection, nItem); | 
 | 	} | 
 | 	else | 
 | 	{ | 
 | 	    RANGE sel; | 
 | 	     | 
 | 	    sel.lower = min(infoPtr->nSelectionMark, nItem); | 
 | 	    sel.upper = max(infoPtr->nSelectionMark, nItem) + 1; | 
 | 	    ranges_add(selection, sel); | 
 | 	} | 
 |     } | 
 |     else | 
 |     { | 
 | 	RECT rcItem, rcSel, rcSelMark; | 
 | 	POINT ptItem; | 
 | 	 | 
 | 	rcItem.left = LVIR_BOUNDS; | 
 | 	if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return; | 
 | 	rcSelMark.left = LVIR_BOUNDS; | 
 | 	if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return; | 
 | 	UnionRect(&rcSel, &rcItem, &rcSelMark); | 
 | 	iterator_frameditems(&i, infoPtr, &rcSel); | 
 | 	while(iterator_next(&i)) | 
 | 	{ | 
 | 	    LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem); | 
 | 	    if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem); | 
 | 	} | 
 | 	iterator_destroy(&i); | 
 |     } | 
 |  | 
 |     LISTVIEW_DeselectAllSkipItems(infoPtr, selection); | 
 |     iterator_rangesitems(&i, selection); | 
 |     while(iterator_next(&i)) | 
 | 	LISTVIEW_SetItemState(infoPtr, i.nItem, &item); | 
 |     /* this will also destroy the selection */ | 
 |     iterator_destroy(&i); | 
 |      | 
 |     LISTVIEW_SetItemFocus(infoPtr, nItem); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Sets a single selection. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nItem : item index | 
 |  * | 
 |  * RETURN: | 
 |  * None | 
 |  */ | 
 | static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem) | 
 | { | 
 |     LVITEMW lvItem; | 
 |  | 
 |     TRACE("nItem=%d\n", nItem); | 
 |      | 
 |     LISTVIEW_DeselectAllSkipItem(infoPtr, nItem); | 
 |  | 
 |     lvItem.state = LVIS_FOCUSED | LVIS_SELECTED; | 
 |     lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED; | 
 |     LISTVIEW_SetItemState(infoPtr, nItem, &lvItem); | 
 |  | 
 |     infoPtr->nSelectionMark = nItem; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Set selection(s) with keyboard. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nItem : item index | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE (needs to be repainted) | 
 |  *   FAILURE : FALSE (nothing has changed) | 
 |  */ | 
 | static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem) | 
 | { | 
 |   /* FIXME: pass in the state */ | 
 |   WORD wShift = HIWORD(GetKeyState(VK_SHIFT)); | 
 |   WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL)); | 
 |   BOOL bResult = FALSE; | 
 |  | 
 |   TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl); | 
 |   if ((nItem >= 0) && (nItem < infoPtr->nItemCount)) | 
 |   { | 
 |     if (infoPtr->dwStyle & LVS_SINGLESEL) | 
 |     { | 
 |       bResult = TRUE; | 
 |       LISTVIEW_SetSelection(infoPtr, nItem); | 
 |     } | 
 |     else | 
 |     { | 
 |       if (wShift) | 
 |       { | 
 |         bResult = TRUE; | 
 |         LISTVIEW_SetGroupSelection(infoPtr, nItem); | 
 |       } | 
 |       else if (wCtrl) | 
 |       { | 
 |         LVITEMW lvItem; | 
 |         lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED); | 
 |         lvItem.stateMask = LVIS_SELECTED; | 
 |         LISTVIEW_SetItemState(infoPtr, nItem, &lvItem); | 
 |  | 
 |         if (lvItem.state & LVIS_SELECTED) | 
 |             infoPtr->nSelectionMark = nItem; | 
 |  | 
 |         bResult = LISTVIEW_SetItemFocus(infoPtr, nItem); | 
 |       } | 
 |       else | 
 |       { | 
 |         bResult = TRUE; | 
 |         LISTVIEW_SetSelection(infoPtr, nItem); | 
 |       } | 
 |     } | 
 |     LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE); | 
 |   } | 
 |  | 
 |   UpdateWindow(infoPtr->hwndSelf); /* update client area */ | 
 |   return bResult; | 
 | } | 
 |  | 
 | static BOOL LISTVIEW_GetItemAtPt(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt) | 
 | { | 
 |     LVHITTESTINFO lvHitTestInfo; | 
 |  | 
 |     ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo)); | 
 |     lvHitTestInfo.pt.x = pt.x; | 
 |     lvHitTestInfo.pt.y = pt.y; | 
 |  | 
 |     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE); | 
 |  | 
 |     lpLVItem->mask = LVIF_PARAM; | 
 |     lpLVItem->iItem = lvHitTestInfo.iItem; | 
 |     lpLVItem->iSubItem = 0; | 
 |  | 
 |     return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Called when the mouse is being actively tracked and has hovered for a specified | 
 |  * amount of time | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] fwKeys : key indicator | 
 |  * [I] x,y : mouse position | 
 |  * | 
 |  * RETURN: | 
 |  *   0 if the message was processed, non-zero if there was an error | 
 |  * | 
 |  * INFO: | 
 |  * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains | 
 |  * over the item for a certain period of time. | 
 |  * | 
 |  */ | 
 | static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, INT x, INT y) | 
 | { | 
 |     if (infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) | 
 |     { | 
 |         LVITEMW item; | 
 |         POINT pt; | 
 |  | 
 |         pt.x = x; | 
 |         pt.y = y; | 
 |  | 
 |         if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt)) | 
 |             LISTVIEW_SetSelection(infoPtr, item.iItem); | 
 |     } | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Called whenever WM_MOUSEMOVE is received. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] fwKeys : key indicator | 
 |  * [I] x,y : mouse position | 
 |  * | 
 |  * RETURN: | 
 |  *   0 if the message is processed, non-zero if there was an error | 
 |  */ | 
 | static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y) | 
 | { | 
 |     TRACKMOUSEEVENT trackinfo; | 
 |  | 
 |     if (!(fwKeys & MK_LBUTTON)) | 
 |         infoPtr->bLButtonDown = FALSE; | 
 |  | 
 |     if (infoPtr->bLButtonDown && DragDetect(infoPtr->hwndSelf, infoPtr->ptClickPos)) | 
 |     { | 
 |         LVHITTESTINFO lvHitTestInfo; | 
 |         NMLISTVIEW nmlv; | 
 |  | 
 |         lvHitTestInfo.pt = infoPtr->ptClickPos; | 
 |         LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE); | 
 |  | 
 |         ZeroMemory(&nmlv, sizeof(nmlv)); | 
 |         nmlv.iItem = lvHitTestInfo.iItem; | 
 |         nmlv.ptAction = infoPtr->ptClickPos; | 
 |  | 
 |         notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv); | 
 |  | 
 |         return 0; | 
 |     } | 
 |     else | 
 |         infoPtr->bLButtonDown = FALSE; | 
 |  | 
 |   /* see if we are supposed to be tracking mouse hovering */ | 
 |   if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) { | 
 |      /* fill in the trackinfo struct */ | 
 |      trackinfo.cbSize = sizeof(TRACKMOUSEEVENT); | 
 |      trackinfo.dwFlags = TME_QUERY; | 
 |      trackinfo.hwndTrack = infoPtr->hwndSelf; | 
 |      trackinfo.dwHoverTime = infoPtr->dwHoverTime; | 
 |  | 
 |      /* see if we are already tracking this hwnd */ | 
 |      _TrackMouseEvent(&trackinfo); | 
 |  | 
 |      if(!(trackinfo.dwFlags & TME_HOVER)) { | 
 |        trackinfo.dwFlags = TME_HOVER; | 
 |  | 
 |        /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */ | 
 |        _TrackMouseEvent(&trackinfo); | 
 |     } | 
 |   } | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 |  | 
 | /*** | 
 |  * Tests wheather the item is assignable to a list with style lStyle  | 
 |  */ | 
 | static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle) | 
 | { | 
 |     if ( (lpLVItem->mask & LVIF_TEXT) &&  | 
 | 	(lpLVItem->pszText == LPSTR_TEXTCALLBACKW) && | 
 | 	(lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE; | 
 |      | 
 |     return TRUE; | 
 | } | 
 |  | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Helper for LISTVIEW_SetItemT *only*: sets item attributes. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] lpLVItem : valid pointer to new item atttributes | 
 |  * [I] isNew : the item being set is being inserted | 
 |  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI | 
 |  * [O] bChanged : will be set to TRUE if the item really changed | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     ITEM_INFO *lpItem; | 
 |     NMLISTVIEW nmlv; | 
 |     UINT uChanged = 0; | 
 |     LVITEMW item; | 
 |  | 
 |     TRACE("()\n"); | 
 |  | 
 |     assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount); | 
 |      | 
 |     if (lpLVItem->mask == 0) return TRUE;    | 
 |  | 
 |     if (infoPtr->dwStyle & LVS_OWNERDATA) | 
 |     { | 
 | 	/* a virtual listview we stores only selection and focus */ | 
 | 	if (lpLVItem->mask & ~LVIF_STATE) | 
 | 	    return FALSE; | 
 | 	lpItem = NULL; | 
 |     } | 
 |     else | 
 |     { | 
 | 	HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem); | 
 | 	lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0); | 
 | 	assert (lpItem); | 
 |     } | 
 |  | 
 |     /* we need to get the lParam and state of the item */ | 
 |     item.iItem = lpLVItem->iItem; | 
 |     item.iSubItem = lpLVItem->iSubItem; | 
 |     item.mask = LVIF_STATE | LVIF_PARAM; | 
 |     item.stateMask = ~0; | 
 |     item.state = 0; | 
 |     item.lParam = 0; | 
 |     if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE; | 
 |  | 
 |     TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state); | 
 |     /* determine what fields will change */     | 
 |     if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask)) | 
 | 	uChanged |= LVIF_STATE; | 
 |  | 
 |     if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage)) | 
 | 	uChanged |= LVIF_IMAGE; | 
 |  | 
 |     if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam)) | 
 | 	uChanged |= LVIF_PARAM; | 
 |  | 
 |     if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent)) | 
 | 	uChanged |= LVIF_INDENT; | 
 |  | 
 |     if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW)) | 
 | 	uChanged |= LVIF_TEXT; | 
 |     | 
 |     TRACE("uChanged=0x%x\n", uChanged);  | 
 |     if (!uChanged) return TRUE; | 
 |     *bChanged = TRUE; | 
 |      | 
 |     ZeroMemory(&nmlv, sizeof(NMLISTVIEW)); | 
 |     nmlv.iItem = lpLVItem->iItem; | 
 |     nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask); | 
 |     nmlv.uOldState = item.state; | 
 |     nmlv.uChanged = uChanged; | 
 |     nmlv.lParam = item.lParam; | 
 |      | 
 |     /* send LVN_ITEMCHANGING notification, if the item is not being inserted */ | 
 |     /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */ | 
 |     /* are enabled */ | 
 |     if(lpItem && !isNew && infoPtr->bDoChangeNotify) | 
 |     { | 
 |       HWND hwndSelf = infoPtr->hwndSelf; | 
 |  | 
 |       if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv)) | 
 | 	return FALSE; | 
 |       if (!IsWindow(hwndSelf)) | 
 | 	return FALSE; | 
 |     } | 
 |  | 
 |     /* copy information */ | 
 |     if (lpLVItem->mask & LVIF_TEXT) | 
 |         textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW); | 
 |  | 
 |     if (lpLVItem->mask & LVIF_IMAGE) | 
 | 	lpItem->hdr.iImage = lpLVItem->iImage; | 
 |  | 
 |     if (lpLVItem->mask & LVIF_PARAM) | 
 | 	lpItem->lParam = lpLVItem->lParam; | 
 |  | 
 |     if (lpLVItem->mask & LVIF_INDENT) | 
 | 	lpItem->iIndent = lpLVItem->iIndent; | 
 |  | 
 |     if (uChanged & LVIF_STATE) | 
 |     { | 
 | 	if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED))) | 
 | 	{ | 
 | 	    lpItem->state &= ~lpLVItem->stateMask; | 
 | 	    lpItem->state |= (lpLVItem->state & lpLVItem->stateMask); | 
 | 	} | 
 | 	if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED) | 
 | 	{ | 
 | 	    if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem); | 
 | 	    ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem); | 
 | 	} | 
 | 	else if (lpLVItem->stateMask & LVIS_SELECTED) | 
 | 	    ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem); | 
 | 	 | 
 | 	/* if we are asked to change focus, and we manage it, do it */ | 
 | 	if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED) | 
 | 	{ | 
 | 	    if (lpLVItem->state & LVIS_FOCUSED) | 
 | 	    { | 
 | 		LISTVIEW_SetItemFocus(infoPtr, -1); | 
 | 		infoPtr->nFocusedItem = lpLVItem->iItem; | 
 |     	        LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST); | 
 | 	    } | 
 | 	    else if (infoPtr->nFocusedItem == lpLVItem->iItem) | 
 | 		infoPtr->nFocusedItem = -1; | 
 | 	} | 
 |     } | 
 |  | 
 |     /* if we're inserting the item, we're done */ | 
 |     if (isNew) return TRUE; | 
 |      | 
 |     /* send LVN_ITEMCHANGED notification */ | 
 |     if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam; | 
 |     if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv); | 
 |  | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] lpLVItem : valid pointer to new subitem atttributes | 
 |  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI | 
 |  * [O] bChanged : will be set to TRUE if the item really changed | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged) | 
 | { | 
 |     HDPA hdpaSubItems; | 
 |     SUBITEM_INFO *lpSubItem; | 
 |  | 
 |     /* we do not support subitems for virtual listviews */ | 
 |     if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE; | 
 |      | 
 |     /* set subitem only if column is present */ | 
 |     if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE; | 
 |     | 
 |     /* First do some sanity checks */ | 
 |     if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE; | 
 |     if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE; | 
 |     | 
 |     /* get the subitem structure, and create it if not there */ | 
 |     hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem); | 
 |     assert (hdpaSubItems); | 
 |      | 
 |     lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem); | 
 |     if (!lpSubItem) | 
 |     { | 
 | 	SUBITEM_INFO *tmpSubItem; | 
 | 	INT i; | 
 |  | 
 | 	lpSubItem = (SUBITEM_INFO *)Alloc(sizeof(SUBITEM_INFO)); | 
 | 	if (!lpSubItem) return FALSE; | 
 | 	/* we could binary search here, if need be...*/ | 
 |   	for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++) | 
 |   	{ | 
 | 	    tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i); | 
 | 	    if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break; | 
 |   	} | 
 | 	if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1) | 
 | 	{ | 
 | 	    Free(lpSubItem); | 
 | 	    return FALSE; | 
 | 	} | 
 |         lpSubItem->iSubItem = lpLVItem->iSubItem; | 
 |         lpSubItem->hdr.iImage = I_IMAGECALLBACK; | 
 | 	*bChanged = TRUE; | 
 |     } | 
 |      | 
 |     if (lpLVItem->mask & LVIF_IMAGE) | 
 | 	if (lpSubItem->hdr.iImage != lpLVItem->iImage) | 
 | 	{ | 
 | 	    lpSubItem->hdr.iImage = lpLVItem->iImage; | 
 | 	    *bChanged = TRUE; | 
 | 	} | 
 |  | 
 |     if (lpLVItem->mask & LVIF_TEXT) | 
 | 	if (lpSubItem->hdr.pszText != lpLVItem->pszText) | 
 | 	{ | 
 | 	    textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW); | 
 | 	    *bChanged = TRUE; | 
 | 	} | 
 |  | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Sets item attributes. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] lpLVItem : new item atttributes | 
 |  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     HWND hwndSelf = infoPtr->hwndSelf; | 
 |     LPWSTR pszText = NULL; | 
 |     BOOL bResult, bChanged = FALSE; | 
 |      | 
 |     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW); | 
 |  | 
 |     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount) | 
 | 	return FALSE; | 
 |  | 
 |     /* For efficiency, we transform the lpLVItem->pszText to Unicode here */ | 
 |     if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText)) | 
 |     { | 
 | 	pszText = lpLVItem->pszText; | 
 | 	((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW); | 
 |     } | 
 |      | 
 |     /* actually set the fields */ | 
 |     if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE; | 
 |      | 
 |     if (lpLVItem->iSubItem) | 
 | 	bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged); | 
 |     else | 
 | 	bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged); | 
 |     if (!IsWindow(hwndSelf)) | 
 | 	return FALSE; | 
 |  | 
 |     /* redraw item, if necessary */ | 
 |     if (bChanged && !infoPtr->bIsDrawing) | 
 |     { | 
 | 	/* this little optimization eliminates some nasty flicker */ | 
 | 	if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && | 
 | 	     (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) ) | 
 | 	    LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem); | 
 | 	else | 
 | 	    LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem); | 
 |     } | 
 |     /* restore text */ | 
 |     if (pszText) | 
 |     { | 
 | 	textfreeT(lpLVItem->pszText, isW); | 
 | 	((LVITEMW *)lpLVItem)->pszText = pszText; | 
 |     } | 
 |  | 
 |     return bResult; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Retrieves the index of the item at coordinate (0, 0) of the client area. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * | 
 |  * RETURN: | 
 |  * item index | 
 |  */ | 
 | static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     INT nItem = 0; | 
 |     SCROLLINFO scrollInfo; | 
 |  | 
 |     scrollInfo.cbSize = sizeof(SCROLLINFO); | 
 |     scrollInfo.fMask = SIF_POS; | 
 |  | 
 |     if (uView == LVS_LIST) | 
 |     { | 
 | 	if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) | 
 | 	    nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr); | 
 |     } | 
 |     else if (uView == LVS_REPORT) | 
 |     { | 
 | 	if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) | 
 | 	    nItem = scrollInfo.nPos; | 
 |     }  | 
 |     else | 
 |     { | 
 | 	if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) | 
 | 	    nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight); | 
 |     } | 
 |  | 
 |     TRACE("nItem=%d\n", nItem); | 
 |      | 
 |     return nItem; | 
 | } | 
 |  | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Erases the background of the given rectangle | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] hdc : device context handle | 
 |  * [I] lprcBox : clipping rectangle | 
 |  * | 
 |  * RETURN: | 
 |  *   Success: TRUE | 
 |  *   Failure: FALSE | 
 |  */ | 
 | static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox) | 
 | { | 
 |     if (!infoPtr->hBkBrush) return FALSE; | 
 |  | 
 |     TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush); | 
 |  | 
 |     return FillRect(hdc, lprcBox, infoPtr->hBkBrush); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Draws an item. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] hdc : device context handle | 
 |  * [I] nItem : item index | 
 |  * [I] nSubItem : subitem index | 
 |  * [I] pos : item position in client coordinates | 
 |  * [I] cdmode : custom draw mode | 
 |  * | 
 |  * RETURN: | 
 |  *   Success: TRUE | 
 |  *   Failure: FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode) | 
 | { | 
 |     UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' }; | 
 |     static const WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 }; | 
 |     DWORD cdsubitemmode = CDRF_DODEFAULT; | 
 |     RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel; | 
 |     NMLVCUSTOMDRAW nmlvcd; | 
 |     HIMAGELIST himl; | 
 |     LVITEMW lvItem; | 
 |     HFONT hOldFont; | 
 |  | 
 |     TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos)); | 
 |  | 
 |     /* get information needed for drawing the item */ | 
 |     lvItem.mask = LVIF_TEXT | LVIF_IMAGE; | 
 |     if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM; | 
 |     if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT; | 
 |     lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK; | 
 |     lvItem.iItem = nItem; | 
 |     lvItem.iSubItem = nSubItem; | 
 |     lvItem.state = 0; | 
 |     lvItem.lParam = 0; | 
 |     lvItem.cchTextMax = DISP_TEXT_SIZE; | 
 |     lvItem.pszText = szDispText; | 
 |     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE; | 
 |     if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))  | 
 | 	lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED); | 
 |     if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = (LPWSTR)szCallback; | 
 |     TRACE("   lvItem=%s\n", debuglvitem_t(&lvItem, TRUE)); | 
 |  | 
 |     /* now check if we need to update the focus rectangle */ | 
 |     lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0; | 
 |  | 
 |     if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED; | 
 |     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel); | 
 |     OffsetRect(&rcBox, pos.x, pos.y); | 
 |     OffsetRect(&rcState, pos.x, pos.y); | 
 |     OffsetRect(&rcIcon, pos.x, pos.y); | 
 |     OffsetRect(&rcLabel, pos.x, pos.y); | 
 |     TRACE("    rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",  | 
 |         wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcState), | 
 |         wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel)); | 
 |  | 
 |     /* fill in the custom draw structure */ | 
 |     customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem); | 
 |  | 
 |     hOldFont = GetCurrentObject(hdc, OBJ_FONT); | 
 |     if (nSubItem > 0) cdmode = infoPtr->cditemmode; | 
 |     if (cdmode & CDRF_NOTIFYITEMDRAW) | 
 |         cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd); | 
 |     if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode; | 
 |     if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint; | 
 |     /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */ | 
 |     if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW) | 
 |     { | 
 |         cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd); | 
 |         if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint; | 
 |     } | 
 |     if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW)) | 
 |         prepaint_setup(infoPtr, hdc, &nmlvcd); | 
 |  | 
 |     /* in full row select, subitems, will just use main item's colors */ | 
 |     if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) | 
 | 	nmlvcd.clrTextBk = CLR_NONE; | 
 |      | 
 |     /* state icons */ | 
 |     if (infoPtr->himlState && !IsRectEmpty(&rcState)) | 
 |     { | 
 |         UINT uStateImage = STATEIMAGEINDEX(lvItem.state); | 
 |         if (uStateImage) | 
 | 	{ | 
 | 	     TRACE("uStateImage=%d\n", uStateImage); | 
 | 	     ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL); | 
 | 	} | 
 |     } | 
 |  | 
 |     /* small icons */ | 
 |     himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall); | 
 |     if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) | 
 |     { | 
 | 	TRACE("iImage=%d\n", lvItem.iImage); | 
 | 	ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top, | 
 | 			(lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL); | 
 |     } | 
 |  | 
 |     /* Don't bother painting item being edited */ | 
 |     if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint; | 
 |  | 
 |     /* draw the selection background, if we're drawing the main item */ | 
 |     if (nSubItem == 0) | 
 |     { | 
 | 	rcSelect = rcLabel; | 
 |     	if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)) | 
 | 	    rcSelect.right = rcBox.right; | 
 |     | 
 |     	if (nmlvcd.clrTextBk != CLR_NONE)  | 
 |             ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0); | 
 |     	if(lprcFocus) *lprcFocus = rcSelect; | 
 |     } | 
 |     | 
 |     /* figure out the text drawing flags */ | 
 |     uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS); | 
 |     if (uView == LVS_ICON) | 
 | 	uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS); | 
 |     else if (nSubItem) | 
 |     { | 
 | 	switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK) | 
 | 	{ | 
 | 	case LVCFMT_RIGHT:  uFormat |= DT_RIGHT;  break; | 
 | 	case LVCFMT_CENTER: uFormat |= DT_CENTER; break; | 
 | 	default:            uFormat |= DT_LEFT; | 
 | 	} | 
 |     } | 
 |     if (!(uFormat & (DT_RIGHT | DT_CENTER))) | 
 |     { | 
 |         if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING; | 
 |         else rcLabel.left += LABEL_HOR_PADDING; | 
 |     } | 
 |     else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING; | 
 |     DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat); | 
 |  | 
 | postpaint: | 
 |     if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT) | 
 |         notify_postpaint(infoPtr, &nmlvcd); | 
 |     if (cdsubitemmode & CDRF_NEWFONT) | 
 |         SelectObject(hdc, hOldFont); | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Draws listview items when in owner draw mode. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] hdc : device context handle | 
 |  * | 
 |  * RETURN: | 
 |  * None | 
 |  */ | 
 | static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode) | 
 | { | 
 |     UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID); | 
 |     DWORD cditemmode = CDRF_DODEFAULT; | 
 |     NMLVCUSTOMDRAW nmlvcd; | 
 |     POINT Origin, Position; | 
 |     DRAWITEMSTRUCT dis; | 
 |     LVITEMW item; | 
 |      | 
 |     TRACE("()\n"); | 
 |  | 
 |     ZeroMemory(&dis, sizeof(dis)); | 
 |      | 
 |     /* Get scroll info once before loop */ | 
 |     LISTVIEW_GetOrigin(infoPtr, &Origin); | 
 |      | 
 |     /* iterate through the invalidated rows */ | 
 |     while(iterator_next(i)) | 
 |     { | 
 | 	item.iItem = i->nItem; | 
 | 	item.iSubItem = 0; | 
 | 	item.mask = LVIF_PARAM | LVIF_STATE; | 
 | 	item.stateMask = LVIS_SELECTED | LVIS_FOCUSED; | 
 | 	if (!LISTVIEW_GetItemW(infoPtr, &item)) continue; | 
 | 	    | 
 | 	dis.CtlType = ODT_LISTVIEW; | 
 | 	dis.CtlID = uID; | 
 | 	dis.itemID = item.iItem; | 
 | 	dis.itemAction = ODA_DRAWENTIRE; | 
 | 	dis.itemState = 0; | 
 | 	if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED; | 
 | 	if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS; | 
 | 	dis.hwndItem = infoPtr->hwndSelf; | 
 | 	dis.hDC = hdc; | 
 | 	LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position); | 
 | 	dis.rcItem.left = Position.x + Origin.x; | 
 | 	dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth; | 
 | 	dis.rcItem.top = Position.y + Origin.y; | 
 | 	dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight; | 
 | 	dis.itemData = item.lParam; | 
 |  | 
 | 	TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem)); | 
 |  | 
 |     /* | 
 |      * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd | 
 |      * structure for the rest. of the paint cycle | 
 |      */ | 
 | 	customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item); | 
 | 	if (cdmode & CDRF_NOTIFYITEMDRAW) | 
 |             cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd); | 
 |      | 
 | 	if (!(cditemmode & CDRF_SKIPDEFAULT)) | 
 | 	{ | 
 |             prepaint_setup (infoPtr, hdc, &nmlvcd); | 
 | 	    SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis); | 
 | 	} | 
 |  | 
 |     	if (cditemmode & CDRF_NOTIFYPOSTPAINT) | 
 |             notify_postpaint(infoPtr, &nmlvcd); | 
 |     } | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Draws listview items when in report display mode. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] hdc : device context handle | 
 |  * [I] cdmode : custom draw mode | 
 |  * | 
 |  * RETURN: | 
 |  * None | 
 |  */ | 
 | static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode) | 
 | { | 
 |     INT rgntype; | 
 |     RECT rcClip, rcItem; | 
 |     POINT Origin, Position; | 
 |     RANGE colRange; | 
 |     ITERATOR j; | 
 |  | 
 |     TRACE("()\n"); | 
 |  | 
 |     /* figure out what to draw */ | 
 |     rgntype = GetClipBox(hdc, &rcClip); | 
 |     if (rgntype == NULLREGION) return; | 
 |      | 
 |     /* Get scroll info once before loop */ | 
 |     LISTVIEW_GetOrigin(infoPtr, &Origin); | 
 |      | 
 |     /* narrow down the columns we need to paint */ | 
 |     for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++) | 
 |     { | 
 | 	LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem); | 
 | 	if (rcItem.right + Origin.x >= rcClip.left) break; | 
 |     } | 
 |     for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--) | 
 |     { | 
 | 	LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem); | 
 | 	if (rcItem.left + Origin.x < rcClip.right) break; | 
 |     } | 
 |     iterator_rangeitems(&j, colRange); | 
 |  | 
 |     /* in full row select, we _have_ to draw the main item */ | 
 |     if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) | 
 | 	j.nSpecial = 0; | 
 |  | 
 |     /* iterate through the invalidated rows */ | 
 |     while(iterator_next(i)) | 
 |     { | 
 | 	/* iterate through the invalidated columns */ | 
 | 	while(iterator_next(&j)) | 
 | 	{ | 
 | 	    LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position); | 
 | 	    Position.x += Origin.x; | 
 | 	    Position.y += Origin.y; | 
 |  | 
 | 	    if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0)) | 
 | 	    { | 
 | 		LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem); | 
 | 		rcItem.top = 0; | 
 | 	        rcItem.bottom = infoPtr->nItemHeight; | 
 | 		OffsetRect(&rcItem, Position.x, Position.y); | 
 | 		if (!RectVisible(hdc, &rcItem)) continue; | 
 | 	    } | 
 |  | 
 | 	    LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode); | 
 | 	} | 
 |     } | 
 |     iterator_destroy(&j); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Draws listview items when in list display mode. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] hdc : device context handle | 
 |  * [I] cdmode : custom draw mode | 
 |  * | 
 |  * RETURN: | 
 |  * None | 
 |  */ | 
 | static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode) | 
 | { | 
 |     POINT Origin, Position; | 
 |  | 
 |     /* Get scroll info once before loop */ | 
 |     LISTVIEW_GetOrigin(infoPtr, &Origin); | 
 |      | 
 |     while(iterator_prev(i)) | 
 |     { | 
 | 	LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position); | 
 | 	Position.x += Origin.x; | 
 | 	Position.y += Origin.y; | 
 |  | 
 |         LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode); | 
 |     } | 
 | } | 
 |  | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Draws listview items. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] hdc : device context handle | 
 |  * | 
 |  * RETURN: | 
 |  * NoneX | 
 |  */ | 
 | static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     COLORREF oldTextColor, oldClrTextBk, oldClrText; | 
 |     NMLVCUSTOMDRAW nmlvcd; | 
 |     HFONT hOldFont; | 
 |     DWORD cdmode; | 
 |     INT oldBkMode; | 
 |     RECT rcClient; | 
 |     ITERATOR i; | 
 |  | 
 |     LISTVIEW_DUMP(infoPtr); | 
 |    | 
 |     infoPtr->bIsDrawing = TRUE; | 
 |  | 
 |     /* save dc values we're gonna trash while drawing */ | 
 |     hOldFont = SelectObject(hdc, infoPtr->hFont); | 
 |     oldBkMode = GetBkMode(hdc); | 
 |     infoPtr->clrTextBkDefault = GetBkColor(hdc); | 
 |     oldTextColor = GetTextColor(hdc); | 
 |  | 
 |     oldClrTextBk = infoPtr->clrTextBk; | 
 |     oldClrText   = infoPtr->clrText; | 
 |     | 
 |     infoPtr->cditemmode = CDRF_DODEFAULT; | 
 |  | 
 |     GetClientRect(infoPtr->hwndSelf, &rcClient); | 
 |     customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0); | 
 |     cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd); | 
 |     if (cdmode & CDRF_SKIPDEFAULT) goto enddraw; | 
 |     prepaint_setup(infoPtr, hdc, &nmlvcd); | 
 |  | 
 |     /* Use these colors to draw the items */ | 
 |     infoPtr->clrTextBk = nmlvcd.clrTextBk; | 
 |     infoPtr->clrText = nmlvcd.clrText; | 
 |  | 
 |     /* nothing to draw */ | 
 |     if(infoPtr->nItemCount == 0) goto enddraw; | 
 |  | 
 |     /* figure out what we need to draw */ | 
 |     iterator_visibleitems(&i, infoPtr, hdc); | 
 |      | 
 |     /* send cache hint notification */ | 
 |     if (infoPtr->dwStyle & LVS_OWNERDATA) | 
 |     { | 
 |     	RANGE range = iterator_range(&i); | 
 | 	NMLVCACHEHINT nmlv; | 
 | 	 | 
 |     	ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT)); | 
 |     	nmlv.iFrom = range.lower; | 
 |     	nmlv.iTo   = range.upper - 1; | 
 |     	notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr); | 
 |     } | 
 |  | 
 |     if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT)) | 
 | 	LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode); | 
 |     else | 
 |     { | 
 |     	if (uView == LVS_REPORT) | 
 |             LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode); | 
 | 	else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */ | 
 | 	    LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode); | 
 |  | 
 | 	/* if we have a focus rect, draw it */ | 
 | 	if (infoPtr->bFocus) | 
 | 	    DrawFocusRect(hdc, &infoPtr->rcFocus); | 
 |     } | 
 |     iterator_destroy(&i); | 
 |      | 
 | enddraw: | 
 |     if (cdmode & CDRF_NOTIFYPOSTPAINT) | 
 | 	notify_postpaint(infoPtr, &nmlvcd); | 
 |  | 
 |     infoPtr->clrTextBk = oldClrTextBk; | 
 |     infoPtr->clrText = oldClrText; | 
 |  | 
 |     SelectObject(hdc, hOldFont); | 
 |     SetBkMode(hdc, oldBkMode); | 
 |     SetBkColor(hdc, infoPtr->clrTextBkDefault); | 
 |     SetTextColor(hdc, oldTextColor); | 
 |     infoPtr->bIsDrawing = FALSE; | 
 | } | 
 |  | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Calculates the approximate width and height of a given number of items. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nItemCount : number of items | 
 |  * [I] wWidth : width | 
 |  * [I] wHeight : height | 
 |  * | 
 |  * RETURN: | 
 |  * Returns a DWORD. The width in the low word and the height in high word. | 
 |  */ | 
 | static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount, | 
 |                                             WORD wWidth, WORD wHeight) | 
 | { | 
 |   UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |   INT nItemCountPerColumn = 1; | 
 |   INT nColumnCount = 0; | 
 |   DWORD dwViewRect = 0; | 
 |  | 
 |   if (nItemCount == -1) | 
 |     nItemCount = infoPtr->nItemCount; | 
 |  | 
 |   if (uView == LVS_LIST) | 
 |   { | 
 |     if (wHeight == 0xFFFF) | 
 |     { | 
 |       /* use current height */ | 
 |       wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top; | 
 |     } | 
 |  | 
 |     if (wHeight < infoPtr->nItemHeight) | 
 |       wHeight = infoPtr->nItemHeight; | 
 |  | 
 |     if (nItemCount > 0) | 
 |     { | 
 |       if (infoPtr->nItemHeight > 0) | 
 |       { | 
 |         nItemCountPerColumn = wHeight / infoPtr->nItemHeight; | 
 |         if (nItemCountPerColumn == 0) | 
 |           nItemCountPerColumn = 1; | 
 |  | 
 |         if (nItemCount % nItemCountPerColumn != 0) | 
 |           nColumnCount = nItemCount / nItemCountPerColumn; | 
 |         else | 
 |           nColumnCount = nItemCount / nItemCountPerColumn + 1; | 
 |       } | 
 |     } | 
 |  | 
 |     /* Microsoft padding magic */ | 
 |     wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2; | 
 |     wWidth = nColumnCount * infoPtr->nItemWidth + 2; | 
 |  | 
 |     dwViewRect = MAKELONG(wWidth, wHeight); | 
 |   } | 
 |   else if (uView == LVS_REPORT) | 
 |   { | 
 |     RECT rcBox; | 
 |  | 
 |     if (infoPtr->nItemCount > 0) | 
 |     { | 
 |       LISTVIEW_GetItemBox(infoPtr, 0, &rcBox); | 
 |       wWidth = rcBox.right - rcBox.left; | 
 |       wHeight = (rcBox.bottom - rcBox.top) * nItemCount; | 
 |     } | 
 |     else | 
 |     { | 
 |       /* use current height and width */ | 
 |       if (wHeight == 0xffff) | 
 |           wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top; | 
 |       if (wWidth == 0xffff) | 
 |           wWidth = infoPtr->rcList.right - infoPtr->rcList.left; | 
 |     } | 
 |  | 
 |     dwViewRect = MAKELONG(wWidth, wHeight); | 
 |   } | 
 |   else if (uView == LVS_SMALLICON) | 
 |     FIXME("uView == LVS_SMALLICON: not implemented\n"); | 
 |   else if (uView == LVS_ICON) | 
 |     FIXME("uView == LVS_ICON: not implemented\n"); | 
 |  | 
 |   return dwViewRect; | 
 | } | 
 |  | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Create a drag image list for the specified item. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] iItem   : index of item | 
 |  * [O] lppt    : Upperr-left corner of the image | 
 |  * | 
 |  * RETURN: | 
 |  * Returns a handle to the image list if successful, NULL otherwise. | 
 |  */ | 
 | static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt) | 
 | { | 
 |     RECT rcItem; | 
 |     SIZE size; | 
 |     POINT pos; | 
 |     HDC hdc, hdcOrig; | 
 |     HBITMAP hbmp, hOldbmp; | 
 |     HIMAGELIST dragList = 0; | 
 |     TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount); | 
 |  | 
 |     if (iItem < 0 || iItem >= infoPtr->nItemCount) | 
 |         return 0; | 
 |  | 
 |     rcItem.left = LVIR_BOUNDS; | 
 |     if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem)) | 
 |         return 0; | 
 |  | 
 |     lppt->x = rcItem.left; | 
 |     lppt->y = rcItem.top; | 
 |  | 
 |     size.cx = rcItem.right - rcItem.left; | 
 |     size.cy = rcItem.bottom - rcItem.top; | 
 |  | 
 |     hdcOrig = GetDC(infoPtr->hwndSelf); | 
 |     hdc = CreateCompatibleDC(hdcOrig); | 
 |     hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy); | 
 |     hOldbmp = SelectObject(hdc, hbmp); | 
 |  | 
 |     rcItem.left = rcItem.top = 0; | 
 |     rcItem.right = size.cx; | 
 |     rcItem.bottom = size.cy; | 
 |     FillRect(hdc, &rcItem, infoPtr->hBkBrush); | 
 |      | 
 |     pos.x = pos.y = 0; | 
 |     if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode)) | 
 |     { | 
 |         dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10); | 
 |         SelectObject(hdc, hOldbmp); | 
 |         ImageList_Add(dragList, hbmp, 0); | 
 |     } | 
 |     else | 
 |         SelectObject(hdc, hOldbmp); | 
 |  | 
 |     DeleteObject(hbmp); | 
 |     DeleteDC(hdc); | 
 |     ReleaseDC(infoPtr->hwndSelf, hdcOrig); | 
 |  | 
 |     TRACE("ret=%p\n", dragList); | 
 |  | 
 |     return dragList; | 
 | } | 
 |  | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Removes all listview items and subitems. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr) | 
 | { | 
 |     NMLISTVIEW nmlv; | 
 |     HDPA hdpaSubItems = NULL; | 
 |     BOOL bSuppress; | 
 |     ITEMHDR *hdrItem; | 
 |     INT i, j; | 
 |  | 
 |     TRACE("()\n"); | 
 |  | 
 |     /* we do it directly, to avoid notifications */ | 
 |     ranges_clear(infoPtr->selectionRanges); | 
 |     infoPtr->nSelectionMark = -1; | 
 |     infoPtr->nFocusedItem = -1; | 
 |     SetRectEmpty(&infoPtr->rcFocus); | 
 |     /* But we are supposed to leave nHotItem as is! */ | 
 |  | 
 |  | 
 |     /* send LVN_DELETEALLITEMS notification */ | 
 |     ZeroMemory(&nmlv, sizeof(NMLISTVIEW)); | 
 |     nmlv.iItem = -1; | 
 |     bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv); | 
 |  | 
 |     for (i = infoPtr->nItemCount - 1; i >= 0; i--) | 
 |     { | 
 |         /* send LVN_DELETEITEM notification, if not suppressed */ | 
 | 	if (!bSuppress) notify_deleteitem(infoPtr, i); | 
 | 	if (!(infoPtr->dwStyle & LVS_OWNERDATA)) | 
 | 	{ | 
 | 	    hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i); | 
 | 	    for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++) | 
 | 	    { | 
 | 		hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j); | 
 | 		if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText); | 
 | 		Free(hdrItem); | 
 | 	    } | 
 | 	    DPA_Destroy(hdpaSubItems); | 
 | 	    DPA_DeletePtr(infoPtr->hdpaItems, i); | 
 | 	} | 
 | 	DPA_DeletePtr(infoPtr->hdpaPosX, i); | 
 | 	DPA_DeletePtr(infoPtr->hdpaPosY, i); | 
 | 	infoPtr->nItemCount --; | 
 |     } | 
 |      | 
 |     LISTVIEW_UpdateScroll(infoPtr); | 
 |  | 
 |     LISTVIEW_InvalidateList(infoPtr); | 
 |      | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Scrolls, and updates the columns, when a column is changing width. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nColumn : column to scroll | 
 |  * [I] dx : amount of scroll, in pixels | 
 |  * | 
 |  * RETURN: | 
 |  *   None. | 
 |  */ | 
 | static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx) | 
 | { | 
 |     COLUMN_INFO *lpColumnInfo; | 
 |     RECT rcOld, rcCol; | 
 |     POINT ptOrigin; | 
 |     INT nCol; | 
 |     | 
 |     if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return; | 
 |     lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)); | 
 |     rcCol = lpColumnInfo->rcHeader; | 
 |     if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) | 
 | 	rcCol.left = rcCol.right; | 
 |      | 
 |     /* ajust the other columns */ | 
 |     for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++) | 
 |     { | 
 | 	lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol); | 
 |         lpColumnInfo->rcHeader.left += dx; | 
 |         lpColumnInfo->rcHeader.right += dx; | 
 |     } | 
 |  | 
 |     /* do not update screen if not in report mode */ | 
 |     if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return; | 
 |      | 
 |     /* if we have a focus, must first erase the focus rect */ | 
 |     if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE); | 
 |      | 
 |     /* Need to reset the item width when inserting a new column */ | 
 |     infoPtr->nItemWidth += dx; | 
 |  | 
 |     LISTVIEW_UpdateScroll(infoPtr); | 
 |     LISTVIEW_GetOrigin(infoPtr, &ptOrigin); | 
 |  | 
 |     /* scroll to cover the deleted column, and invalidate for redraw */ | 
 |     rcOld = infoPtr->rcList; | 
 |     rcOld.left = ptOrigin.x + rcCol.left + dx; | 
 |     ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE); | 
 |      | 
 |     /* we can restore focus now */ | 
 |     if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Removes a column from the listview control. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nColumn : column index | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn) | 
 | { | 
 |     RECT rcCol; | 
 |      | 
 |     TRACE("nColumn=%d\n", nColumn); | 
 |  | 
 |     if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0 | 
 |            || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE; | 
 |  | 
 |     /* While the MSDN specifically says that column zero should not be deleted, | 
 |        what actually happens is that the column itself is deleted but no items or subitems | 
 |        are removed. | 
 |      */ | 
 |  | 
 |     LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol); | 
 |      | 
 |     if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn)) | 
 | 	return FALSE; | 
 |  | 
 |     Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn)); | 
 |     DPA_DeletePtr(infoPtr->hdpaColumns, nColumn); | 
 |    | 
 |     if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn) | 
 |     { | 
 | 	SUBITEM_INFO *lpSubItem, *lpDelItem; | 
 | 	HDPA hdpaSubItems; | 
 | 	INT nItem, nSubItem, i; | 
 | 	 | 
 | 	for (nItem = 0; nItem < infoPtr->nItemCount; nItem++) | 
 | 	{ | 
 | 	    hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem); | 
 | 	    nSubItem = 0; | 
 | 	    lpDelItem = 0; | 
 | 	    for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++) | 
 | 	    { | 
 | 		lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i); | 
 | 		if (lpSubItem->iSubItem == nColumn) | 
 | 		{ | 
 | 		    nSubItem = i; | 
 | 		    lpDelItem = lpSubItem; | 
 | 		} | 
 | 		else if (lpSubItem->iSubItem > nColumn)  | 
 | 		{ | 
 | 		    lpSubItem->iSubItem--; | 
 | 		} | 
 | 	    } | 
 |  | 
 | 	    /* if we found our subitem, zapp it */	 | 
 | 	    if (nSubItem > 0) | 
 | 	    { | 
 | 		/* free string */ | 
 | 		if (is_textW(lpDelItem->hdr.pszText)) | 
 | 		    Free(lpDelItem->hdr.pszText); | 
 |  | 
 | 		/* free item */ | 
 | 		Free(lpDelItem); | 
 |  | 
 | 		/* free dpa memory */ | 
 | 		DPA_DeletePtr(hdpaSubItems, nSubItem); | 
 |     	    } | 
 | 	} | 
 |     } | 
 |  | 
 |     /* update the other column info */ | 
 |     if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) | 
 |         LISTVIEW_InvalidateList(infoPtr); | 
 |     else | 
 |         LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left)); | 
 |  | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Invalidates the listview after an item's insertion or deletion. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nItem : item index | 
 |  * [I] dir : -1 if deleting, 1 if inserting | 
 |  * | 
 |  * RETURN: | 
 |  *   None | 
 |  */ | 
 | static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     INT nPerCol, nItemCol, nItemRow; | 
 |     RECT rcScroll; | 
 |     POINT Origin; | 
 |  | 
 |     /* if we don't refresh, what's the point of scrolling? */ | 
 |     if (!is_redrawing(infoPtr)) return; | 
 |      | 
 |     assert (abs(dir) == 1); | 
 |  | 
 |     /* arrange icons if autoarrange is on */ | 
 |     if (is_autoarrange(infoPtr)) | 
 |     { | 
 | 	BOOL arrange = TRUE; | 
 | 	if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE; | 
 | 	if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE; | 
 | 	if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); | 
 |     } | 
 |  | 
 |     /* scrollbars need updating */ | 
 |     LISTVIEW_UpdateScroll(infoPtr); | 
 |  | 
 |     /* figure out the item's position */  | 
 |     if (uView == LVS_REPORT) | 
 | 	nPerCol = infoPtr->nItemCount + 1; | 
 |     else if (uView == LVS_LIST) | 
 | 	nPerCol = LISTVIEW_GetCountPerColumn(infoPtr); | 
 |     else /* LVS_ICON, or LVS_SMALLICON */ | 
 | 	return; | 
 |      | 
 |     nItemCol = nItem / nPerCol; | 
 |     nItemRow = nItem % nPerCol; | 
 |     LISTVIEW_GetOrigin(infoPtr, &Origin); | 
 |  | 
 |     /* move the items below up a slot */ | 
 |     rcScroll.left = nItemCol * infoPtr->nItemWidth; | 
 |     rcScroll.top = nItemRow * infoPtr->nItemHeight; | 
 |     rcScroll.right = rcScroll.left + infoPtr->nItemWidth; | 
 |     rcScroll.bottom = nPerCol * infoPtr->nItemHeight; | 
 |     OffsetRect(&rcScroll, Origin.x, Origin.y); | 
 |     TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight); | 
 |     if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList)) | 
 |     { | 
 | 	TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList)); | 
 | 	ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,  | 
 | 		       &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE); | 
 |     } | 
 |  | 
 |     /* report has only that column, so we're done */ | 
 |     if (uView == LVS_REPORT) return; | 
 |  | 
 |     /* now for LISTs, we have to deal with the columns to the right */ | 
 |     rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth; | 
 |     rcScroll.top = 0; | 
 |     rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth; | 
 |     rcScroll.bottom = nPerCol * infoPtr->nItemHeight; | 
 |     OffsetRect(&rcScroll, Origin.x, Origin.y); | 
 |     if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList)) | 
 | 	ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight, | 
 | 		       &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Removes an item from the listview control. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nItem : item index | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     LVITEMW item; | 
 |  | 
 |     TRACE("(nItem=%d)\n", nItem); | 
 |  | 
 |     if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE; | 
 |      | 
 |     /* remove selection, and focus */ | 
 |     item.state = 0; | 
 |     item.stateMask = LVIS_SELECTED | LVIS_FOCUSED; | 
 |     LISTVIEW_SetItemState(infoPtr, nItem, &item); | 
 | 	     | 
 |     /* send LVN_DELETEITEM notification. */ | 
 |     if (!notify_deleteitem(infoPtr, nItem)) return FALSE; | 
 |  | 
 |     /* we need to do this here, because we'll be deleting stuff */   | 
 |     if (uView == LVS_SMALLICON || uView == LVS_ICON) | 
 | 	LISTVIEW_InvalidateItem(infoPtr, nItem); | 
 |      | 
 |     if (!(infoPtr->dwStyle & LVS_OWNERDATA)) | 
 |     { | 
 |         HDPA hdpaSubItems; | 
 | 	ITEMHDR *hdrItem; | 
 | 	INT i; | 
 |  | 
 | 	hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);	 | 
 | 	for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++) | 
 |     	{ | 
 |             hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i); | 
 | 	    if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText); | 
 |             Free(hdrItem); | 
 |         } | 
 |         DPA_Destroy(hdpaSubItems); | 
 |     } | 
 |  | 
 |     if (uView == LVS_SMALLICON || uView == LVS_ICON) | 
 |     { | 
 | 	DPA_DeletePtr(infoPtr->hdpaPosX, nItem); | 
 | 	DPA_DeletePtr(infoPtr->hdpaPosY, nItem); | 
 |     } | 
 |  | 
 |     infoPtr->nItemCount--; | 
 |     LISTVIEW_ShiftIndices(infoPtr, nItem, -1); | 
 |  | 
 |     /* now is the invalidation fun */ | 
 |     LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1); | 
 |     return TRUE; | 
 | } | 
 |  | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Callback implementation for editlabel control | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] pszText : modified text | 
 |  * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW) | 
 | { | 
 |     HWND hwndSelf = infoPtr->hwndSelf; | 
 |     NMLVDISPINFOW dispInfo; | 
 |  | 
 |     TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW); | 
 |  | 
 |     ZeroMemory(&dispInfo, sizeof(dispInfo)); | 
 |     dispInfo.item.mask = LVIF_PARAM | LVIF_STATE; | 
 |     dispInfo.item.iItem = infoPtr->nEditLabelItem; | 
 |     dispInfo.item.iSubItem = 0; | 
 |     dispInfo.item.stateMask = ~0; | 
 |     if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE; | 
 |     /* add the text from the edit in */ | 
 |     dispInfo.item.mask |= LVIF_TEXT; | 
 |     dispInfo.item.pszText = pszText; | 
 |     dispInfo.item.cchTextMax = textlenT(pszText, isW); | 
 |  | 
 |     /* Do we need to update the Item Text */ | 
 |     if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE; | 
 |     if (!IsWindow(hwndSelf)) | 
 | 	return FALSE; | 
 |     if (!pszText) return TRUE; | 
 |  | 
 |     if (!(infoPtr->dwStyle & LVS_OWNERDATA)) | 
 |     { | 
 |         HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nEditLabelItem); | 
 |         ITEM_INFO* lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0); | 
 |         if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW) | 
 |         { | 
 |             LISTVIEW_InvalidateItem(infoPtr, infoPtr->nEditLabelItem); | 
 |             return TRUE; | 
 |         } | 
 |     } | 
 |  | 
 |     ZeroMemory(&dispInfo, sizeof(dispInfo)); | 
 |     dispInfo.item.mask = LVIF_TEXT; | 
 |     dispInfo.item.iItem = infoPtr->nEditLabelItem; | 
 |     dispInfo.item.iSubItem = 0; | 
 |     dispInfo.item.pszText = pszText; | 
 |     dispInfo.item.cchTextMax = textlenT(pszText, isW); | 
 |     return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Begin in place editing of specified list view item | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nItem : item index | 
 |  * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW) | 
 | { | 
 |     WCHAR szDispText[DISP_TEXT_SIZE] = { 0 }; | 
 |     NMLVDISPINFOW dispInfo; | 
 |     RECT rect; | 
 |     HWND hwndSelf = infoPtr->hwndSelf; | 
 |  | 
 |     TRACE("(nItem=%d, isW=%d)\n", nItem, isW); | 
 |  | 
 |     if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0; | 
 |     if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0; | 
 |  | 
 |     infoPtr->nEditLabelItem = nItem; | 
 |  | 
 |     /* Is the EditBox still there, if so remove it */ | 
 |     if(infoPtr->hwndEdit != 0) | 
 |     { | 
 |         SetFocus(infoPtr->hwndSelf); | 
 |         infoPtr->hwndEdit = 0; | 
 |     } | 
 |  | 
 |     LISTVIEW_SetSelection(infoPtr, nItem); | 
 |     LISTVIEW_SetItemFocus(infoPtr, nItem); | 
 |     LISTVIEW_InvalidateItem(infoPtr, nItem); | 
 |  | 
 |     rect.left = LVIR_LABEL; | 
 |     if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0; | 
 |      | 
 |     ZeroMemory(&dispInfo, sizeof(dispInfo)); | 
 |     dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT; | 
 |     dispInfo.item.iItem = nItem; | 
 |     dispInfo.item.iSubItem = 0; | 
 |     dispInfo.item.stateMask = ~0; | 
 |     dispInfo.item.pszText = szDispText; | 
 |     dispInfo.item.cchTextMax = DISP_TEXT_SIZE; | 
 |     if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0; | 
 |  | 
 |     infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE, | 
 | 		    rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW); | 
 |     if (!infoPtr->hwndEdit) return 0; | 
 |      | 
 |     if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW)) | 
 |     { | 
 | 	if (!IsWindow(hwndSelf)) | 
 | 	    return 0; | 
 | 	SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0); | 
 | 	infoPtr->hwndEdit = 0; | 
 | 	return 0; | 
 |     } | 
 |  | 
 |     ShowWindow(infoPtr->hwndEdit, SW_NORMAL); | 
 |     SetFocus(infoPtr->hwndEdit); | 
 |     SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1); | 
 |     return infoPtr->hwndEdit; | 
 | } | 
 |  | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Ensures the specified item is visible, scrolling into view if necessary. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nItem : item index | 
 |  * [I] bPartial : partially or entirely visible | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     INT nScrollPosHeight = 0; | 
 |     INT nScrollPosWidth = 0; | 
 |     INT nHorzAdjust = 0; | 
 |     INT nVertAdjust = 0; | 
 |     INT nHorzDiff = 0; | 
 |     INT nVertDiff = 0; | 
 |     RECT rcItem, rcTemp; | 
 |  | 
 |     rcItem.left = LVIR_BOUNDS; | 
 |     if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE; | 
 |  | 
 |     if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE; | 
 |      | 
 |     if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right) | 
 |     { | 
 |         /* scroll left/right, but in LVS_REPORT mode */ | 
 |         if (uView == LVS_LIST) | 
 |             nScrollPosWidth = infoPtr->nItemWidth; | 
 |         else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON)) | 
 |             nScrollPosWidth = 1; | 
 |  | 
 | 	if (rcItem.left < infoPtr->rcList.left) | 
 | 	{ | 
 | 	    nHorzAdjust = -1; | 
 | 	    if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left; | 
 | 	} | 
 | 	else | 
 | 	{ | 
 | 	    nHorzAdjust = 1; | 
 | 	    if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right; | 
 | 	} | 
 |     } | 
 |  | 
 |     if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom) | 
 |     { | 
 | 	/* scroll up/down, but not in LVS_LIST mode */ | 
 |         if (uView == LVS_REPORT) | 
 |             nScrollPosHeight = infoPtr->nItemHeight; | 
 |         else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON)) | 
 |             nScrollPosHeight = 1; | 
 |  | 
 | 	if (rcItem.top < infoPtr->rcList.top) | 
 | 	{ | 
 | 	    nVertAdjust = -1; | 
 | 	    if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top; | 
 | 	} | 
 | 	else | 
 | 	{ | 
 | 	    nVertAdjust = 1; | 
 | 	    if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom; | 
 | 	} | 
 |     } | 
 |  | 
 |     if (!nScrollPosWidth && !nScrollPosHeight) return TRUE; | 
 |  | 
 |     if (nScrollPosWidth) | 
 |     { | 
 | 	INT diff = nHorzDiff / nScrollPosWidth; | 
 | 	if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust; | 
 | 	LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0); | 
 |     } | 
 |  | 
 |     if (nScrollPosHeight) | 
 |     { | 
 | 	INT diff = nVertDiff / nScrollPosHeight; | 
 | 	if (nVertDiff % nScrollPosHeight) diff += nVertAdjust; | 
 | 	LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0); | 
 |     } | 
 |  | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Searches for an item with specific characteristics. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] hwnd : window handle | 
 |  * [I] nStart : base item index | 
 |  * [I] lpFindInfo : item information to look for | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : index of item | 
 |  *   FAILURE : -1 | 
 |  */ | 
 | static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart, | 
 |                               const LVFINDINFOW *lpFindInfo) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' }; | 
 |     BOOL bWrap = FALSE, bNearest = FALSE; | 
 |     INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1; | 
 |     ULONG xdist, ydist, dist, mindist = 0x7fffffff; | 
 |     POINT Position, Destination; | 
 |     LVITEMW lvItem; | 
 |  | 
 |     if (!lpFindInfo || nItem < 0) return -1; | 
 |      | 
 |     lvItem.mask = 0; | 
 |     if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL)) | 
 |     { | 
 |         lvItem.mask |= LVIF_TEXT; | 
 |         lvItem.pszText = szDispText; | 
 |         lvItem.cchTextMax = DISP_TEXT_SIZE; | 
 |     } | 
 |  | 
 |     if (lpFindInfo->flags & LVFI_WRAP) | 
 |         bWrap = TRUE; | 
 |  | 
 |     if ((lpFindInfo->flags & LVFI_NEARESTXY) &&  | 
 | 	(uView == LVS_ICON || uView ==LVS_SMALLICON)) | 
 |     { | 
 | 	POINT Origin; | 
 | 	RECT rcArea; | 
 | 	 | 
 |         LISTVIEW_GetOrigin(infoPtr, &Origin); | 
 | 	Destination.x = lpFindInfo->pt.x - Origin.x; | 
 | 	Destination.y = lpFindInfo->pt.y - Origin.y; | 
 | 	switch(lpFindInfo->vkDirection) | 
 | 	{ | 
 | 	case VK_DOWN:  Destination.y += infoPtr->nItemHeight; break; | 
 | 	case VK_UP:    Destination.y -= infoPtr->nItemHeight; break; | 
 | 	case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break; | 
 | 	case VK_LEFT:  Destination.x -= infoPtr->nItemWidth; break; | 
 | 	case VK_HOME:  Destination.x = Destination.y = 0; break; | 
 | 	case VK_NEXT:  Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break; | 
 | 	case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break; | 
 | 	case VK_END: | 
 | 	    LISTVIEW_GetAreaRect(infoPtr, &rcArea); | 
 | 	    Destination.x = rcArea.right;  | 
 | 	    Destination.y = rcArea.bottom;  | 
 | 	    break; | 
 | 	default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection); | 
 | 	} | 
 | 	bNearest = TRUE; | 
 |     } | 
 |     else Destination.x = Destination.y = 0; | 
 |  | 
 |     /* if LVFI_PARAM is specified, all other flags are ignored */ | 
 |     if (lpFindInfo->flags & LVFI_PARAM) | 
 |     { | 
 |         lvItem.mask |= LVIF_PARAM; | 
 | 	bNearest = FALSE; | 
 | 	lvItem.mask &= ~LVIF_TEXT; | 
 |     } | 
 |  | 
 | again: | 
 |     for (; nItem < nLast; nItem++) | 
 |     { | 
 |         lvItem.iItem = nItem; | 
 |         lvItem.iSubItem = 0; | 
 |         if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue; | 
 |  | 
 | 	if (lvItem.mask & LVIF_PARAM) | 
 |         { | 
 |             if (lpFindInfo->lParam == lvItem.lParam) | 
 |                 return nItem; | 
 |             else | 
 |                 continue; | 
 |         } | 
 | 	 | 
 |         if (lvItem.mask & LVIF_TEXT) | 
 | 	{ | 
 |             if (lpFindInfo->flags & LVFI_PARTIAL) | 
 |             { | 
 |             	if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue; | 
 |             } | 
 |             else | 
 |             { | 
 |             	if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue; | 
 |             } | 
 | 	} | 
 |  | 
 |         if (!bNearest) return nItem; | 
 | 	 | 
 | 	/* This is very inefficient. To do a good job here, | 
 | 	 * we need a sorted array of (x,y) item positions */ | 
 | 	LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position); | 
 |  | 
 | 	/* compute the distance^2 to the destination */ | 
 | 	xdist = Destination.x - Position.x; | 
 | 	ydist = Destination.y - Position.y; | 
 | 	dist = xdist * xdist + ydist * ydist; | 
 |  | 
 | 	/* remember the distance, and item if it's closer */ | 
 | 	if (dist < mindist) | 
 | 	{ | 
 | 	    mindist = dist; | 
 | 	    nNearestItem = nItem; | 
 | 	} | 
 |     } | 
 |  | 
 |     if (bWrap) | 
 |     { | 
 |         nItem = 0; | 
 |         nLast = min(nStart + 1, infoPtr->nItemCount); | 
 |         bWrap = FALSE; | 
 | 	goto again; | 
 |     } | 
 |  | 
 |     return nNearestItem; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Searches for an item with specific characteristics. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] hwnd : window handle | 
 |  * [I] nStart : base item index | 
 |  * [I] lpFindInfo : item information to look for | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : index of item | 
 |  *   FAILURE : -1 | 
 |  */ | 
 | static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart, | 
 |                               const LVFINDINFOA *lpFindInfo) | 
 | { | 
 |     BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL); | 
 |     LVFINDINFOW fiw; | 
 |     INT res; | 
 |  | 
 |     memcpy(&fiw, lpFindInfo, sizeof(fiw)); | 
 |     if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE); | 
 |     res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw); | 
 |     if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE); | 
 |     return res; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Retrieves the background image of the listview control. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [O] lpBkImage : background image attributes | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage)   */ | 
 | /* {   */ | 
 | /*   FIXME (listview, "empty stub!\n"); */ | 
 | /*   return FALSE;   */ | 
 | /* }   */ | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Retrieves column attributes. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nColumn :  column index | 
 |  * [IO] lpColumn : column information | 
 |  * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW | 
 |  *           otherwise it is in fact a LPLVCOLUMNA | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW) | 
 | { | 
 |     COLUMN_INFO *lpColumnInfo; | 
 |     HDITEMW hdi; | 
 |  | 
 |     if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE; | 
 |     lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn); | 
 |  | 
 |     /* initialize memory */ | 
 |     ZeroMemory(&hdi, sizeof(hdi)); | 
 |  | 
 |     if (lpColumn->mask & LVCF_TEXT) | 
 |     { | 
 |         hdi.mask |= HDI_TEXT; | 
 |         hdi.pszText = lpColumn->pszText; | 
 |         hdi.cchTextMax = lpColumn->cchTextMax; | 
 |     } | 
 |  | 
 |     if (lpColumn->mask & LVCF_IMAGE) | 
 |         hdi.mask |= HDI_IMAGE; | 
 |  | 
 |     if (lpColumn->mask & LVCF_ORDER) | 
 |         hdi.mask |= HDI_ORDER; | 
 |  | 
 |     if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE; | 
 |  | 
 |     if (lpColumn->mask & LVCF_FMT) | 
 | 	lpColumn->fmt = lpColumnInfo->fmt; | 
 |  | 
 |     if (lpColumn->mask & LVCF_WIDTH) | 
 |         lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left; | 
 |  | 
 |     if (lpColumn->mask & LVCF_IMAGE) | 
 | 	lpColumn->iImage = hdi.iImage; | 
 |  | 
 |     if (lpColumn->mask & LVCF_ORDER) | 
 | 	lpColumn->iOrder = hdi.iOrder; | 
 |  | 
 |     return TRUE; | 
 | } | 
 |  | 
 |  | 
 | static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray) | 
 | { | 
 |     INT i; | 
 |  | 
 |     if (!lpiArray) | 
 | 	return FALSE; | 
 |  | 
 |     /* FIXME: little hack */ | 
 |     for (i = 0; i < iCount; i++) | 
 | 	lpiArray[i] = i; | 
 |  | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Retrieves the column width. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] int : column index | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : column width | 
 |  *   FAILURE : zero | 
 |  */ | 
 | static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn) | 
 | { | 
 |     INT nColumnWidth = 0; | 
 |     RECT rcHeader; | 
 |  | 
 |     TRACE("nColumn=%d\n", nColumn); | 
 |  | 
 |     /* we have a 'column' in LIST and REPORT mode only */ | 
 |     switch(infoPtr->dwStyle & LVS_TYPEMASK) | 
 |     { | 
 |     case LVS_LIST: | 
 | 	nColumnWidth = infoPtr->nItemWidth; | 
 | 	break; | 
 |     case LVS_REPORT: | 
 | 	if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0; | 
 | 	LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader); | 
 | 	nColumnWidth = rcHeader.right - rcHeader.left; | 
 | 	break; | 
 |     } | 
 |  | 
 |     TRACE("nColumnWidth=%d\n", nColumnWidth); | 
 |     return nColumnWidth; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * In list or report display mode, retrieves the number of items that can fit | 
 |  * vertically in the visible area. In icon or small icon display mode, | 
 |  * retrieves the total number of visible items. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * | 
 |  * RETURN: | 
 |  * Number of fully visible items. | 
 |  */ | 
 | static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr) | 
 | { | 
 |     switch (infoPtr->dwStyle & LVS_TYPEMASK) | 
 |     { | 
 |     case LVS_ICON: | 
 |     case LVS_SMALLICON: | 
 | 	return infoPtr->nItemCount; | 
 |     case LVS_REPORT: | 
 | 	return LISTVIEW_GetCountPerColumn(infoPtr); | 
 |     case LVS_LIST: | 
 | 	return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr); | 
 |     } | 
 |     assert(FALSE); | 
 |     return 0; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Retrieves an image list handle. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nImageList : image list identifier | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : image list handle | 
 |  *   FAILURE : NULL | 
 |  */ | 
 | static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList) | 
 | { | 
 |     switch (nImageList) | 
 |     { | 
 |     case LVSIL_NORMAL: return infoPtr->himlNormal; | 
 |     case LVSIL_SMALL: return infoPtr->himlSmall; | 
 |     case LVSIL_STATE: return infoPtr->himlState; | 
 |     } | 
 |     return NULL; | 
 | } | 
 |  | 
 | /* LISTVIEW_GetISearchString */ | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Retrieves item attributes. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] hwnd : window handle | 
 |  * [IO] lpLVItem : item info | 
 |  * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW, | 
 |  *           if FALSE, the lpLVItem is a LPLVITEMA. | 
 |  * | 
 |  * NOTE: | 
 |  *   This is the internal 'GetItem' interface -- it tries to | 
 |  *   be smart and avoid text copies, if possible, by modifying | 
 |  *   lpLVItem->pszText to point to the text string. Please note | 
 |  *   that this is not always possible (e.g. OWNERDATA), so on | 
 |  *   entry you *must* supply valid values for pszText, and cchTextMax. | 
 |  *   The only difference to the documented interface is that upon | 
 |  *   return, you should use *only* the lpLVItem->pszText, rather than | 
 |  *   the buffer pointer you provided on input. Most code already does | 
 |  *   that, so it's not a problem. | 
 |  *   For the two cases when the text must be copied (that is, | 
 |  *   for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT. | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW) | 
 | { | 
 |     ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK }; | 
 |     NMLVDISPINFOW dispInfo; | 
 |     ITEM_INFO *lpItem; | 
 |     ITEMHDR* pItemHdr; | 
 |     HDPA hdpaSubItems; | 
 |     INT isubitem; | 
 |  | 
 |     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW); | 
 |  | 
 |     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount) | 
 | 	return FALSE; | 
 |  | 
 |     if (lpLVItem->mask == 0) return TRUE; | 
 |  | 
 |     /* make a local copy */ | 
 |     isubitem = lpLVItem->iSubItem; | 
 |  | 
 |     /* a quick optimization if all we're asked is the focus state | 
 |      * these queries are worth optimising since they are common, | 
 |      * and can be answered in constant time, without the heavy accesses */ | 
 |     if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) && | 
 | 	 !(infoPtr->uCallbackMask & LVIS_FOCUSED) ) | 
 |     { | 
 | 	lpLVItem->state = 0; | 
 | 	if (infoPtr->nFocusedItem == lpLVItem->iItem) | 
 | 	    lpLVItem->state |= LVIS_FOCUSED; | 
 | 	return TRUE; | 
 |     } | 
 |  | 
 |     ZeroMemory(&dispInfo, sizeof(dispInfo)); | 
 |  | 
 |     /* if the app stores all the data, handle it separately */ | 
 |     if (infoPtr->dwStyle & LVS_OWNERDATA) | 
 |     { | 
 | 	dispInfo.item.state = 0; | 
 |  | 
 | 	/* apprently, we should not callback for lParam in LVS_OWNERDATA */ | 
 | 	if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask) | 
 | 	{ | 
 | 	    /* NOTE: copy only fields which we _know_ are initialized, some apps | 
 | 	     *       depend on the uninitialized fields being 0 */ | 
 | 	    dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM; | 
 | 	    dispInfo.item.iItem = lpLVItem->iItem; | 
 | 	    dispInfo.item.iSubItem = isubitem; | 
 | 	    if (lpLVItem->mask & LVIF_TEXT) | 
 | 	    { | 
 | 		dispInfo.item.pszText = lpLVItem->pszText; | 
 | 		dispInfo.item.cchTextMax = lpLVItem->cchTextMax;		 | 
 | 	    } | 
 | 	    if (lpLVItem->mask & LVIF_STATE) | 
 | 	        dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask; | 
 | 	    notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW); | 
 | 	    dispInfo.item.stateMask = lpLVItem->stateMask; | 
 | 	    if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS)) | 
 | 	    { | 
 | 	        /* full size structure expected - _WIN32IE >= 0x560 */ | 
 | 	        *lpLVItem = dispInfo.item; | 
 | 	    } | 
 | 	    else if (lpLVItem->mask & LVIF_INDENT) | 
 | 	    { | 
 | 	        /* indent member expected - _WIN32IE >= 0x300 */ | 
 | 	        memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId )); | 
 | 	    } | 
 | 	    else | 
 | 	    { | 
 | 	        /* minimal structure expected */ | 
 | 	        memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent )); | 
 | 	    } | 
 | 	    TRACE("   getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW)); | 
 | 	} | 
 | 	 | 
 | 	/* make sure lParam is zeroed out */ | 
 | 	if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0; | 
 | 	 | 
 | 	/* we store only a little state, so if we're not asked, we're done */ | 
 | 	if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE; | 
 |  | 
 | 	/* if focus is handled by us, report it */ | 
 | 	if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )  | 
 | 	{ | 
 | 	    lpLVItem->state &= ~LVIS_FOCUSED; | 
 | 	    if (infoPtr->nFocusedItem == lpLVItem->iItem) | 
 | 	        lpLVItem->state |= LVIS_FOCUSED; | 
 |         } | 
 |  | 
 | 	/* and do the same for selection, if we handle it */ | 
 | 	if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )  | 
 | 	{ | 
 | 	    lpLVItem->state &= ~LVIS_SELECTED; | 
 | 	    if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem)) | 
 | 		lpLVItem->state |= LVIS_SELECTED; | 
 | 	} | 
 | 	 | 
 | 	return TRUE; | 
 |     } | 
 |  | 
 |     /* find the item and subitem structures before we proceed */ | 
 |     hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem); | 
 |     lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0); | 
 |     assert (lpItem); | 
 |  | 
 |     if (isubitem) | 
 |     { | 
 | 	SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem); | 
 |         pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr; | 
 |         if (!lpSubItem) | 
 |         { | 
 |             WARN(" iSubItem invalid (%08x), ignored.\n", isubitem); | 
 |             isubitem = 0; | 
 |         } | 
 |     } | 
 |     else | 
 | 	pItemHdr = &lpItem->hdr; | 
 |  | 
 |     /* Do we need to query the state from the app? */ | 
 |     if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0) | 
 |     { | 
 | 	dispInfo.item.mask |= LVIF_STATE; | 
 | 	dispInfo.item.stateMask = infoPtr->uCallbackMask; | 
 |     } | 
 |    | 
 |     /* Do we need to enquire about the image? */ | 
 |     if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK && | 
 |         (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))) | 
 |     { | 
 | 	dispInfo.item.mask |= LVIF_IMAGE; | 
 |         dispInfo.item.iImage = I_IMAGECALLBACK; | 
 |     } | 
 |  | 
 |     /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */ | 
 |     if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText)) | 
 |     { | 
 | 	dispInfo.item.mask |= LVIF_TEXT; | 
 | 	dispInfo.item.pszText = lpLVItem->pszText; | 
 | 	dispInfo.item.cchTextMax = lpLVItem->cchTextMax; | 
 | 	if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0) | 
 | 	    *dispInfo.item.pszText = '\0'; | 
 |     } | 
 |  | 
 |     /* If we don't have all the requested info, query the application */ | 
 |     if (dispInfo.item.mask != 0) | 
 |     { | 
 | 	dispInfo.item.iItem = lpLVItem->iItem; | 
 | 	dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */ | 
 | 	dispInfo.item.lParam = lpItem->lParam; | 
 | 	notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW); | 
 | 	TRACE("   getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW)); | 
 |     } | 
 |  | 
 |     /* we should not store values for subitems */ | 
 |     if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM; | 
 |  | 
 |     /* Now, handle the iImage field */ | 
 |     if (dispInfo.item.mask & LVIF_IMAGE) | 
 |     { | 
 | 	lpLVItem->iImage = dispInfo.item.iImage; | 
 | 	if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK) | 
 | 	    pItemHdr->iImage = dispInfo.item.iImage; | 
 |     } | 
 |     else if (lpLVItem->mask & LVIF_IMAGE) | 
 |     { | 
 |         if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)) | 
 |             lpLVItem->iImage = pItemHdr->iImage; | 
 |         else | 
 |             lpLVItem->iImage = 0; | 
 |     } | 
 |  | 
 |     /* The pszText field */ | 
 |     if (dispInfo.item.mask & LVIF_TEXT) | 
 |     { | 
 | 	if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText) | 
 | 	    textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW); | 
 |  | 
 | 	lpLVItem->pszText = dispInfo.item.pszText; | 
 |     } | 
 |     else if (lpLVItem->mask & LVIF_TEXT) | 
 |     { | 
 | 	if (isW) lpLVItem->pszText = pItemHdr->pszText; | 
 | 	else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax); | 
 |     } | 
 |  | 
 |     /* if this is a subitem, we're done */ | 
 |     if (isubitem) return TRUE; | 
 |    | 
 |     /* Next is the lParam field */ | 
 |     if (dispInfo.item.mask & LVIF_PARAM) | 
 |     { | 
 | 	lpLVItem->lParam = dispInfo.item.lParam; | 
 | 	if ((dispInfo.item.mask & LVIF_DI_SETITEM)) | 
 | 	    lpItem->lParam = dispInfo.item.lParam; | 
 |     } | 
 |     else if (lpLVItem->mask & LVIF_PARAM) | 
 | 	lpLVItem->lParam = lpItem->lParam; | 
 |  | 
 |     /* ... the state field (this one is different due to uCallbackmask) */ | 
 |     if (lpLVItem->mask & LVIF_STATE)  | 
 |     { | 
 | 	lpLVItem->state = lpItem->state; | 
 | 	if (dispInfo.item.mask & LVIF_STATE) | 
 | 	{ | 
 | 	    lpLVItem->state &= ~dispInfo.item.stateMask; | 
 | 	    lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask); | 
 | 	} | 
 | 	if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )  | 
 | 	{ | 
 | 	    lpLVItem->state &= ~LVIS_FOCUSED; | 
 | 	    if (infoPtr->nFocusedItem == lpLVItem->iItem) | 
 | 	        lpLVItem->state |= LVIS_FOCUSED; | 
 |         } | 
 | 	if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )  | 
 | 	{ | 
 | 	    lpLVItem->state &= ~LVIS_SELECTED; | 
 | 	    if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem)) | 
 | 		lpLVItem->state |= LVIS_SELECTED; | 
 | 	}	     | 
 |     } | 
 |  | 
 |     /* and last, but not least, the indent field */ | 
 |     if (lpLVItem->mask & LVIF_INDENT) | 
 | 	lpLVItem->iIndent = lpItem->iIndent; | 
 |  | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Retrieves item attributes. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] hwnd : window handle | 
 |  * [IO] lpLVItem : item info | 
 |  * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW, | 
 |  *           if FALSE, the lpLVItem is a LPLVITEMA. | 
 |  * | 
 |  * NOTE: | 
 |  *   This is the external 'GetItem' interface -- it properly copies | 
 |  *   the text in the provided buffer. | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW) | 
 | { | 
 |     LPWSTR pszText; | 
 |     BOOL bResult; | 
 |  | 
 |     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount) | 
 | 	return FALSE; | 
 |  | 
 |     pszText = lpLVItem->pszText; | 
 |     bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW); | 
 |     if (bResult && lpLVItem->pszText != pszText) | 
 | 	textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax); | 
 |     lpLVItem->pszText = pszText; | 
 |  | 
 |     return bResult; | 
 | } | 
 |  | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Retrieves the position (upper-left) of the listview control item. | 
 |  * Note that for LVS_ICON style, the upper-left is that of the icon | 
 |  * and not the bounding box. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nItem : item index | 
 |  * [O] lpptPosition : coordinate information | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     POINT Origin; | 
 |  | 
 |     TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition); | 
 |  | 
 |     if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE; | 
 |  | 
 |     LISTVIEW_GetOrigin(infoPtr, &Origin); | 
 |     LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition); | 
 |  | 
 |     if (uView == LVS_ICON) | 
 |     { | 
 |         lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2; | 
 |         lpptPosition->y += ICON_TOP_PADDING; | 
 |     } | 
 |     lpptPosition->x += Origin.x; | 
 |     lpptPosition->y += Origin.y; | 
 |      | 
 |     TRACE ("  lpptPosition=%s\n", wine_dbgstr_point(lpptPosition)); | 
 |     return TRUE; | 
 | } | 
 |  | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Retrieves the bounding rectangle for a listview control item. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nItem : item index | 
 |  * [IO] lprc : bounding rectangle coordinates | 
 |  *     lprc->left specifies the portion of the item for which the bounding | 
 |  *     rectangle will be retrieved. | 
 |  * | 
 |  *     LVIR_BOUNDS Returns the bounding rectangle of the entire item, | 
 |  *        including the icon and label. | 
 |  *         * | 
 |  *         * For LVS_ICON | 
 |  *         * Experiment shows that native control returns: | 
 |  *         *  width = min (48, length of text line) | 
 |  *         *    .left = position.x - (width - iconsize.cx)/2 | 
 |  *         *    .right = .left + width | 
 |  *         *  height = #lines of text * ntmHeight + icon height + 8 | 
 |  *         *    .top = position.y - 2 | 
 |  *         *    .bottom = .top + height | 
 |  *         *  separation between items .y = itemSpacing.cy - height | 
 |  *         *                           .x = itemSpacing.cx - width | 
 |  *     LVIR_ICON Returns the bounding rectangle of the icon or small icon. | 
 |  *         * | 
 |  *         * For LVS_ICON | 
 |  *         * Experiment shows that native control returns: | 
 |  *         *  width = iconSize.cx + 16 | 
 |  *         *    .left = position.x - (width - iconsize.cx)/2 | 
 |  *         *    .right = .left + width | 
 |  *         *  height = iconSize.cy + 4 | 
 |  *         *    .top = position.y - 2 | 
 |  *         *    .bottom = .top + height | 
 |  *         *  separation between items .y = itemSpacing.cy - height | 
 |  *         *                           .x = itemSpacing.cx - width | 
 |  *     LVIR_LABEL Returns the bounding rectangle of the item text. | 
 |  *         * | 
 |  *         * For LVS_ICON | 
 |  *         * Experiment shows that native control returns: | 
 |  *         *  width = text length | 
 |  *         *    .left = position.x - width/2 | 
 |  *         *    .right = .left + width | 
 |  *         *  height = ntmH * linecount + 2 | 
 |  *         *    .top = position.y + iconSize.cy + 6 | 
 |  *         *    .bottom = .top + height | 
 |  *         *  separation between items .y = itemSpacing.cy - height | 
 |  *         *                           .x = itemSpacing.cx - width | 
 |  *     LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL | 
 |  *	rectangles, but excludes columns in report view. | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  * | 
 |  * NOTES | 
 |  *   Note that the bounding rectangle of the label in the LVS_ICON view depends | 
 |  *   upon whether the window has the focus currently and on whether the item | 
 |  *   is the one with the focus.  Ensure that the control's record of which | 
 |  *   item has the focus agrees with the items' records. | 
 |  */ | 
 | static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' }; | 
 |     BOOL doLabel = TRUE, oversizedBox = FALSE; | 
 |     POINT Position, Origin; | 
 |     LVITEMW lvItem; | 
 |     RECT label_rect; | 
 |  | 
 |     TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc); | 
 |  | 
 |     if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE; | 
 |  | 
 |     LISTVIEW_GetOrigin(infoPtr, &Origin); | 
 |     LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position); | 
 |  | 
 |     /* Be smart and try to figure out the minimum we have to do */ | 
 |     if (lprc->left == LVIR_ICON) doLabel = FALSE; | 
 |     if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE; | 
 |     if (uView == LVS_ICON && lprc->left != LVIR_ICON && | 
 | 	infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED)) | 
 | 	oversizedBox = TRUE; | 
 |  | 
 |     /* get what we need from the item before hand, so we make | 
 |      * only one request. This can speed up things, if data | 
 |      * is stored on the app side */ | 
 |     lvItem.mask = 0; | 
 |     if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT; | 
 |     if (doLabel) lvItem.mask |= LVIF_TEXT; | 
 |     lvItem.iItem = nItem; | 
 |     lvItem.iSubItem = 0; | 
 |     lvItem.pszText = szDispText; | 
 |     lvItem.cchTextMax = DISP_TEXT_SIZE; | 
 |     if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE; | 
 |     /* we got the state already up, simulate it here, to avoid a reget */ | 
 |     if (uView == LVS_ICON && (lprc->left != LVIR_ICON)) | 
 |     { | 
 | 	lvItem.mask |= LVIF_STATE; | 
 | 	lvItem.stateMask = LVIS_FOCUSED; | 
 | 	lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0); | 
 |     } | 
 |  | 
 |     if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS) | 
 | 	lprc->left = LVIR_BOUNDS; | 
 |     switch(lprc->left) | 
 |     { | 
 |     case LVIR_ICON: | 
 | 	LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL); | 
 |         break; | 
 |  | 
 |     case LVIR_LABEL: | 
 | 	LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc); | 
 |         break; | 
 |  | 
 |     case LVIR_BOUNDS: | 
 | 	LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL); | 
 |         break; | 
 |  | 
 |     case LVIR_SELECTBOUNDS: | 
 | 	LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect); | 
 | 	UnionRect(lprc, lprc, &label_rect); | 
 |         break; | 
 |  | 
 |     default: | 
 | 	WARN("Unknown value: %ld\n", lprc->left); | 
 | 	return FALSE; | 
 |     } | 
 |  | 
 |     OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y); | 
 |  | 
 |     TRACE(" rect=%s\n", wine_dbgstr_rect(lprc)); | 
 |  | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Retrieves the spacing between listview control items. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [IO] lprc : rectangle to receive the output | 
 |  *             on input, lprc->top = nSubItem | 
 |  *                       lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL | 
 |  *  | 
 |  * NOTE: for subItem = 0, we should return the bounds of the _entire_ item, | 
 |  *       not only those of the first column. | 
 |  *       Fortunately, LISTVIEW_GetItemMetrics does the right thing. | 
 |  *  | 
 |  * RETURN: | 
 |  *     TRUE: success | 
 |  *     FALSE: failure | 
 |  */ | 
 | static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc) | 
 | { | 
 |     POINT Position; | 
 |     LVITEMW lvItem; | 
 |     INT nColumn = lprc->top; | 
 |      | 
 |     if (!lprc) return FALSE; | 
 |  | 
 |     TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top); | 
 |     /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */ | 
 |     if (lprc->top == 0) | 
 |         return LISTVIEW_GetItemRect(infoPtr, nItem, lprc); | 
 |  | 
 |     if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE; | 
 |  | 
 |     if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE; | 
 |  | 
 |     if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE; | 
 |  | 
 |     lvItem.mask = 0; | 
 |     lvItem.iItem = nItem; | 
 |     lvItem.iSubItem = nColumn; | 
 |      | 
 |     if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE; | 
 |     switch(lprc->left) | 
 |     { | 
 |     case LVIR_ICON: | 
 | 	LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL); | 
 |         break; | 
 |  | 
 |     case LVIR_LABEL: | 
 |     case LVIR_BOUNDS: | 
 | 	LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL); | 
 |         break; | 
 |  | 
 |     default: | 
 | 	ERR("Unknown bounds=%ld\n", lprc->left); | 
 | 	return FALSE; | 
 |     } | 
 |  | 
 |     OffsetRect(lprc, Position.x, Position.y); | 
 |     return TRUE; | 
 | } | 
 |  | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Retrieves the width of a label. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : string width (in pixels) | 
 |  *   FAILURE : zero | 
 |  */ | 
 | static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem) | 
 | { | 
 |     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' }; | 
 |     LVITEMW lvItem; | 
 |  | 
 |     TRACE("(nItem=%d)\n", nItem); | 
 |  | 
 |     lvItem.mask = LVIF_TEXT; | 
 |     lvItem.iItem = nItem; | 
 |     lvItem.iSubItem = 0; | 
 |     lvItem.pszText = szDispText; | 
 |     lvItem.cchTextMax = DISP_TEXT_SIZE; | 
 |     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0; | 
 |    | 
 |     return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Retrieves the spacing between listview control items. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] bSmall : flag for small or large icon | 
 |  * | 
 |  * RETURN: | 
 |  * Horizontal + vertical spacing | 
 |  */ | 
 | static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall) | 
 | { | 
 |   LONG lResult; | 
 |  | 
 |   if (!bSmall) | 
 |   { | 
 |     lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy); | 
 |   } | 
 |   else | 
 |   { | 
 |     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON) | 
 |       lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING); | 
 |     else | 
 |       lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight); | 
 |   } | 
 |   return lResult; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Retrieves the state of a listview control item. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nItem : item index | 
 |  * [I] uMask : state mask | 
 |  * | 
 |  * RETURN: | 
 |  * State specified by the mask. | 
 |  */ | 
 | static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask) | 
 | { | 
 |     LVITEMW lvItem; | 
 |  | 
 |     if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0; | 
 |  | 
 |     lvItem.iItem = nItem; | 
 |     lvItem.iSubItem = 0; | 
 |     lvItem.mask = LVIF_STATE; | 
 |     lvItem.stateMask = uMask; | 
 |     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0; | 
 |  | 
 |     return lvItem.state & uMask; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Retrieves the text of a listview control item or subitem. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] hwnd : window handle | 
 |  * [I] nItem : item index | 
 |  * [IO] lpLVItem : item information | 
 |  * [I] isW :  TRUE if lpLVItem is Unicode | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : string length | 
 |  *   FAILURE : 0 | 
 |  */ | 
 | static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW) | 
 | { | 
 |     if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0; | 
 |  | 
 |     lpLVItem->mask = LVIF_TEXT; | 
 |     lpLVItem->iItem = nItem; | 
 |     if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0; | 
 |  | 
 |     return textlenT(lpLVItem->pszText, isW); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Searches for an item based on properties + relationships. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nItem : item index | 
 |  * [I] uFlags : relationship flag | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : item index | 
 |  *   FAILURE : -1 | 
 |  */ | 
 | static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     UINT uMask = 0; | 
 |     LVFINDINFOW lvFindInfo; | 
 |     INT nCountPerColumn; | 
 |     INT nCountPerRow; | 
 |     INT i; | 
 |  | 
 |     TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount); | 
 |     if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1; | 
 |  | 
 |     ZeroMemory(&lvFindInfo, sizeof(lvFindInfo)); | 
 |  | 
 |     if (uFlags & LVNI_CUT) | 
 |       uMask |= LVIS_CUT; | 
 |  | 
 |     if (uFlags & LVNI_DROPHILITED) | 
 |       uMask |= LVIS_DROPHILITED; | 
 |  | 
 |     if (uFlags & LVNI_FOCUSED) | 
 |       uMask |= LVIS_FOCUSED; | 
 |  | 
 |     if (uFlags & LVNI_SELECTED) | 
 |       uMask |= LVIS_SELECTED; | 
 |  | 
 |     /* if we're asked for the focused item, that's only one,  | 
 |      * so it's worth optimizing */ | 
 |     if (uFlags & LVNI_FOCUSED) | 
 |     { | 
 | 	if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1; | 
 | 	return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem; | 
 |     } | 
 |      | 
 |     if (uFlags & LVNI_ABOVE) | 
 |     { | 
 |       if ((uView == LVS_LIST) || (uView == LVS_REPORT)) | 
 |       { | 
 |         while (nItem >= 0) | 
 |         { | 
 |           nItem--; | 
 |           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask) | 
 |             return nItem; | 
 |         } | 
 |       } | 
 |       else | 
 |       { | 
 |         /* Special case for autoarrange - move 'til the top of a list */ | 
 |         if (is_autoarrange(infoPtr)) | 
 |         { | 
 |           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr); | 
 |           while (nItem - nCountPerRow >= 0) | 
 |           { | 
 |             nItem -= nCountPerRow; | 
 |             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) | 
 |               return nItem; | 
 |           } | 
 |           return -1; | 
 |         } | 
 |         lvFindInfo.flags = LVFI_NEARESTXY; | 
 |         lvFindInfo.vkDirection = VK_UP; | 
 |         ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt); | 
 |         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1) | 
 |         { | 
 |           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask) | 
 |             return nItem; | 
 |         } | 
 |       } | 
 |     } | 
 |     else if (uFlags & LVNI_BELOW) | 
 |     { | 
 |       if ((uView == LVS_LIST) || (uView == LVS_REPORT)) | 
 |       { | 
 |         while (nItem < infoPtr->nItemCount) | 
 |         { | 
 |           nItem++; | 
 |           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) | 
 |             return nItem; | 
 |         } | 
 |       } | 
 |       else | 
 |       { | 
 |         /* Special case for autoarrange - move 'til the bottom of a list */ | 
 |         if (is_autoarrange(infoPtr)) | 
 |         { | 
 |           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr); | 
 |           while (nItem + nCountPerRow < infoPtr->nItemCount ) | 
 |           { | 
 |             nItem += nCountPerRow; | 
 |             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) | 
 |               return nItem; | 
 |           } | 
 |           return -1; | 
 |         } | 
 |         lvFindInfo.flags = LVFI_NEARESTXY; | 
 |         lvFindInfo.vkDirection = VK_DOWN; | 
 |         ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt); | 
 |         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1) | 
 |         { | 
 |           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) | 
 |             return nItem; | 
 |         } | 
 |       } | 
 |     } | 
 |     else if (uFlags & LVNI_TOLEFT) | 
 |     { | 
 |       if (uView == LVS_LIST) | 
 |       { | 
 |         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr); | 
 |         while (nItem - nCountPerColumn >= 0) | 
 |         { | 
 |           nItem -= nCountPerColumn; | 
 |           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) | 
 |             return nItem; | 
 |         } | 
 |       } | 
 |       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON)) | 
 |       { | 
 |         /* Special case for autoarrange - move 'ti the beginning of a row */ | 
 |         if (is_autoarrange(infoPtr)) | 
 |         { | 
 |           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr); | 
 |           while (nItem % nCountPerRow > 0) | 
 |           { | 
 |             nItem --; | 
 |             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) | 
 |               return nItem; | 
 |           } | 
 |           return -1; | 
 |         } | 
 |         lvFindInfo.flags = LVFI_NEARESTXY; | 
 |         lvFindInfo.vkDirection = VK_LEFT; | 
 |         ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt); | 
 |         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1) | 
 |         { | 
 |           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask) | 
 |             return nItem; | 
 |         } | 
 |       } | 
 |     } | 
 |     else if (uFlags & LVNI_TORIGHT) | 
 |     { | 
 |       if (uView == LVS_LIST) | 
 |       { | 
 |         nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr); | 
 |         while (nItem + nCountPerColumn < infoPtr->nItemCount) | 
 |         { | 
 |           nItem += nCountPerColumn; | 
 |           if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask) | 
 |             return nItem; | 
 |         } | 
 |       } | 
 |       else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON)) | 
 |       { | 
 |         /* Special case for autoarrange - move 'til the end of a row */ | 
 |         if (is_autoarrange(infoPtr)) | 
 |         { | 
 |           nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr); | 
 |           while (nItem % nCountPerRow < nCountPerRow - 1 ) | 
 |           { | 
 |             nItem ++; | 
 |             if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) | 
 |               return nItem; | 
 |           } | 
 |           return -1; | 
 |         } | 
 |         lvFindInfo.flags = LVFI_NEARESTXY; | 
 |         lvFindInfo.vkDirection = VK_RIGHT; | 
 |         ListView_GetItemPosition(infoPtr->hwndSelf, nItem, &lvFindInfo.pt); | 
 |         while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1) | 
 |         { | 
 |           if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask) | 
 |             return nItem; | 
 |         } | 
 |       } | 
 |     } | 
 |     else | 
 |     { | 
 |       nItem++; | 
 |  | 
 |       /* search by index */ | 
 |       for (i = nItem; i < infoPtr->nItemCount; i++) | 
 |       { | 
 |         if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask) | 
 |           return i; | 
 |       } | 
 |     } | 
 |  | 
 |     return -1; | 
 | } | 
 |  | 
 | /* LISTVIEW_GetNumberOfWorkAreas */ | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Retrieves the origin coordinates when in icon or small icon display mode. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [O] lpptOrigin : coordinate information | 
 |  * | 
 |  * RETURN: | 
 |  *   None. | 
 |  */ | 
 | static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     INT nHorzPos = 0, nVertPos = 0; | 
 |     SCROLLINFO scrollInfo; | 
 |  | 
 |     scrollInfo.cbSize = sizeof(SCROLLINFO);     | 
 |     scrollInfo.fMask = SIF_POS; | 
 |      | 
 |     if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) | 
 | 	nHorzPos = scrollInfo.nPos; | 
 |     if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) | 
 | 	nVertPos = scrollInfo.nPos; | 
 |  | 
 |     TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos); | 
 |  | 
 |     lpptOrigin->x = infoPtr->rcList.left; | 
 |     lpptOrigin->y = infoPtr->rcList.top; | 
 |     if (uView == LVS_LIST) | 
 | 	nHorzPos *= infoPtr->nItemWidth; | 
 |     else if (uView == LVS_REPORT) | 
 | 	nVertPos *= infoPtr->nItemHeight; | 
 |      | 
 |     lpptOrigin->x -= nHorzPos; | 
 |     lpptOrigin->y -= nVertPos; | 
 |  | 
 |     TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin)); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Retrieves the width of a string. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] hwnd : window handle | 
 |  * [I] lpszText : text string to process | 
 |  * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : string width (in pixels) | 
 |  *   FAILURE : zero | 
 |  */ | 
 | static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW) | 
 | { | 
 |     SIZE stringSize; | 
 |      | 
 |     stringSize.cx = 0;     | 
 |     if (is_textT(lpszText, isW)) | 
 |     { | 
 |     	HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont; | 
 |     	HDC hdc = GetDC(infoPtr->hwndSelf); | 
 |     	HFONT hOldFont = SelectObject(hdc, hFont); | 
 |  | 
 |     	if (isW) | 
 |   	    GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize); | 
 |     	else | 
 |   	    GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize); | 
 |     	SelectObject(hdc, hOldFont); | 
 |     	ReleaseDC(infoPtr->hwndSelf, hdc); | 
 |     } | 
 |     return stringSize.cx; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Determines which listview item is located at the specified position. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [IO] lpht : hit test information | 
 |  * [I] subitem : fill out iSubItem. | 
 |  * [I] select : return the index only if the hit selects the item | 
 |  * | 
 |  * NOTE: | 
 |  * (mm 20001022): We must not allow iSubItem to be touched, for | 
 |  * an app might pass only a structure with space up to iItem! | 
 |  * (MS Office 97 does that for instance in the file open dialog) | 
 |  *  | 
 |  * RETURN: | 
 |  *   SUCCESS : item index | 
 |  *   FAILURE : -1 | 
 |  */ | 
 | static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select) | 
 | { | 
 |     WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' }; | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch; | 
 |     POINT Origin, Position, opt; | 
 |     LVITEMW lvItem; | 
 |     ITERATOR i; | 
 |     INT iItem; | 
 |      | 
 |     TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select); | 
 |      | 
 |     lpht->flags = 0; | 
 |     lpht->iItem = -1; | 
 |     if (subitem) lpht->iSubItem = 0; | 
 |  | 
 |     if (infoPtr->rcList.left > lpht->pt.x) | 
 | 	lpht->flags |= LVHT_TOLEFT; | 
 |     else if (infoPtr->rcList.right < lpht->pt.x) | 
 | 	lpht->flags |= LVHT_TORIGHT; | 
 |      | 
 |     if (infoPtr->rcList.top > lpht->pt.y) | 
 | 	lpht->flags |= LVHT_ABOVE; | 
 |     else if (infoPtr->rcList.bottom < lpht->pt.y) | 
 | 	lpht->flags |= LVHT_BELOW; | 
 |  | 
 |     TRACE("lpht->flags=0x%x\n", lpht->flags); | 
 |     if (lpht->flags) return -1; | 
 |  | 
 |     lpht->flags |= LVHT_NOWHERE; | 
 |  | 
 |     LISTVIEW_GetOrigin(infoPtr, &Origin); | 
 |     | 
 |     /* first deal with the large items */ | 
 |     rcSearch.left = lpht->pt.x; | 
 |     rcSearch.top = lpht->pt.y; | 
 |     rcSearch.right = rcSearch.left + 1; | 
 |     rcSearch.bottom = rcSearch.top + 1; | 
 |      | 
 |     iterator_frameditems(&i, infoPtr, &rcSearch); | 
 |     iterator_next(&i); /* go to first item in the sequence */ | 
 |     iItem = i.nItem; | 
 |     iterator_destroy(&i); | 
 |     | 
 |     TRACE("lpht->iItem=%d\n", iItem);  | 
 |     if (iItem == -1) return -1; | 
 |  | 
 |     lvItem.mask = LVIF_STATE | LVIF_TEXT; | 
 |     if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT; | 
 |     lvItem.stateMask = LVIS_STATEIMAGEMASK; | 
 |     if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED; | 
 |     lvItem.iItem = iItem; | 
 |     lvItem.iSubItem = 0; | 
 |     lvItem.pszText = szDispText; | 
 |     lvItem.cchTextMax = DISP_TEXT_SIZE; | 
 |     if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1; | 
 |     if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;  | 
 |      | 
 |     LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel); | 
 |     LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position); | 
 |     opt.x = lpht->pt.x - Position.x - Origin.x; | 
 |     opt.y = lpht->pt.y - Position.y - Origin.y; | 
 |      | 
 |     if (uView == LVS_REPORT) | 
 | 	rcBounds = rcBox; | 
 |     else | 
 | 	UnionRect(&rcBounds, &rcIcon, &rcLabel); | 
 |     TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds)); | 
 |     if (!PtInRect(&rcBounds, opt)) return -1; | 
 |  | 
 |     if (PtInRect(&rcIcon, opt)) | 
 | 	lpht->flags |= LVHT_ONITEMICON; | 
 |     else if (PtInRect(&rcLabel, opt)) | 
 | 	lpht->flags |= LVHT_ONITEMLABEL; | 
 |     else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt)) | 
 | 	lpht->flags |= LVHT_ONITEMSTATEICON; | 
 |     if (lpht->flags & LVHT_ONITEM) | 
 | 	lpht->flags &= ~LVHT_NOWHERE; | 
 |     | 
 |     TRACE("lpht->flags=0x%x\n", lpht->flags);  | 
 |     if (uView == LVS_REPORT && subitem) | 
 |     { | 
 |   	INT j; | 
 |  | 
 |         rcBounds.right = rcBounds.left; | 
 |         for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++) | 
 |         { | 
 | 	    rcBounds.left = rcBounds.right; | 
 | 	    rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j); | 
 | 	    if (PtInRect(&rcBounds, opt)) | 
 | 	    { | 
 | 		lpht->iSubItem = j; | 
 | 		break; | 
 | 	    } | 
 | 	} | 
 |     } | 
 |  | 
 |     if (select && !(uView == LVS_REPORT && | 
 |                     ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || | 
 |                      (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)))) | 
 |     { | 
 |         if (uView == LVS_REPORT) | 
 |         { | 
 |             UnionRect(&rcBounds, &rcIcon, &rcLabel); | 
 |             UnionRect(&rcBounds, &rcBounds, &rcState); | 
 |         } | 
 |         if (!PtInRect(&rcBounds, opt)) iItem = -1; | 
 |     } | 
 |     return lpht->iItem = iItem; | 
 | } | 
 |  | 
 |  | 
 | /* LISTVIEW_InsertCompare:  callback routine for comparing pszText members of the LV_ITEMS | 
 |    in a LISTVIEW on insert.  Passed to DPA_Sort in LISTVIEW_InsertItem. | 
 |    This function should only be used for inserting items into a sorted list (LVM_INSERTITEM) | 
 |    and not during the processing of a LVM_SORTITEMS message. Applications should provide | 
 |    their own sort proc. when sending LVM_SORTITEMS. | 
 | */ | 
 | /* Platform SDK: | 
 |     (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion... | 
 |         if: | 
 |           LVS_SORTXXX must be specified, | 
 |           LVS_OWNERDRAW is not set, | 
 |           <item>.pszText is not LPSTR_TEXTCALLBACK. | 
 |  | 
 |     (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices | 
 |     are sorted based on item text..." | 
 | */ | 
 | static INT WINAPI LISTVIEW_InsertCompare(  LPVOID first, LPVOID second,  LPARAM lParam) | 
 | { | 
 |     ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 ); | 
 |     ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 ); | 
 |     INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);  | 
 |  | 
 |     /* if we're sorting descending, negate the return value */ | 
 |     return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Inserts a new item in the listview control. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] lpLVItem : item information | 
 |  * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : new item index | 
 |  *   FAILURE : -1 | 
 |  */ | 
 | static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     INT nItem; | 
 |     HDPA hdpaSubItems; | 
 |     NMLISTVIEW nmlv; | 
 |     ITEM_INFO *lpItem; | 
 |     BOOL is_sorted, has_changed; | 
 |     LVITEMW item; | 
 |     HWND hwndSelf = infoPtr->hwndSelf; | 
 |  | 
 |     TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW); | 
 |  | 
 |     if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++; | 
 |  | 
 |     /* make sure it's an item, and not a subitem; cannot insert a subitem */ | 
 |     if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iSubItem) return -1; | 
 |  | 
 |     if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1; | 
 |  | 
 |     if (!(lpItem = (ITEM_INFO *)Alloc(sizeof(ITEM_INFO)))) return -1; | 
 |      | 
 |     /* insert item in listview control data structure */ | 
 |     if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail; | 
 |     if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE); | 
 |  | 
 |     is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) && | 
 | 	        !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText); | 
 |  | 
 |     nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount); | 
 |     TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem); | 
 |     nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems ); | 
 |     if (nItem == -1) goto fail; | 
 |     infoPtr->nItemCount++; | 
 |  | 
 |     /* shift indices first so they don't get tangled */ | 
 |     LISTVIEW_ShiftIndices(infoPtr, nItem, 1); | 
 |  | 
 |     /* set the item attributes */ | 
 |     if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS)) | 
 |     { | 
 |         /* full size structure expected - _WIN32IE >= 0x560 */ | 
 |         item = *lpLVItem; | 
 |     } | 
 |     else if (lpLVItem->mask & LVIF_INDENT) | 
 |     { | 
 |         /* indent member expected - _WIN32IE >= 0x300 */ | 
 |         memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId )); | 
 |     } | 
 |     else | 
 |     { | 
 |         /* minimal structure expected */ | 
 |         memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent )); | 
 |     } | 
 |     item.iItem = nItem; | 
 |     if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) item.state &= ~LVIS_STATEIMAGEMASK; | 
 |     if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo; | 
 |  | 
 |     /* if we're sorted, sort the list, and update the index */ | 
 |     if (is_sorted) | 
 |     { | 
 | 	DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr ); | 
 | 	nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems ); | 
 | 	assert(nItem != -1); | 
 |     } | 
 |  | 
 |     /* make room for the position, if we are in the right mode */ | 
 |     if ((uView == LVS_SMALLICON) || (uView == LVS_ICON)) | 
 |     { | 
 |         if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1) | 
 | 	    goto undo; | 
 |         if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1) | 
 | 	{ | 
 | 	    DPA_DeletePtr(infoPtr->hdpaPosX, nItem); | 
 | 	    goto undo; | 
 | 	} | 
 |     } | 
 |      | 
 |     /* send LVN_INSERTITEM notification */ | 
 |     ZeroMemory(&nmlv, sizeof(NMLISTVIEW)); | 
 |     nmlv.iItem = nItem; | 
 |     nmlv.lParam = lpItem->lParam; | 
 |     notify_listview(infoPtr, LVN_INSERTITEM, &nmlv); | 
 |     if (!IsWindow(hwndSelf)) | 
 | 	return -1; | 
 |  | 
 |     /* align items (set position of each item) */ | 
 |     if ((uView == LVS_SMALLICON || uView == LVS_ICON)) | 
 |     { | 
 | 	POINT pt; | 
 |  | 
 | 	if (infoPtr->dwStyle & LVS_ALIGNLEFT) | 
 | 	    LISTVIEW_NextIconPosLeft(infoPtr, &pt); | 
 |         else | 
 | 	    LISTVIEW_NextIconPosTop(infoPtr, &pt); | 
 |  | 
 | 	LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE); | 
 |     } | 
 |  | 
 |     /* now is the invalidation fun */ | 
 |     LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1); | 
 |     return nItem; | 
 |  | 
 | undo: | 
 |     LISTVIEW_ShiftIndices(infoPtr, nItem, -1); | 
 |     DPA_DeletePtr(infoPtr->hdpaItems, nItem); | 
 |     infoPtr->nItemCount--; | 
 | fail: | 
 |     DPA_DeletePtr(hdpaSubItems, 0); | 
 |     DPA_Destroy (hdpaSubItems); | 
 |     Free (lpItem); | 
 |     return -1; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Redraws a range of items. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nFirst : first item | 
 |  * [I] nLast : last item | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast) | 
 | { | 
 |     INT i; | 
 |   | 
 |     if (nLast < nFirst || min(nFirst, nLast) < 0 ||  | 
 | 	max(nFirst, nLast) >= infoPtr->nItemCount) | 
 | 	return FALSE; | 
 |      | 
 |     for (i = nFirst; i <= nLast; i++) | 
 | 	LISTVIEW_InvalidateItem(infoPtr, i); | 
 |  | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Scroll the content of a listview. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] dx : horizontal scroll amount in pixels | 
 |  * [I] dy : vertical scroll amount in pixels | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  * | 
 |  * COMMENTS: | 
 |  *  If the control is in report mode (LVS_REPORT) the control can | 
 |  *  be scrolled only in line increments. "dy" will be rounded to the | 
 |  *  nearest number of pixels that are a whole line. Ex: if line height | 
 |  *  is 16 and an 8 is passed, the list will be scrolled by 16. If a 7 | 
 |  *  is passed the the scroll will be 0.  (per MSDN 7/2002) | 
 |  * | 
 |  *  For:  (per experimentaion with native control and CSpy ListView) | 
 |  *     LVS_ICON       dy=1 = 1 pixel  (vertical only) | 
 |  *                    dx ignored | 
 |  *     LVS_SMALLICON  dy=1 = 1 pixel  (vertical only) | 
 |  *                    dx ignored | 
 |  *     LVS_LIST       dx=1 = 1 column (horizontal only) | 
 |  *                           but will only scroll 1 column per message | 
 |  *                           no matter what the value. | 
 |  *                    dy must be 0 or FALSE returned. | 
 |  *     LVS_REPORT     dx=1 = 1 pixel | 
 |  *                    dy=  see above | 
 |  * | 
 |  */ | 
 | static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy) | 
 | { | 
 |     switch(infoPtr->dwStyle & LVS_TYPEMASK) { | 
 |     case LVS_REPORT: | 
 | 	dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2; | 
 |         dy /= infoPtr->nItemHeight; | 
 | 	break; | 
 |     case LVS_LIST: | 
 |     	if (dy != 0) return FALSE; | 
 | 	break; | 
 |     default: /* icon */ | 
 | 	dx = 0; | 
 | 	break; | 
 |     }	 | 
 |  | 
 |     if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0); | 
 |     if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0); | 
 |    | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Sets the background color. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] clrBk : background color | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk) | 
 | { | 
 |     TRACE("(clrBk=%lx)\n", clrBk); | 
 |  | 
 |     if(infoPtr->clrBk != clrBk) { | 
 | 	if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush); | 
 | 	infoPtr->clrBk = clrBk; | 
 | 	if (clrBk == CLR_NONE) | 
 | 	    infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND); | 
 | 	else | 
 | 	    infoPtr->hBkBrush = CreateSolidBrush(clrBk); | 
 | 	LISTVIEW_InvalidateList(infoPtr); | 
 |     } | 
 |  | 
 |    return TRUE; | 
 | } | 
 |  | 
 | /* LISTVIEW_SetBkImage */ | 
 |  | 
 | /*** Helper for {Insert,Set}ColumnT *only* */ | 
 | static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW) | 
 | { | 
 |     if (lpColumn->mask & LVCF_FMT) | 
 |     { | 
 | 	/* format member is valid */ | 
 | 	lphdi->mask |= HDI_FORMAT; | 
 |  | 
 | 	/* set text alignment (leftmost column must be left-aligned) */ | 
 |         if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT) | 
 |             lphdi->fmt |= HDF_LEFT; | 
 |         else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT) | 
 |             lphdi->fmt |= HDF_RIGHT; | 
 |         else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER) | 
 |             lphdi->fmt |= HDF_CENTER; | 
 |  | 
 |         if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT) | 
 |             lphdi->fmt |= HDF_BITMAP_ON_RIGHT; | 
 |  | 
 |         if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES) | 
 |         { | 
 |             lphdi->fmt |= HDF_IMAGE; | 
 |             lphdi->iImage = I_IMAGECALLBACK; | 
 |         } | 
 |     } | 
 |  | 
 |     if (lpColumn->mask & LVCF_WIDTH) | 
 |     { | 
 |         lphdi->mask |= HDI_WIDTH; | 
 |         if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER) | 
 |         { | 
 |             /* make it fill the remainder of the controls width */ | 
 |             RECT rcHeader; | 
 |             INT item_index; | 
 |  | 
 |             for(item_index = 0; item_index < (nColumn - 1); item_index++) | 
 | 	    { | 
 |             	LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader); | 
 | 		lphdi->cxy += rcHeader.right - rcHeader.left; | 
 | 	    } | 
 |  | 
 |             /* retrieve the layout of the header */ | 
 |             GetClientRect(infoPtr->hwndSelf, &rcHeader); | 
 |             TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader)); | 
 |  | 
 |             lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy; | 
 |         } | 
 |         else | 
 |             lphdi->cxy = lpColumn->cx; | 
 |     } | 
 |  | 
 |     if (lpColumn->mask & LVCF_TEXT) | 
 |     { | 
 |         lphdi->mask |= HDI_TEXT | HDI_FORMAT; | 
 |         lphdi->fmt |= HDF_STRING; | 
 |         lphdi->pszText = lpColumn->pszText; | 
 |         lphdi->cchTextMax = textlenT(lpColumn->pszText, isW); | 
 |     } | 
 |  | 
 |     if (lpColumn->mask & LVCF_IMAGE) | 
 |     { | 
 |         lphdi->mask |= HDI_IMAGE; | 
 |         lphdi->iImage = lpColumn->iImage; | 
 |     } | 
 |  | 
 |     if (lpColumn->mask & LVCF_ORDER) | 
 |     { | 
 | 	lphdi->mask |= HDI_ORDER; | 
 | 	lphdi->iOrder = lpColumn->iOrder; | 
 |     } | 
 | } | 
 |  | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Inserts a new column. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nColumn : column index | 
 |  * [I] lpColumn : column information | 
 |  * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : new column index | 
 |  *   FAILURE : -1 | 
 |  */ | 
 | static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, | 
 |                                   const LVCOLUMNW *lpColumn, BOOL isW) | 
 | { | 
 |     COLUMN_INFO *lpColumnInfo; | 
 |     INT nNewColumn; | 
 |     HDITEMW hdi; | 
 |  | 
 |     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW); | 
 |  | 
 |     if (!lpColumn || nColumn < 0) return -1; | 
 |     nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns)); | 
 |      | 
 |     ZeroMemory(&hdi, sizeof(HDITEMW)); | 
 |     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW); | 
 |  | 
 |     /* insert item in header control */ | 
 |     nNewColumn = SendMessageW(infoPtr->hwndHeader,  | 
 | 		              isW ? HDM_INSERTITEMW : HDM_INSERTITEMA, | 
 |                               (WPARAM)nColumn, (LPARAM)&hdi); | 
 |     if (nNewColumn == -1) return -1; | 
 |     if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn); | 
 |     | 
 |     /* create our own column info */  | 
 |     if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail; | 
 |     if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail; | 
 |  | 
 |     if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt; | 
 |     if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail; | 
 |  | 
 |     /* now we have to actually adjust the data */ | 
 |     if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0) | 
 |     { | 
 | 	SUBITEM_INFO *lpSubItem; | 
 | 	HDPA hdpaSubItems; | 
 | 	INT nItem, i; | 
 | 	 | 
 | 	for (nItem = 0; nItem < infoPtr->nItemCount; nItem++) | 
 | 	{ | 
 | 	    hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem); | 
 | 	    for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++) | 
 | 	    { | 
 | 		lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i); | 
 | 		if (lpSubItem->iSubItem >= nNewColumn) | 
 | 		    lpSubItem->iSubItem++; | 
 | 	    } | 
 | 	} | 
 |     } | 
 |  | 
 |     /* make space for the new column */ | 
 |     LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left); | 
 |      | 
 |     return nNewColumn; | 
 |  | 
 | fail: | 
 |     if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0); | 
 |     if (lpColumnInfo) | 
 |     { | 
 | 	DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn); | 
 | 	Free(lpColumnInfo); | 
 |     } | 
 |     return -1; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Sets the attributes of a header item. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nColumn : column index | 
 |  * [I] lpColumn : column attributes | 
 |  * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, | 
 |                                 const LVCOLUMNW *lpColumn, BOOL isW) | 
 | { | 
 |     HDITEMW hdi, hdiget; | 
 |     BOOL bResult; | 
 |  | 
 |     TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW); | 
 |      | 
 |     if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE; | 
 |  | 
 |     ZeroMemory(&hdi, sizeof(HDITEMW)); | 
 |     if (lpColumn->mask & LVCF_FMT) | 
 |     { | 
 |         hdi.mask |= HDI_FORMAT; | 
 |         hdiget.mask = HDI_FORMAT; | 
 |         if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget)) | 
 | 	    hdi.fmt = hdiget.fmt & HDF_STRING; | 
 |     } | 
 |     column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW); | 
 |  | 
 |     /* set header item attributes */ | 
 |     bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi); | 
 |     if (!bResult) return FALSE; | 
 |  | 
 |     if (lpColumn->mask & LVCF_FMT) | 
 |     { | 
 | 	COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn); | 
 | 	int oldFmt = lpColumnInfo->fmt; | 
 | 	 | 
 | 	lpColumnInfo->fmt = lpColumn->fmt; | 
 | 	if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE)) | 
 | 	{ | 
 | 	    UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 | 	    if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn); | 
 | 	} | 
 |     } | 
 |  | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Sets the column order array | 
 |  * | 
 |  * PARAMETERS: | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] iCount : number of elements in column order array | 
 |  * [I] lpiArray : pointer to column order array | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray) | 
 | { | 
 |   FIXME("iCount %d lpiArray %p\n", iCount, lpiArray); | 
 |  | 
 |   if (!lpiArray) | 
 |     return FALSE; | 
 |  | 
 |   return TRUE; | 
 | } | 
 |  | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Sets the width of a column | 
 |  * | 
 |  * PARAMETERS: | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nColumn : column index | 
 |  * [I] cx : column width | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     WCHAR szDispText[DISP_TEXT_SIZE] = { 0 }; | 
 |     INT max_cx = 0; | 
 |     HDITEMW hdi; | 
 |  | 
 |     TRACE("(nColumn=%d, cx=%d\n", nColumn, cx); | 
 |  | 
 |     /* set column width only if in report or list mode */ | 
 |     if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE; | 
 |  | 
 |     /* take care of invalid cx values */ | 
 |     if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE; | 
 |     else if (uView == LVS_LIST && cx < 1) return FALSE; | 
 |  | 
 |     /* resize all columns if in LVS_LIST mode */ | 
 |     if(uView == LVS_LIST)  | 
 |     { | 
 | 	infoPtr->nItemWidth = cx; | 
 | 	LISTVIEW_InvalidateList(infoPtr); | 
 | 	return TRUE; | 
 |     } | 
 |  | 
 |     if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE; | 
 |  | 
 |     if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1)) | 
 |     { | 
 | 	INT nLabelWidth; | 
 | 	LVITEMW lvItem; | 
 |  | 
 | 	lvItem.mask = LVIF_TEXT;	 | 
 | 	lvItem.iItem = 0; | 
 | 	lvItem.iSubItem = nColumn; | 
 | 	lvItem.pszText = szDispText; | 
 | 	lvItem.cchTextMax = DISP_TEXT_SIZE; | 
 | 	for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++) | 
 | 	{ | 
 | 	    if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue; | 
 | 	    nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE); | 
 | 	    if (max_cx < nLabelWidth) max_cx = nLabelWidth; | 
 | 	} | 
 | 	if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE))) | 
 | 	    max_cx += infoPtr->iconSize.cx; | 
 | 	max_cx += TRAILING_LABEL_PADDING; | 
 |     } | 
 |  | 
 |     /* autosize based on listview items width */ | 
 |     if(cx == LVSCW_AUTOSIZE) | 
 | 	cx = max_cx; | 
 |     else if(cx == LVSCW_AUTOSIZE_USEHEADER) | 
 |     { | 
 | 	/* if iCol is the last column make it fill the remainder of the controls width */ | 
 |         if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)  | 
 | 	{ | 
 | 	    RECT rcHeader; | 
 | 	    POINT Origin; | 
 |  | 
 | 	    LISTVIEW_GetOrigin(infoPtr, &Origin); | 
 | 	    LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader); | 
 |  | 
 | 	    cx = infoPtr->rcList.right - Origin.x - rcHeader.left; | 
 | 	} | 
 | 	else | 
 | 	{ | 
 |             /* Despite what the MS docs say, if this is not the last | 
 |                column, then MS resizes the column to the width of the | 
 |                largest text string in the column, including headers | 
 |                and items. This is different from LVSCW_AUTOSIZE in that | 
 | 	       LVSCW_AUTOSIZE ignores the header string length. */ | 
 | 	    cx = 0; | 
 |  | 
 | 	    /* retrieve header text */ | 
 | 	    hdi.mask = HDI_TEXT; | 
 | 	    hdi.cchTextMax = DISP_TEXT_SIZE; | 
 | 	    hdi.pszText = szDispText; | 
 | 	    if (Header_GetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi)) | 
 | 	    { | 
 | 		HDC hdc = GetDC(infoPtr->hwndSelf); | 
 | 		HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0)); | 
 | 		SIZE size; | 
 |  | 
 | 		if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size)) | 
 | 		    cx = size.cx + TRAILING_HEADER_PADDING; | 
 | 		/* FIXME: Take into account the header image, if one is present */ | 
 | 		SelectObject(hdc, old_font); | 
 | 		ReleaseDC(infoPtr->hwndSelf, hdc); | 
 | 	    } | 
 | 	    cx = max (cx, max_cx); | 
 | 	} | 
 |     } | 
 |  | 
 |     if (cx < 0) return FALSE; | 
 |  | 
 |     /* call header to update the column change */ | 
 |     hdi.mask = HDI_WIDTH; | 
 |     hdi.cxy = cx; | 
 |     TRACE("hdi.cxy=%d\n", hdi.cxy); | 
 |     return Header_SetItemW(infoPtr->hwndHeader, nColumn, (LPARAM)&hdi); | 
 | } | 
 |  | 
 | /*** | 
 |  * Creates the checkbox imagelist.  Helper for LISTVIEW_SetExtendedListViewStyle | 
 |  * | 
 |  */ | 
 | static HIMAGELIST LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO *infoPtr) | 
 | { | 
 |     HDC hdc_wnd, hdc; | 
 |     HBITMAP hbm_im, hbm_mask, hbm_orig; | 
 |     RECT rc; | 
 |     HBRUSH hbr_white = GetStockObject(WHITE_BRUSH); | 
 |     HBRUSH hbr_black = GetStockObject(BLACK_BRUSH); | 
 |     HIMAGELIST himl; | 
 |  | 
 |     himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), | 
 |                             ILC_COLOR | ILC_MASK, 2, 2); | 
 |     hdc_wnd = GetDC(infoPtr->hwndSelf); | 
 |     hdc = CreateCompatibleDC(hdc_wnd); | 
 |     hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON)); | 
 |     hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL); | 
 |     ReleaseDC(infoPtr->hwndSelf, hdc_wnd); | 
 |  | 
 |     rc.left = rc.top = 0; | 
 |     rc.right = GetSystemMetrics(SM_CXSMICON); | 
 |     rc.bottom = GetSystemMetrics(SM_CYSMICON); | 
 |  | 
 |     hbm_orig = SelectObject(hdc, hbm_mask); | 
 |     FillRect(hdc, &rc, hbr_white); | 
 |     InflateRect(&rc, -3, -3); | 
 |     FillRect(hdc, &rc, hbr_black); | 
 |  | 
 |     SelectObject(hdc, hbm_im); | 
 |     DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO); | 
 |     SelectObject(hdc, hbm_orig); | 
 |     ImageList_Add(himl, hbm_im, hbm_mask);  | 
 |  | 
 |     SelectObject(hdc, hbm_im); | 
 |     DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED); | 
 |     SelectObject(hdc, hbm_orig); | 
 |     ImageList_Add(himl, hbm_im, hbm_mask); | 
 |  | 
 |     DeleteObject(hbm_mask); | 
 |     DeleteObject(hbm_im); | 
 |     DeleteDC(hdc); | 
 |  | 
 |     return himl; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Sets the extended listview style. | 
 |  * | 
 |  * PARAMETERS: | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] dwMask : mask | 
 |  * [I] dwStyle : style | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : previous style | 
 |  *   FAILURE : 0 | 
 |  */ | 
 | static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle) | 
 | { | 
 |     DWORD dwOldStyle = infoPtr->dwLvExStyle; | 
 |  | 
 |     /* set new style */ | 
 |     if (dwMask) | 
 | 	infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask); | 
 |     else | 
 | 	infoPtr->dwLvExStyle = dwStyle; | 
 |  | 
 |     if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES) | 
 |     { | 
 |         HIMAGELIST himl = 0; | 
 |         if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) | 
 |             himl = LISTVIEW_CreateCheckBoxIL(infoPtr); | 
 |         LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl); | 
 |     } | 
 |  | 
 |     return dwOldStyle; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Sets the new hot cursor used during hot tracking and hover selection. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] hCursor : the new hot cursor handle | 
 |  * | 
 |  * RETURN: | 
 |  * Returns the previous hot cursor | 
 |  */ | 
 | static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor) | 
 | { | 
 |     HCURSOR oldCursor = infoPtr->hHotCursor; | 
 |      | 
 |     infoPtr->hHotCursor = hCursor; | 
 |  | 
 |     return oldCursor; | 
 | } | 
 |  | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Sets the hot item index. | 
 |  * | 
 |  * PARAMETERS: | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] iIndex : index | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : previous hot item index | 
 |  *   FAILURE : -1 (no hot item) | 
 |  */ | 
 | static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex) | 
 | { | 
 |     INT iOldIndex = infoPtr->nHotItem; | 
 |      | 
 |     infoPtr->nHotItem = iIndex; | 
 |      | 
 |     return iOldIndex; | 
 | } | 
 |  | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Sets the amount of time the cursor must hover over an item before it is selected. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] dwHoverTime : hover time, if -1 the hover time is set to the default | 
 |  * | 
 |  * RETURN: | 
 |  * Returns the previous hover time | 
 |  */ | 
 | static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime) | 
 | { | 
 |     DWORD oldHoverTime = infoPtr->dwHoverTime; | 
 |      | 
 |     infoPtr->dwHoverTime = dwHoverTime; | 
 |      | 
 |     return oldHoverTime; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Sets spacing for icons of LVS_ICON style. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize) | 
 |  * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize) | 
 |  * | 
 |  * RETURN: | 
 |  *   MAKELONG(oldcx, oldcy) | 
 |  */ | 
 | static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy) | 
 | { | 
 |     DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy); | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |  | 
 |     TRACE("requested=(%d,%d)\n", cx, cy); | 
 |      | 
 |     /* this is supported only for LVS_ICON style */ | 
 |     if (uView != LVS_ICON) return oldspacing; | 
 |    | 
 |     /* set to defaults, if instructed to */ | 
 |     if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING); | 
 |     if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING); | 
 |  | 
 |     /* if 0 then compute width | 
 |      * FIXME: Should scan each item and determine max width of | 
 |      *        icon or label, then make that the width */ | 
 |     if (cx == 0) | 
 | 	cx = infoPtr->iconSpacing.cx; | 
 |  | 
 |     /* if 0 then compute height */ | 
 |     if (cy == 0)  | 
 | 	cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight + | 
 | 	     ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING; | 
 |      | 
 |  | 
 |     infoPtr->iconSpacing.cx = cx; | 
 |     infoPtr->iconSpacing.cy = cy; | 
 |  | 
 |     TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n", | 
 | 	  LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,  | 
 | 	  infoPtr->iconSize.cx, infoPtr->iconSize.cy, | 
 | 	  infoPtr->ntmHeight); | 
 |  | 
 |     /* these depend on the iconSpacing */ | 
 |     LISTVIEW_UpdateItemSize(infoPtr); | 
 |  | 
 |     return oldspacing; | 
 | } | 
 |  | 
 | static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small) | 
 | { | 
 |     INT cx, cy; | 
 |      | 
 |     if (himl && ImageList_GetIconSize(himl, &cx, &cy)) | 
 |     { | 
 | 	size->cx = cx; | 
 | 	size->cy = cy; | 
 |     } | 
 |     else | 
 |     { | 
 | 	size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON); | 
 | 	size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON); | 
 |     } | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Sets image lists. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nType : image list type | 
 |  * [I] himl : image list handle | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : old image list | 
 |  *   FAILURE : NULL | 
 |  */ | 
 | static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     INT oldHeight = infoPtr->nItemHeight; | 
 |     HIMAGELIST himlOld = 0; | 
 |  | 
 |     TRACE("(nType=%d, himl=%p\n", nType, himl); | 
 |  | 
 |     switch (nType) | 
 |     { | 
 |     case LVSIL_NORMAL: | 
 |         himlOld = infoPtr->himlNormal; | 
 |         infoPtr->himlNormal = himl; | 
 |         if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE); | 
 |         LISTVIEW_SetIconSpacing(infoPtr, 0, 0); | 
 |     break; | 
 |  | 
 |     case LVSIL_SMALL: | 
 |         himlOld = infoPtr->himlSmall; | 
 |         infoPtr->himlSmall = himl; | 
 |          if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE); | 
 |     break; | 
 |  | 
 |     case LVSIL_STATE: | 
 |         himlOld = infoPtr->himlState; | 
 |         infoPtr->himlState = himl; | 
 |         set_icon_size(&infoPtr->iconStateSize, himl, TRUE); | 
 |         ImageList_SetBkColor(infoPtr->himlState, CLR_NONE); | 
 |     break; | 
 |  | 
 |     default: | 
 |         ERR("Unknown icon type=%d\n", nType); | 
 | 	return NULL; | 
 |     } | 
 |  | 
 |     infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr); | 
 |     if (infoPtr->nItemHeight != oldHeight) | 
 |         LISTVIEW_UpdateScroll(infoPtr); | 
 |  | 
 |     return himlOld; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Preallocates memory (does *not* set the actual count of items !) | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nItems : item count (projected number of items to allocate) | 
 |  * [I] dwFlags : update flags | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags) | 
 | { | 
 |     TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags); | 
 |  | 
 |     if (infoPtr->dwStyle & LVS_OWNERDATA) | 
 |     { | 
 | 	UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 | 	INT nOldCount = infoPtr->nItemCount; | 
 |  | 
 | 	if (nItems < nOldCount) | 
 | 	{ | 
 | 	    RANGE range = { nItems, nOldCount }; | 
 | 	    ranges_del(infoPtr->selectionRanges, range); | 
 | 	    if (infoPtr->nFocusedItem >= nItems) | 
 | 	    { | 
 | 		infoPtr->nFocusedItem = -1; | 
 | 		SetRectEmpty(&infoPtr->rcFocus); | 
 | 	    } | 
 | 	} | 
 |  | 
 | 	infoPtr->nItemCount = nItems; | 
 | 	LISTVIEW_UpdateScroll(infoPtr); | 
 |  | 
 | 	/* the flags are valid only in ownerdata report and list modes */ | 
 | 	if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0; | 
 |  | 
 | 	if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1) | 
 | 	    LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE); | 
 |  | 
 | 	if (!(dwFlags & LVSICF_NOINVALIDATEALL)) | 
 | 	    LISTVIEW_InvalidateList(infoPtr); | 
 | 	else | 
 | 	{ | 
 | 	    INT nFrom, nTo; | 
 | 	    POINT Origin; | 
 | 	    RECT rcErase; | 
 | 	     | 
 | 	    LISTVIEW_GetOrigin(infoPtr, &Origin); | 
 |     	    nFrom = min(nOldCount, nItems); | 
 | 	    nTo = max(nOldCount, nItems); | 
 |      | 
 | 	    if (uView == LVS_REPORT) | 
 | 	    { | 
 | 		rcErase.left = 0; | 
 | 		rcErase.top = nFrom * infoPtr->nItemHeight; | 
 | 		rcErase.right = infoPtr->nItemWidth; | 
 | 		rcErase.bottom = nTo * infoPtr->nItemHeight; | 
 | 		OffsetRect(&rcErase, Origin.x, Origin.y); | 
 | 		if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList)) | 
 | 		    LISTVIEW_InvalidateRect(infoPtr, &rcErase); | 
 | 	    } | 
 | 	    else /* LVS_LIST */ | 
 | 	    { | 
 | 		INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr); | 
 |  | 
 | 		rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth; | 
 | 		rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight; | 
 | 		rcErase.right = rcErase.left + infoPtr->nItemWidth; | 
 | 		rcErase.bottom = nPerCol * infoPtr->nItemHeight; | 
 | 		OffsetRect(&rcErase, Origin.x, Origin.y); | 
 | 		if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList)) | 
 | 		    LISTVIEW_InvalidateRect(infoPtr, &rcErase); | 
 |  | 
 | 		rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth; | 
 | 		rcErase.top = 0; | 
 | 		rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth; | 
 | 		rcErase.bottom = nPerCol * infoPtr->nItemHeight; | 
 | 		OffsetRect(&rcErase, Origin.x, Origin.y); | 
 | 		if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList)) | 
 | 		    LISTVIEW_InvalidateRect(infoPtr, &rcErase); | 
 | 	    } | 
 | 	} | 
 |     } | 
 |     else | 
 |     { | 
 | 	/* According to MSDN for non-LVS_OWNERDATA this is just | 
 | 	 * a performance issue. The control allocates its internal | 
 | 	 * data structures for the number of items specified. It | 
 | 	 * cuts down on the number of memory allocations. Therefore | 
 | 	 * we will just issue a WARN here | 
 | 	 */ | 
 | 	WARN("for non-ownerdata performance option not implemented.\n"); | 
 |     } | 
 |  | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Sets the position of an item. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nItem : item index | 
 |  * [I] pt : coordinate | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     POINT Origin; | 
 |  | 
 |     TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt)); | 
 |  | 
 |     if (nItem < 0 || nItem >= infoPtr->nItemCount || | 
 | 	!(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE; | 
 |  | 
 |     LISTVIEW_GetOrigin(infoPtr, &Origin); | 
 |  | 
 |     /* This point value seems to be an undocumented feature. | 
 |      * The best guess is that it means either at the origin,  | 
 |      * or at true beginning of the list. I will assume the origin. */ | 
 |     if ((pt.x == -1) && (pt.y == -1)) | 
 | 	pt = Origin; | 
 |      | 
 |     if (uView == LVS_ICON) | 
 |     { | 
 | 	pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2; | 
 | 	pt.y -= ICON_TOP_PADDING; | 
 |     } | 
 |     pt.x -= Origin.x; | 
 |     pt.y -= Origin.y; | 
 |  | 
 |     infoPtr->bAutoarrange = FALSE; | 
 |  | 
 |     return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Sets the state of one or many items. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nItem : item index | 
 |  * [I] lpLVItem : item or subitem info | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem) | 
 | { | 
 |     BOOL bResult = TRUE; | 
 |     LVITEMW lvItem; | 
 |  | 
 |     lvItem.iItem = nItem; | 
 |     lvItem.iSubItem = 0; | 
 |     lvItem.mask = LVIF_STATE; | 
 |     lvItem.state = lpLVItem->state; | 
 |     lvItem.stateMask = lpLVItem->stateMask; | 
 |     TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE)); | 
 |  | 
 |     if (nItem == -1) | 
 |     { | 
 |     	/* apply to all items */ | 
 |     	for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++) | 
 | 	    if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE; | 
 |     } | 
 |     else | 
 | 	bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE); | 
 |  | 
 |     /* | 
 |      * Update selection mark | 
 |      * | 
 |      * Investigation on windows 2k showed that selection mark was updated | 
 |      * whenever a new selection was made, but if the selected item was | 
 |      * unselected it was not updated. | 
 |      * | 
 |      * we are probably still not 100% accurate, but this at least sets the | 
 |      * proper selection mark when it is needed | 
 |      */ | 
 |  | 
 |     if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) && | 
 |         ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark))) | 
 |     { | 
 |         int i; | 
 |         infoPtr->nSelectionMark = -1; | 
 |         for (i = 0; i < infoPtr->nItemCount; i++) | 
 |         { | 
 |             if (infoPtr->uCallbackMask & LVIS_SELECTED) | 
 |             { | 
 |                 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED)) | 
 |                 { | 
 |                     infoPtr->nSelectionMark = i; | 
 |                     break; | 
 |                 } | 
 |             } | 
 |             else if (ranges_contain(infoPtr->selectionRanges, i)) | 
 |             { | 
 |                 infoPtr->nSelectionMark = i; | 
 |                 break; | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     return bResult; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Sets the text of an item or subitem. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] hwnd : window handle | 
 |  * [I] nItem : item index | 
 |  * [I] lpLVItem : item or subitem info | 
 |  * [I] isW : TRUE if input is Unicode | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW) | 
 | { | 
 |     LVITEMW lvItem; | 
 |  | 
 |     if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE; | 
 |      | 
 |     lvItem.iItem = nItem; | 
 |     lvItem.iSubItem = lpLVItem->iSubItem; | 
 |     lvItem.mask = LVIF_TEXT; | 
 |     lvItem.pszText = lpLVItem->pszText; | 
 |     lvItem.cchTextMax = lpLVItem->cchTextMax; | 
 |      | 
 |     TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW); | 
 |  | 
 |     return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);  | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Set item index that marks the start of a multiple selection. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nIndex : index | 
 |  * | 
 |  * RETURN: | 
 |  * Index number or -1 if there is no selection mark. | 
 |  */ | 
 | static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex) | 
 | { | 
 |   INT nOldIndex = infoPtr->nSelectionMark; | 
 |  | 
 |   TRACE("(nIndex=%d)\n", nIndex); | 
 |  | 
 |   infoPtr->nSelectionMark = nIndex; | 
 |  | 
 |   return nOldIndex; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Sets the text background color. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] clrTextBk : text background color | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk) | 
 | { | 
 |     TRACE("(clrTextBk=%lx)\n", clrTextBk); | 
 |  | 
 |     if (infoPtr->clrTextBk != clrTextBk) | 
 |     { | 
 | 	infoPtr->clrTextBk = clrTextBk; | 
 | 	LISTVIEW_InvalidateList(infoPtr); | 
 |     } | 
 |      | 
 |   return TRUE; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Sets the text foreground color. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] clrText : text color | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText) | 
 | { | 
 |     TRACE("(clrText=%lx)\n", clrText); | 
 |  | 
 |     if (infoPtr->clrText != clrText) | 
 |     { | 
 | 	infoPtr->clrText = clrText; | 
 | 	LISTVIEW_InvalidateList(infoPtr); | 
 |     } | 
 |  | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Determines which listview item is located at the specified position. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr        : valid pointer to the listview structure | 
 |  * [I] hwndNewToolTip : handle to new ToolTip | 
 |  * | 
 |  * RETURN: | 
 |  *   old tool tip | 
 |  */ | 
 | static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip) | 
 | { | 
 |   HWND hwndOldToolTip = infoPtr->hwndToolTip; | 
 |   infoPtr->hwndToolTip = hwndNewToolTip; | 
 |   return hwndOldToolTip; | 
 | } | 
 |  | 
 | /* LISTVIEW_SetUnicodeFormat */ | 
 | /* LISTVIEW_SetWorkAreas */ | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Callback internally used by LISTVIEW_SortItems() | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] first : pointer to first ITEM_INFO to compare | 
 |  * [I] second : pointer to second ITEM_INFO to compare | 
 |  * [I] lParam : HWND of control | 
 |  * | 
 |  * RETURN: | 
 |  *   if first comes before second : negative | 
 |  *   if first comes after second : positive | 
 |  *   if first and second are equivalent : zero | 
 |  */ | 
 | static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam) | 
 | { | 
 |   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam; | 
 |   ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 ); | 
 |   ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 ); | 
 |  | 
 |   /* Forward the call to the client defined callback */ | 
 |   return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort ); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Sorts the listview items. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] pfnCompare : application-defined value | 
 |  * [I] lParamSort : pointer to comparision callback | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     HDPA hdpaSubItems; | 
 |     ITEM_INFO *lpItem; | 
 |     LPVOID selectionMarkItem; | 
 |     LVITEMW item; | 
 |     int i; | 
 |  | 
 |     TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort); | 
 |  | 
 |     if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE; | 
 |  | 
 |     if (!pfnCompare) return FALSE; | 
 |     if (!infoPtr->hdpaItems) return FALSE; | 
 |  | 
 |     /* if there are 0 or 1 items, there is no need to sort */ | 
 |     if (infoPtr->nItemCount < 2) return TRUE; | 
 |  | 
 |     if (infoPtr->nFocusedItem >= 0) | 
 |     { | 
 | 	hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem); | 
 | 	lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0); | 
 | 	if (lpItem) lpItem->state |= LVIS_FOCUSED; | 
 |     } | 
 |     /* FIXME: go thorugh selected items and mark them so in lpItem->state */ | 
 |     /*        clear the lpItem->state for non-selected ones */ | 
 |     /*        remove the selection ranges */ | 
 |      | 
 |     infoPtr->pfnCompare = pfnCompare; | 
 |     infoPtr->lParamSort = lParamSort; | 
 |     DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr); | 
 |  | 
 |     /* Adjust selections and indices so that they are the way they should | 
 |      * be after the sort (otherwise, the list items move around, but | 
 |      * whatever is at the item's previous original position will be | 
 |      * selected instead) | 
 |      */ | 
 |     selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL; | 
 |     for (i=0; i < infoPtr->nItemCount; i++) | 
 |     { | 
 | 	hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i); | 
 | 	lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0); | 
 |  | 
 | 	if (lpItem->state & LVIS_SELECTED) | 
 | 	{ | 
 | 	    item.state = LVIS_SELECTED; | 
 | 	    item.stateMask = LVIS_SELECTED; | 
 | 	    LISTVIEW_SetItemState(infoPtr, i, &item); | 
 | 	} | 
 | 	if (lpItem->state & LVIS_FOCUSED) | 
 | 	{ | 
 |             infoPtr->nFocusedItem = i; | 
 | 	    lpItem->state &= ~LVIS_FOCUSED; | 
 | 	} | 
 |     } | 
 |     if (selectionMarkItem != NULL) | 
 | 	infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem); | 
 |     /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */ | 
 |  | 
 |     /* refresh the display */ | 
 |     if (uView != LVS_ICON && uView != LVS_SMALLICON) | 
 | 	LISTVIEW_InvalidateList(infoPtr); | 
 |  | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Update theme handle after a theme change. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : 0 | 
 |  *   FAILURE : something else | 
 |  */ | 
 | static LRESULT LISTVIEW_ThemeChanged(LISTVIEW_INFO *infoPtr) | 
 | { | 
 |     HTHEME theme = GetWindowTheme(infoPtr->hwndSelf); | 
 |     CloseThemeData(theme); | 
 |     OpenThemeData(infoPtr->hwndSelf, themeClass); | 
 |     return 0; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Updates an items or rearranges the listview control. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nItem : item index | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem) | 
 | { | 
 |     TRACE("(nItem=%d)\n", nItem); | 
 |  | 
 |     if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE; | 
 |  | 
 |     /* rearrange with default alignment style */ | 
 |     if (is_autoarrange(infoPtr)) | 
 | 	LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); | 
 |     else | 
 | 	LISTVIEW_InvalidateItem(infoPtr, nItem); | 
 |  | 
 |     return TRUE; | 
 | } | 
 |  | 
 | 	 | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Creates the listview control. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] hwnd : window handle | 
 |  * [I] lpcs : the create parameters | 
 |  * | 
 |  * RETURN: | 
 |  *   Success: 0 | 
 |  *   Failure: -1 | 
 |  */ | 
 | static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs) | 
 | { | 
 |   LISTVIEW_INFO *infoPtr; | 
 |   UINT uView = lpcs->style & LVS_TYPEMASK; | 
 |   LOGFONTW logFont; | 
 |  | 
 |   TRACE("(lpcs=%p)\n", lpcs); | 
 |  | 
 |   /* initialize info pointer */ | 
 |   infoPtr = (LISTVIEW_INFO *)Alloc(sizeof(LISTVIEW_INFO)); | 
 |   if (!infoPtr) return -1; | 
 |  | 
 |   SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr); | 
 |  | 
 |   infoPtr->hwndSelf = hwnd; | 
 |   infoPtr->dwStyle = lpcs->style; | 
 |   /* determine the type of structures to use */ | 
 |   infoPtr->hwndNotify = lpcs->hwndParent; | 
 |   infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, | 
 |                                        (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY); | 
 |  | 
 |   /* initialize color information  */ | 
 |   infoPtr->clrBk = CLR_NONE; | 
 |   infoPtr->clrText = comctl32_color.clrWindowText; | 
 |   infoPtr->clrTextBk = CLR_DEFAULT; | 
 |   LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow); | 
 |  | 
 |   /* set default values */ | 
 |   infoPtr->nFocusedItem = -1; | 
 |   infoPtr->nSelectionMark = -1; | 
 |   infoPtr->nHotItem = -1; | 
 |   infoPtr->bRedraw = TRUE; | 
 |   infoPtr->bNoItemMetrics = TRUE; | 
 |   infoPtr->bDoChangeNotify = TRUE; | 
 |   infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING); | 
 |   infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING); | 
 |   infoPtr->nEditLabelItem = -1; | 
 |   infoPtr->dwHoverTime = -1; /* default system hover time */ | 
 |   infoPtr->nMeasureItemHeight = 0; | 
 |  | 
 |   /* get default font (icon title) */ | 
 |   SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0); | 
 |   infoPtr->hDefaultFont = CreateFontIndirectW(&logFont); | 
 |   infoPtr->hFont = infoPtr->hDefaultFont; | 
 |   LISTVIEW_SaveTextMetrics(infoPtr); | 
 |  | 
 |   /* create header */ | 
 |   infoPtr->hwndHeader =	CreateWindowW(WC_HEADERW, NULL, | 
 |     WS_CHILD | HDS_HORZ | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS), | 
 |     0, 0, 0, 0, hwnd, NULL, | 
 |     lpcs->hInstance, NULL); | 
 |   if (!infoPtr->hwndHeader) goto fail; | 
 |  | 
 |   /* set header unicode format */ | 
 |   SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL); | 
 |  | 
 |   /* set header font */ | 
 |   SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE); | 
 |  | 
 |   /* allocate memory for the data structure */ | 
 |   if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail; | 
 |   if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail; | 
 |   if (!(infoPtr->hdpaPosX  = DPA_Create(10))) goto fail; | 
 |   if (!(infoPtr->hdpaPosY  = DPA_Create(10))) goto fail; | 
 |   if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail; | 
 |  | 
 |   /* initialize the icon sizes */ | 
 |   set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON); | 
 |   set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE); | 
 |  | 
 |   /* init item size to avoid division by 0 */ | 
 |   LISTVIEW_UpdateItemSize (infoPtr); | 
 |    | 
 |   if (uView == LVS_REPORT) | 
 |   { | 
 |     if (!(LVS_NOCOLUMNHEADER & lpcs->style)) | 
 |     { | 
 |       ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL); | 
 |     } | 
 |     else | 
 |     { | 
 |       /* set HDS_HIDDEN flag to hide the header bar */ | 
 |       SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, | 
 |                     GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN); | 
 |     } | 
 |   } | 
 |  | 
 |   OpenThemeData(hwnd, themeClass); | 
 |  | 
 |   return 0; | 
 |  | 
 | fail: | 
 |     DestroyWindow(infoPtr->hwndHeader); | 
 |     ranges_destroy(infoPtr->selectionRanges); | 
 |     DPA_Destroy(infoPtr->hdpaItems); | 
 |     DPA_Destroy(infoPtr->hdpaPosX); | 
 |     DPA_Destroy(infoPtr->hdpaPosY); | 
 |     DPA_Destroy(infoPtr->hdpaColumns); | 
 |     Free(infoPtr); | 
 |     return -1; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Destroys the listview control. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * | 
 |  * RETURN: | 
 |  *   Success: 0 | 
 |  *   Failure: -1 | 
 |  */ | 
 | static LRESULT LISTVIEW_Destroy(LISTVIEW_INFO *infoPtr) | 
 | { | 
 |     HTHEME theme = GetWindowTheme(infoPtr->hwndSelf); | 
 |     CloseThemeData(theme); | 
 |     return 0; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Enables the listview control. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] bEnable : specifies whether to enable or disable the window | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static BOOL LISTVIEW_Enable(LISTVIEW_INFO *infoPtr, BOOL bEnable) | 
 | { | 
 |     if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED) | 
 |         InvalidateRect(infoPtr->hwndSelf, NULL, TRUE); | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Erases the background of the listview control. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] hdc : device context handle | 
 |  * | 
 |  * RETURN: | 
 |  *   SUCCESS : TRUE | 
 |  *   FAILURE : FALSE | 
 |  */ | 
 | static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc) | 
 | { | 
 |     RECT rc; | 
 |  | 
 |     TRACE("(hdc=%p)\n", hdc); | 
 |  | 
 |     if (!GetClipBox(hdc, &rc)) return FALSE; | 
 |  | 
 |     return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc); | 
 | } | 
 | 	 | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Helper function for LISTVIEW_[HV]Scroll *only*. | 
 |  * Performs vertical/horizontal scrolling by a give amount. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] dx : amount of horizontal scroll | 
 |  * [I] dy : amount of vertical scroll | 
 |  */ | 
 | static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy) | 
 | { | 
 |     /* now we can scroll the list */ | 
 |     ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,  | 
 | 		   &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE); | 
 |     /* if we have focus, adjust rect */ | 
 |     OffsetRect(&infoPtr->rcFocus, dx, dy); | 
 |     UpdateWindow(infoPtr->hwndSelf); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Performs vertical scrolling. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nScrollCode : scroll code | 
 |  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise | 
 |  * [I] hScrollWnd  : scrollbar control window handle | 
 |  * | 
 |  * RETURN: | 
 |  * Zero | 
 |  * | 
 |  * NOTES: | 
 |  *   SB_LINEUP/SB_LINEDOWN: | 
 |  *        for LVS_ICON, LVS_SMALLICON is 37 by experiment | 
 |  *        for LVS_REPORT is 1 line | 
 |  *        for LVS_LIST cannot occur | 
 |  * | 
 |  */ | 
 | static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,  | 
 | 				INT nScrollDiff, HWND hScrollWnd) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     INT nOldScrollPos, nNewScrollPos; | 
 |     SCROLLINFO scrollInfo; | 
 |     BOOL is_an_icon; | 
 |  | 
 |     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,  | 
 | 	debugscrollcode(nScrollCode), nScrollDiff); | 
 |  | 
 |     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0); | 
 |  | 
 |     scrollInfo.cbSize = sizeof(SCROLLINFO); | 
 |     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS; | 
 |  | 
 |     is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON)); | 
 |  | 
 |     if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1; | 
 |  | 
 |     nOldScrollPos = scrollInfo.nPos; | 
 |     switch (nScrollCode) | 
 |     { | 
 |     case SB_INTERNAL: | 
 |         break; | 
 |  | 
 |     case SB_LINEUP: | 
 | 	nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1; | 
 |         break; | 
 |  | 
 |     case SB_LINEDOWN: | 
 | 	nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1; | 
 |         break; | 
 |  | 
 |     case SB_PAGEUP: | 
 | 	nScrollDiff = -scrollInfo.nPage; | 
 |         break; | 
 |  | 
 |     case SB_PAGEDOWN: | 
 | 	nScrollDiff = scrollInfo.nPage; | 
 |         break; | 
 |  | 
 |     case SB_THUMBPOSITION: | 
 |     case SB_THUMBTRACK: | 
 | 	nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos; | 
 |         break; | 
 |  | 
 |     default: | 
 | 	nScrollDiff = 0; | 
 |     } | 
 |  | 
 |     /* quit right away if pos isn't changing */ | 
 |     if (nScrollDiff == 0) return 0; | 
 |      | 
 |     /* calculate new position, and handle overflows */ | 
 |     nNewScrollPos = scrollInfo.nPos + nScrollDiff; | 
 |     if (nScrollDiff > 0) { | 
 | 	if (nNewScrollPos < nOldScrollPos || | 
 | 	    nNewScrollPos > scrollInfo.nMax) | 
 | 	    nNewScrollPos = scrollInfo.nMax; | 
 |     } else { | 
 | 	if (nNewScrollPos > nOldScrollPos || | 
 | 	    nNewScrollPos < scrollInfo.nMin) | 
 | 	    nNewScrollPos = scrollInfo.nMin; | 
 |     } | 
 |  | 
 |     /* set the new position, and reread in case it changed */ | 
 |     scrollInfo.fMask = SIF_POS; | 
 |     scrollInfo.nPos = nNewScrollPos; | 
 |     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE); | 
 |      | 
 |     /* carry on only if it really changed */ | 
 |     if (nNewScrollPos == nOldScrollPos) return 0; | 
 |      | 
 |     /* now adjust to client coordinates */ | 
 |     nScrollDiff = nOldScrollPos - nNewScrollPos; | 
 |     if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight; | 
 |     | 
 |     /* and scroll the window */  | 
 |     scroll_list(infoPtr, 0, nScrollDiff); | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Performs horizontal scrolling. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nScrollCode : scroll code | 
 |  * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise | 
 |  * [I] hScrollWnd  : scrollbar control window handle | 
 |  * | 
 |  * RETURN: | 
 |  * Zero | 
 |  * | 
 |  * NOTES: | 
 |  *   SB_LINELEFT/SB_LINERIGHT: | 
 |  *        for LVS_ICON, LVS_SMALLICON  1 pixel | 
 |  *        for LVS_REPORT is 1 pixel | 
 |  *        for LVS_LIST  is 1 column --> which is a 1 because the | 
 |  *                                      scroll is based on columns not pixels | 
 |  * | 
 |  */ | 
 | static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode, | 
 |                                 INT nScrollDiff, HWND hScrollWnd) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     INT nOldScrollPos, nNewScrollPos; | 
 |     SCROLLINFO scrollInfo; | 
 |  | 
 |     TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,  | 
 | 	debugscrollcode(nScrollCode), nScrollDiff); | 
 |  | 
 |     if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0); | 
 |  | 
 |     scrollInfo.cbSize = sizeof(SCROLLINFO); | 
 |     scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS; | 
 |  | 
 |     if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1; | 
 |  | 
 |     nOldScrollPos = scrollInfo.nPos; | 
 |  | 
 |     switch (nScrollCode) | 
 |     { | 
 |     case SB_INTERNAL: | 
 |         break; | 
 |  | 
 |     case SB_LINELEFT: | 
 | 	nScrollDiff = -1; | 
 |         break; | 
 |  | 
 |     case SB_LINERIGHT: | 
 | 	nScrollDiff = 1; | 
 |         break; | 
 |  | 
 |     case SB_PAGELEFT: | 
 | 	nScrollDiff = -scrollInfo.nPage; | 
 |         break; | 
 |  | 
 |     case SB_PAGERIGHT: | 
 | 	nScrollDiff = scrollInfo.nPage; | 
 |         break; | 
 |  | 
 |     case SB_THUMBPOSITION: | 
 |     case SB_THUMBTRACK: | 
 | 	nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos; | 
 | 	break; | 
 |  | 
 |     default: | 
 | 	nScrollDiff = 0; | 
 |     } | 
 |  | 
 |     /* quit right away if pos isn't changing */ | 
 |     if (nScrollDiff == 0) return 0; | 
 |      | 
 |     /* calculate new position, and handle overflows */ | 
 |     nNewScrollPos = scrollInfo.nPos + nScrollDiff; | 
 |     if (nScrollDiff > 0) { | 
 | 	if (nNewScrollPos < nOldScrollPos || | 
 | 	    nNewScrollPos > scrollInfo.nMax) | 
 | 	    nNewScrollPos = scrollInfo.nMax; | 
 |     } else { | 
 | 	if (nNewScrollPos > nOldScrollPos || | 
 | 	    nNewScrollPos < scrollInfo.nMin) | 
 | 	    nNewScrollPos = scrollInfo.nMin; | 
 |     } | 
 |  | 
 |     /* set the new position, and reread in case it changed */ | 
 |     scrollInfo.fMask = SIF_POS; | 
 |     scrollInfo.nPos = nNewScrollPos; | 
 |     nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE); | 
 |      | 
 |     /* carry on only if it really changed */ | 
 |     if (nNewScrollPos == nOldScrollPos) return 0; | 
 |      | 
 |     if(uView == LVS_REPORT) | 
 |         LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos); | 
 |        | 
 |     /* now adjust to client coordinates */ | 
 |     nScrollDiff = nOldScrollPos - nNewScrollPos; | 
 |     if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth; | 
 |     | 
 |     /* and scroll the window */ | 
 |     scroll_list(infoPtr, nScrollDiff, 0); | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     INT gcWheelDelta = 0; | 
 |     INT pulScrollLines = 3; | 
 |     SCROLLINFO scrollInfo; | 
 |  | 
 |     TRACE("(wheelDelta=%d)\n", wheelDelta); | 
 |  | 
 |     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0); | 
 |     gcWheelDelta -= wheelDelta; | 
 |  | 
 |     scrollInfo.cbSize = sizeof(SCROLLINFO); | 
 |     scrollInfo.fMask = SIF_POS; | 
 |  | 
 |     switch(uView) | 
 |     { | 
 |     case LVS_ICON: | 
 |     case LVS_SMALLICON: | 
 |        /* | 
 |         *  listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number | 
 |         *  should be fixed in the future. | 
 |         */ | 
 |         LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ? | 
 |                 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0); | 
 |         break; | 
 |  | 
 |     case LVS_REPORT: | 
 |         if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines) | 
 |         { | 
 |             int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines); | 
 |             cLineScroll *= (gcWheelDelta / WHEEL_DELTA); | 
 |             LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0); | 
 |         } | 
 |         break; | 
 |  | 
 |     case LVS_LIST: | 
 |         LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0); | 
 |         break; | 
 |     } | 
 |     return 0; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * ??? | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nVirtualKey : virtual key | 
 |  * [I] lKeyData : key data | 
 |  * | 
 |  * RETURN: | 
 |  * Zero | 
 |  */ | 
 | static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData) | 
 | { | 
 |   UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK; | 
 |   HWND hwndSelf = infoPtr->hwndSelf; | 
 |   INT nItem = -1; | 
 |   NMLVKEYDOWN nmKeyDown; | 
 |  | 
 |   TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData); | 
 |  | 
 |   /* send LVN_KEYDOWN notification */ | 
 |   nmKeyDown.wVKey = nVirtualKey; | 
 |   nmKeyDown.flags = 0; | 
 |   notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr); | 
 |   if (!IsWindow(hwndSelf)) | 
 |     return 0; | 
 |  | 
 |   switch (nVirtualKey) | 
 |   { | 
 |   case VK_SPACE: | 
 |     nItem = infoPtr->nFocusedItem; | 
 |     break; | 
 |  | 
 |   case VK_RETURN: | 
 |     if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1)) | 
 |     { | 
 |         if (!notify(infoPtr, NM_RETURN)) return 0; | 
 |         if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0; | 
 |     } | 
 |     break; | 
 |  | 
 |   case VK_HOME: | 
 |     if (infoPtr->nItemCount > 0) | 
 |       nItem = 0; | 
 |     break; | 
 |  | 
 |   case VK_END: | 
 |     if (infoPtr->nItemCount > 0) | 
 |       nItem = infoPtr->nItemCount - 1; | 
 |     break; | 
 |  | 
 |   case VK_LEFT: | 
 |     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT); | 
 |     break; | 
 |  | 
 |   case VK_UP: | 
 |     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE); | 
 |     break; | 
 |  | 
 |   case VK_RIGHT: | 
 |     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT); | 
 |     break; | 
 |  | 
 |   case VK_DOWN: | 
 |     nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW); | 
 |     break; | 
 |  | 
 |   case VK_PRIOR: | 
 |     if (uView == LVS_REPORT) | 
 |     { | 
 |       INT topidx = LISTVIEW_GetTopIndex(infoPtr); | 
 |       if (infoPtr->nFocusedItem == topidx) | 
 |         nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1; | 
 |       else | 
 |         nItem = topidx; | 
 |     } | 
 |     else | 
 |       nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr) | 
 |                                     * LISTVIEW_GetCountPerRow(infoPtr); | 
 |     if(nItem < 0) nItem = 0; | 
 |     break; | 
 |  | 
 |   case VK_NEXT: | 
 |     if (uView == LVS_REPORT) | 
 |     { | 
 |       INT topidx = LISTVIEW_GetTopIndex(infoPtr); | 
 |       INT cnt = LISTVIEW_GetCountPerColumn(infoPtr); | 
 |       if (infoPtr->nFocusedItem == topidx + cnt - 1) | 
 |         nItem = infoPtr->nFocusedItem + cnt - 1; | 
 |       else | 
 |         nItem = topidx + cnt - 1; | 
 |     } | 
 |     else | 
 |       nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr) | 
 |                                     * LISTVIEW_GetCountPerRow(infoPtr); | 
 |     if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1; | 
 |     break; | 
 |   } | 
 |  | 
 |   if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE)) | 
 |       LISTVIEW_KeySelection(infoPtr, nItem); | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Kills the focus. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * | 
 |  * RETURN: | 
 |  * Zero | 
 |  */ | 
 | static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr) | 
 | { | 
 |     TRACE("()\n"); | 
 |  | 
 |     /* if we did not have the focus, there's nothing to do */ | 
 |     if (!infoPtr->bFocus) return 0; | 
 |     | 
 |     /* send NM_KILLFOCUS notification */ | 
 |     if (!notify(infoPtr, NM_KILLFOCUS)) return 0; | 
 |  | 
 |     /* if we have a focus rectagle, get rid of it */ | 
 |     LISTVIEW_ShowFocusRect(infoPtr, FALSE); | 
 |      | 
 |     /* set window focus flag */ | 
 |     infoPtr->bFocus = FALSE; | 
 |  | 
 |     /* invalidate the selected items before reseting focus flag */ | 
 |     LISTVIEW_InvalidateSelectedItems(infoPtr); | 
 |      | 
 |     return 0; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Processes double click messages (left mouse button). | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] wKey : key flag | 
 |  * [I] x,y : mouse coordinate | 
 |  * | 
 |  * RETURN: | 
 |  * Zero | 
 |  */ | 
 | static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y) | 
 | { | 
 |     LVHITTESTINFO htInfo; | 
 |  | 
 |     TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y); | 
 |  | 
 |     /* send NM_RELEASEDCAPTURE notification */ | 
 |     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0; | 
 |  | 
 |     htInfo.pt.x = x; | 
 |     htInfo.pt.y = y; | 
 |  | 
 |     /* send NM_DBLCLK notification */ | 
 |     LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE); | 
 |     if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0; | 
 |  | 
 |     /* To send the LVN_ITEMACTIVATE, it must be on an Item */ | 
 |     if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo); | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Processes mouse down messages (left mouse button). | 
 |  * | 
 |  * PARAMETERS: | 
 |  *   infoPtr  [I ] valid pointer to the listview structure | 
 |  *   wKey     [I ] key flag | 
 |  *   x,y      [I ] mouse coordinate | 
 |  * | 
 |  * RETURN: | 
 |  *   Zero | 
 |  */ | 
 | static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y) | 
 | { | 
 |   LVHITTESTINFO lvHitTestInfo; | 
 |   static BOOL bGroupSelect = TRUE; | 
 |   POINT pt = { x, y }; | 
 |   INT nItem; | 
 |  | 
 |   TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y); | 
 |  | 
 |   /* send NM_RELEASEDCAPTURE notification */ | 
 |   if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0; | 
 |  | 
 |   if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf); | 
 |  | 
 |   /* set left button down flag and record the click position */ | 
 |   infoPtr->bLButtonDown = TRUE; | 
 |   infoPtr->ptClickPos = pt; | 
 |  | 
 |   lvHitTestInfo.pt.x = x; | 
 |   lvHitTestInfo.pt.y = y; | 
 |  | 
 |   nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE); | 
 |   TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem); | 
 |   infoPtr->nEditLabelItem = -1; | 
 |   if ((nItem >= 0) && (nItem < infoPtr->nItemCount)) | 
 |   { | 
 |     if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON)) | 
 |     { | 
 |         DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK)); | 
 |         if(state == 1 || state == 2) | 
 |         { | 
 |             LVITEMW lvitem; | 
 |             state ^= 3; | 
 |             lvitem.state = INDEXTOSTATEIMAGEMASK(state); | 
 |             lvitem.stateMask = LVIS_STATEIMAGEMASK; | 
 |             LISTVIEW_SetItemState(infoPtr, nItem, &lvitem); | 
 |         } | 
 |         return 0; | 
 |     } | 
 |  | 
 |     if (infoPtr->dwStyle & LVS_SINGLESEL) | 
 |     { | 
 |       if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED)) | 
 |         infoPtr->nEditLabelItem = nItem; | 
 |       else | 
 |         LISTVIEW_SetSelection(infoPtr, nItem); | 
 |     } | 
 |     else | 
 |     { | 
 |       if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT)) | 
 |       { | 
 |         if (bGroupSelect) | 
 | 	{ | 
 |           if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0; | 
 |     	  LISTVIEW_SetItemFocus(infoPtr, nItem); | 
 |           infoPtr->nSelectionMark = nItem; | 
 | 	} | 
 |         else | 
 | 	{ | 
 |           LVITEMW item; | 
 |  | 
 | 	  item.state = LVIS_SELECTED | LVIS_FOCUSED; | 
 | 	  item.stateMask = LVIS_SELECTED | LVIS_FOCUSED; | 
 |  | 
 | 	  LISTVIEW_SetItemState(infoPtr,nItem,&item); | 
 | 	  infoPtr->nSelectionMark = nItem; | 
 | 	} | 
 |       } | 
 |       else if (wKey & MK_CONTROL) | 
 |       { | 
 |         LVITEMW item; | 
 |  | 
 | 	bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0); | 
 | 	 | 
 | 	item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED; | 
 |         item.stateMask = LVIS_SELECTED | LVIS_FOCUSED; | 
 | 	LISTVIEW_SetItemState(infoPtr, nItem, &item); | 
 |         infoPtr->nSelectionMark = nItem; | 
 |       } | 
 |       else  if (wKey & MK_SHIFT) | 
 |       { | 
 |         LISTVIEW_SetGroupSelection(infoPtr, nItem); | 
 |       } | 
 |       else | 
 |       { | 
 | 	if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED)) | 
 | 	  infoPtr->nEditLabelItem = nItem; | 
 |  | 
 | 	/* set selection (clears other pre-existing selections) */ | 
 |         LISTVIEW_SetSelection(infoPtr, nItem); | 
 |       } | 
 |     } | 
 |   } | 
 |   else | 
 |   { | 
 |     /* remove all selections */ | 
 |     LISTVIEW_DeselectAll(infoPtr); | 
 |     ReleaseCapture(); | 
 |   } | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Processes mouse up messages (left mouse button). | 
 |  * | 
 |  * PARAMETERS: | 
 |  *   infoPtr [I ] valid pointer to the listview structure | 
 |  *   wKey    [I ] key flag | 
 |  *   x,y     [I ] mouse coordinate | 
 |  * | 
 |  * RETURN: | 
 |  *   Zero | 
 |  */ | 
 | static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y) | 
 | { | 
 |     LVHITTESTINFO lvHitTestInfo; | 
 |      | 
 |     TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y); | 
 |  | 
 |     if (!infoPtr->bLButtonDown) return 0; | 
 |  | 
 |     lvHitTestInfo.pt.x = x; | 
 |     lvHitTestInfo.pt.y = y; | 
 |  | 
 |     /* send NM_CLICK notification */ | 
 |     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE); | 
 |     if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0; | 
 |  | 
 |     /* set left button flag */ | 
 |     infoPtr->bLButtonDown = FALSE; | 
 |  | 
 |     /* if we clicked on a selected item, edit the label */ | 
 |     if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL)) | 
 |         LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE); | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Destroys the listview control (called after WM_DESTROY). | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * | 
 |  * RETURN: | 
 |  * Zero | 
 |  */ | 
 | static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr) | 
 | { | 
 |   TRACE("()\n"); | 
 |  | 
 |   /* delete all items */ | 
 |   LISTVIEW_DeleteAllItems(infoPtr); | 
 |  | 
 |   /* destroy data structure */ | 
 |   DPA_Destroy(infoPtr->hdpaItems); | 
 |   DPA_Destroy(infoPtr->hdpaPosX); | 
 |   DPA_Destroy(infoPtr->hdpaPosY); | 
 |   DPA_Destroy(infoPtr->hdpaColumns); | 
 |   ranges_destroy(infoPtr->selectionRanges); | 
 |  | 
 |   /* destroy image lists */ | 
 |   if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS)) | 
 |   { | 
 |       if (infoPtr->himlNormal) | 
 | 	  ImageList_Destroy(infoPtr->himlNormal); | 
 |       if (infoPtr->himlSmall) | 
 | 	  ImageList_Destroy(infoPtr->himlSmall); | 
 |       if (infoPtr->himlState) | 
 | 	  ImageList_Destroy(infoPtr->himlState); | 
 |   } | 
 |  | 
 |   /* destroy font, bkgnd brush */ | 
 |   infoPtr->hFont = 0; | 
 |   if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont); | 
 |   if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush); | 
 |  | 
 |   SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0); | 
 |  | 
 |   /* free listview info pointer*/ | 
 |   Free(infoPtr); | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Handles notifications from header. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] nCtrlId : control identifier | 
 |  * [I] lpnmh : notification information | 
 |  * | 
 |  * RETURN: | 
 |  * Zero | 
 |  */ | 
 | static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh) | 
 | { | 
 |     UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK; | 
 |     HWND hwndSelf = infoPtr->hwndSelf; | 
 |      | 
 |     TRACE("(lpnmh=%p)\n", lpnmh); | 
 |  | 
 |     if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0; | 
 |      | 
 |     switch (lpnmh->hdr.code) | 
 |     {     | 
 |         case HDN_ITEMCHANGINGW: | 
 |         case HDN_ITEMCHANGINGA: | 
 |             return notify_forward_header(infoPtr, lpnmh); | 
 | 	case HDN_ITEMCHANGEDW: | 
 | 	case HDN_ITEMCHANGEDA: | 
 |             notify_forward_header(infoPtr, lpnmh); | 
 | 	    if (!IsWindow(hwndSelf)) | 
 | 		break; | 
 |             /* Fall through */ | 
 | 	case HDN_TRACKW: | 
 | 	case HDN_TRACKA: | 
 | 	{ | 
 | 	    COLUMN_INFO *lpColumnInfo; | 
 | 	    INT dx, cxy; | 
 |  | 
 | 	    if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH)) | 
 | 	    { | 
 |     		HDITEMW hdi; | 
 |      | 
 | 		hdi.mask = HDI_WIDTH; | 
 |     		if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, (LPARAM)&hdi)) return 0; | 
 | 		cxy = hdi.cxy; | 
 | 	    } | 
 | 	    else | 
 | 		cxy = lpnmh->pitem->cxy; | 
 | 	     | 
 | 	    /* determine how much we change since the last know position */ | 
 | 	    lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem); | 
 | 	    dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left); | 
 | 	    if (dx != 0) | 
 | 	    { | 
 | 		lpColumnInfo->rcHeader.right += dx; | 
 | 		LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx); | 
 | 		LISTVIEW_UpdateItemSize(infoPtr); | 
 | 		if (uView == LVS_REPORT && is_redrawing(infoPtr)) | 
 | 		{ | 
 | 		    POINT ptOrigin; | 
 | 		    RECT rcCol = lpColumnInfo->rcHeader; | 
 | 		     | 
 | 		    LISTVIEW_GetOrigin(infoPtr, &ptOrigin); | 
 | 		    OffsetRect(&rcCol, ptOrigin.x, 0); | 
 | 		     | 
 | 		    rcCol.top = infoPtr->rcList.top; | 
 | 		    rcCol.bottom = infoPtr->rcList.bottom; | 
 |  | 
 | 		    /* resizing left-aligned columns leaves most of the left side untouched */ | 
 | 		    if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT) | 
 | 		    { | 
 | 			INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth + dx; | 
 | 			rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty); | 
 | 		    } | 
 | 		     | 
 | 		    LISTVIEW_InvalidateRect(infoPtr, &rcCol); | 
 | 		} | 
 | 	    } | 
 | 	} | 
 | 	break; | 
 |  | 
 | 	case HDN_ITEMCLICKW: | 
 | 	case HDN_ITEMCLICKA: | 
 | 	{ | 
 |             /* Handle sorting by Header Column */ | 
 |             NMLISTVIEW nmlv; | 
 |  | 
 |             ZeroMemory(&nmlv, sizeof(NMLISTVIEW)); | 
 |             nmlv.iItem = -1; | 
 |             nmlv.iSubItem = lpnmh->iItem; | 
 |             notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv); | 
 |         } | 
 | 	break; | 
 |  | 
 | 	case HDN_DIVIDERDBLCLICKW: | 
 | 	case HDN_DIVIDERDBLCLICKA: | 
 |             LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE); | 
 |             break; | 
 |     } | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Paint non-client area of control. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structureof the sender | 
 |  * [I] region : update region | 
 |  * | 
 |  * RETURN: | 
 |  *  TRUE  - frame was painted | 
 |  *  FALSE - call default window proc | 
 |  */ | 
 | static BOOL LISTVIEW_NCPaint(LISTVIEW_INFO *infoPtr, HRGN region) | 
 | { | 
 |     HTHEME theme = GetWindowTheme (infoPtr->hwndSelf); | 
 |     HDC dc; | 
 |     RECT r; | 
 |     HRGN cliprgn; | 
 |     int cxEdge = GetSystemMetrics (SM_CXEDGE), | 
 |         cyEdge = GetSystemMetrics (SM_CYEDGE); | 
 |  | 
 |     if (!theme) return FALSE; | 
 |  | 
 |     GetWindowRect(infoPtr->hwndSelf, &r); | 
 |  | 
 |     cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge, | 
 |         r.right - cxEdge, r.bottom - cyEdge); | 
 |     if (region != (HRGN)1) | 
 |         CombineRgn (cliprgn, cliprgn, region, RGN_AND); | 
 |     OffsetRect(&r, -r.left, -r.top); | 
 |  | 
 |     dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN); | 
 |     OffsetRect(&r, -r.left, -r.top); | 
 |  | 
 |     if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0)) | 
 |         DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r); | 
 |     DrawThemeBackground (theme, dc, 0, 0, &r, 0); | 
 |     ReleaseDC(infoPtr->hwndSelf, dc); | 
 |  | 
 |     /* Call default proc to get the scrollbars etc. painted */ | 
 |     DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0); | 
 |  | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Determines the type of structure to use. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structureof the sender | 
 |  * [I] hwndFrom : listview window handle | 
 |  * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT | 
 |  * | 
 |  * RETURN: | 
 |  * Zero | 
 |  */ | 
 | static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand) | 
 | { | 
 |     TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand); | 
 |  | 
 |     if (nCommand != NF_REQUERY) return 0; | 
 |      | 
 |     infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY); | 
 |      | 
 |     return 0; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Paints/Repaints the listview control. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] hdc : device context handle | 
 |  * | 
 |  * RETURN: | 
 |  * Zero | 
 |  */ | 
 | static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc) | 
 | { | 
 |     TRACE("(hdc=%p)\n", hdc); | 
 |  | 
 |     if (infoPtr->bNoItemMetrics && infoPtr->nItemCount) | 
 |     { | 
 | 	UINT uView =  infoPtr->dwStyle & LVS_TYPEMASK; | 
 | 	 | 
 | 	infoPtr->bNoItemMetrics = FALSE; | 
 | 	LISTVIEW_UpdateItemSize(infoPtr); | 
 | 	if (uView == LVS_ICON || uView == LVS_SMALLICON) | 
 | 	    LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); | 
 | 	LISTVIEW_UpdateScroll(infoPtr); | 
 |     } | 
 |     if (hdc)  | 
 | 	LISTVIEW_Refresh(infoPtr, hdc); | 
 |     else | 
 |     { | 
 | 	PAINTSTRUCT ps; | 
 |  | 
 | 	hdc = BeginPaint(infoPtr->hwndSelf, &ps); | 
 | 	if (!hdc) return 1; | 
 | 	if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint); | 
 | 	LISTVIEW_Refresh(infoPtr, hdc); | 
 | 	EndPaint(infoPtr->hwndSelf, &ps); | 
 |     } | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Paints/Repaints the listview control. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] hdc : device context handle | 
 |  * [I] options : drawing options | 
 |  * | 
 |  * RETURN: | 
 |  * Zero | 
 |  */ | 
 | static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options) | 
 | { | 
 |     FIXME("Partial Stub: (hdc=%p options=0x%08lx)\n", hdc, options); | 
 |  | 
 |     if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf)) | 
 |         return 0; | 
 |  | 
 |     if (options & PRF_ERASEBKGND) | 
 |         LISTVIEW_EraseBkgnd(infoPtr, hdc); | 
 |  | 
 |     if (options & PRF_CLIENT) | 
 |         LISTVIEW_Paint(infoPtr, hdc); | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Processes double click messages (right mouse button). | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] wKey : key flag | 
 |  * [I] x,y : mouse coordinate | 
 |  * | 
 |  * RETURN: | 
 |  * Zero | 
 |  */ | 
 | static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y) | 
 | { | 
 |     LVHITTESTINFO lvHitTestInfo; | 
 |      | 
 |     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y); | 
 |  | 
 |     /* send NM_RELEASEDCAPTURE notification */ | 
 |     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0; | 
 |  | 
 |     /* send NM_RDBLCLK notification */ | 
 |     lvHitTestInfo.pt.x = x; | 
 |     lvHitTestInfo.pt.y = y; | 
 |     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE); | 
 |     notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo); | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Processes mouse down messages (right mouse button). | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] wKey : key flag | 
 |  * [I] x,y : mouse coordinate | 
 |  * | 
 |  * RETURN: | 
 |  * Zero | 
 |  */ | 
 | static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y) | 
 | { | 
 |     LVHITTESTINFO lvHitTestInfo; | 
 |     INT nItem; | 
 |  | 
 |     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y); | 
 |  | 
 |     /* send NM_RELEASEDCAPTURE notification */ | 
 |     if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0; | 
 |  | 
 |     /* make sure the listview control window has the focus */ | 
 |     if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf); | 
 |  | 
 |     /* set right button down flag */ | 
 |     infoPtr->bRButtonDown = TRUE; | 
 |  | 
 |     /* determine the index of the selected item */ | 
 |     lvHitTestInfo.pt.x = x; | 
 |     lvHitTestInfo.pt.y = y; | 
 |     nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE); | 
 |    | 
 |     if ((nItem >= 0) && (nItem < infoPtr->nItemCount)) | 
 |     { | 
 | 	LISTVIEW_SetItemFocus(infoPtr, nItem); | 
 | 	if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) && | 
 |             !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED)) | 
 | 	    LISTVIEW_SetSelection(infoPtr, nItem); | 
 |     } | 
 |     else | 
 |     { | 
 | 	LISTVIEW_DeselectAll(infoPtr); | 
 |     } | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Processes mouse up messages (right mouse button). | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] wKey : key flag | 
 |  * [I] x,y : mouse coordinate | 
 |  * | 
 |  * RETURN: | 
 |  * Zero | 
 |  */ | 
 | static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y) | 
 | { | 
 |     LVHITTESTINFO lvHitTestInfo; | 
 |     POINT pt; | 
 |  | 
 |     TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y); | 
 |  | 
 |     if (!infoPtr->bRButtonDown) return 0; | 
 |   | 
 |     /* set button flag */ | 
 |     infoPtr->bRButtonDown = FALSE; | 
 |  | 
 |     /* Send NM_RClICK notification */ | 
 |     lvHitTestInfo.pt.x = x; | 
 |     lvHitTestInfo.pt.y = y; | 
 |     LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE); | 
 |     if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0; | 
 |  | 
 |     /* Change to screen coordinate for WM_CONTEXTMENU */ | 
 |     pt = lvHitTestInfo.pt; | 
 |     ClientToScreen(infoPtr->hwndSelf, &pt); | 
 |  | 
 |     /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */ | 
 |     SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU, | 
 | 		 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y)); | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Sets the cursor. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] hwnd : window handle of window containing the cursor | 
 |  * [I] nHittest : hit-test code | 
 |  * [I] wMouseMsg : ideintifier of the mouse message | 
 |  * | 
 |  * RETURN: | 
 |  * TRUE if cursor is set | 
 |  * FALSE otherwise | 
 |  */ | 
 | static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg) | 
 | { | 
 |     LVHITTESTINFO lvHitTestInfo; | 
 |  | 
 |     if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE; | 
 |  | 
 |     if(!infoPtr->hHotCursor)  return FALSE; | 
 |  | 
 |     GetCursorPos(&lvHitTestInfo.pt); | 
 |     if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE; | 
 |  | 
 |     SetCursor(infoPtr->hHotCursor); | 
 |  | 
 |     return TRUE; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Sets the focus. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] hwndLoseFocus : handle of previously focused window | 
 |  * | 
 |  * RETURN: | 
 |  * Zero | 
 |  */ | 
 | static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus) | 
 | { | 
 |     TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus); | 
 |  | 
 |     /* if we have the focus already, there's nothing to do */ | 
 |     if (infoPtr->bFocus) return 0; | 
 |     | 
 |     /* send NM_SETFOCUS notification */ | 
 |     if (!notify(infoPtr, NM_SETFOCUS)) return 0; | 
 |  | 
 |     /* set window focus flag */ | 
 |     infoPtr->bFocus = TRUE; | 
 |  | 
 |     /* put the focus rect back on */ | 
 |     LISTVIEW_ShowFocusRect(infoPtr, TRUE); | 
 |  | 
 |     /* redraw all visible selected items */ | 
 |     LISTVIEW_InvalidateSelectedItems(infoPtr); | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Sets the font. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] fRedraw : font handle | 
 |  * [I] fRedraw : redraw flag | 
 |  * | 
 |  * RETURN: | 
 |  * Zero | 
 |  */ | 
 | static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw) | 
 | { | 
 |     HFONT oldFont = infoPtr->hFont; | 
 |  | 
 |     TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw); | 
 |  | 
 |     infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont; | 
 |     if (infoPtr->hFont == oldFont) return 0; | 
 |      | 
 |     LISTVIEW_SaveTextMetrics(infoPtr); | 
 |  | 
 |     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT) | 
 | 	SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0)); | 
 |  | 
 |     if (fRedraw) LISTVIEW_InvalidateList(infoPtr); | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Message handling for WM_SETREDRAW. | 
 |  * For the Listview, it invalidates the entire window (the doc specifies otherwise) | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] bRedraw: state of redraw flag | 
 |  * | 
 |  * RETURN: | 
 |  * DefWinProc return value | 
 |  */ | 
 | static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw) | 
 | { | 
 |     TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw); | 
 |  | 
 |     /* we cannot use straight equality here because _any_ non-zero value is TRUE */ | 
 |     if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0; | 
 |  | 
 |     infoPtr->bRedraw = bRedraw; | 
 |  | 
 |     if(!bRedraw) return 0; | 
 |      | 
 |     if (is_autoarrange(infoPtr)) | 
 | 	LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); | 
 |     LISTVIEW_UpdateScroll(infoPtr); | 
 |  | 
 |     /* despite what the WM_SETREDRAW docs says, apps expect us | 
 |      * to invalidate the listview here... stupid! */ | 
 |     LISTVIEW_InvalidateList(infoPtr); | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Resizes the listview control. This function processes WM_SIZE | 
 |  * messages.  At this time, the width and height are not used. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] Width : new width | 
 |  * [I] Height : new height | 
 |  * | 
 |  * RETURN: | 
 |  * Zero | 
 |  */ | 
 | static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height) | 
 | { | 
 |     RECT rcOld = infoPtr->rcList; | 
 |  | 
 |     TRACE("(width=%d, height=%d)\n", Width, Height); | 
 |  | 
 |     LISTVIEW_UpdateSize(infoPtr); | 
 |     if (EqualRect(&rcOld, &infoPtr->rcList)) return 0; | 
 |    | 
 |     /* do not bother with display related stuff if we're not redrawing */  | 
 |     if (!is_redrawing(infoPtr)) return 0; | 
 |      | 
 |     if (is_autoarrange(infoPtr))  | 
 | 	LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); | 
 |  | 
 |     LISTVIEW_UpdateScroll(infoPtr); | 
 |  | 
 |     /* refresh all only for lists whose height changed significantly */ | 
 |     if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&  | 
 | 	(rcOld.bottom - rcOld.top) / infoPtr->nItemHeight != | 
 | 	(infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight) | 
 | 	LISTVIEW_InvalidateList(infoPtr); | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Sets the size information. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * | 
 |  * RETURN: | 
 |  *  None | 
 |  */ | 
 | static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr) | 
 | { | 
 |     UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 |  | 
 |     TRACE("uView=%d, rcList(old)=%s\n", uView, wine_dbgstr_rect(&infoPtr->rcList)); | 
 |      | 
 |     GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList); | 
 |  | 
 |     if (uView == LVS_LIST) | 
 |     { | 
 | 	/* Apparently the "LIST" style is supposed to have the same | 
 | 	 * number of items in a column even if there is no scroll bar. | 
 | 	 * Since if a scroll bar already exists then the bottom is already | 
 | 	 * reduced, only reduce if the scroll bar does not currently exist. | 
 | 	 * The "2" is there to mimic the native control. I think it may be | 
 | 	 * related to either padding or edges.  (GLA 7/2002) | 
 | 	 */ | 
 |     	if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL)) | 
 | 	    infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL); | 
 |         infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0); | 
 |     } | 
 |     else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER)) | 
 |     { | 
 | 	HDLAYOUT hl; | 
 | 	WINDOWPOS wp; | 
 |  | 
 | 	hl.prc = &infoPtr->rcList; | 
 | 	hl.pwpos = ℘ | 
 | 	Header_Layout(infoPtr->hwndHeader, &hl); | 
 |  | 
 | 	SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags); | 
 |  | 
 | 	infoPtr->rcList.top = max(wp.cy, 0); | 
 |     } | 
 |  | 
 |     TRACE("  rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList)); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Processes WM_STYLECHANGED messages. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] wStyleType : window style type (normal or extended) | 
 |  * [I] lpss : window style information | 
 |  * | 
 |  * RETURN: | 
 |  * Zero | 
 |  */ | 
 | static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType, | 
 |                                  const STYLESTRUCT *lpss) | 
 | { | 
 |     UINT uNewView = lpss->styleNew & LVS_TYPEMASK; | 
 |     UINT uOldView = lpss->styleOld & LVS_TYPEMASK; | 
 |  | 
 |     TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n", | 
 |           wStyleType, lpss->styleOld, lpss->styleNew); | 
 |  | 
 |     if (wStyleType != GWL_STYLE) return 0; | 
 |    | 
 |     /* FIXME: if LVS_NOSORTHEADER changed, update header */ | 
 |     /*        what if LVS_OWNERDATA changed? */ | 
 |     /*        or LVS_SINGLESEL */ | 
 |     /*        or LVS_SORT{AS,DES}CENDING */ | 
 |  | 
 |     infoPtr->dwStyle = lpss->styleNew; | 
 |  | 
 |     if (((lpss->styleOld & WS_HSCROLL) != 0)&& | 
 |         ((lpss->styleNew & WS_HSCROLL) == 0)) | 
 |        ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE); | 
 |  | 
 |     if (((lpss->styleOld & WS_VSCROLL) != 0)&& | 
 |         ((lpss->styleNew & WS_VSCROLL) == 0)) | 
 |        ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE); | 
 |  | 
 |     if (uNewView != uOldView) | 
 |     { | 
 |     	SIZE oldIconSize = infoPtr->iconSize; | 
 |     	HIMAGELIST himl; | 
 |      | 
 |         SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0); | 
 |     	ShowWindow(infoPtr->hwndHeader, SW_HIDE); | 
 |  | 
 |         ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE); | 
 |         SetRectEmpty(&infoPtr->rcFocus); | 
 |  | 
 |         himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall); | 
 |         set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON); | 
 |      | 
 |         if (uNewView == LVS_ICON) | 
 |         { | 
 |             if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy)) | 
 |             { | 
 | 	  	TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n", | 
 | 		      oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy); | 
 | 	        LISTVIEW_SetIconSpacing(infoPtr, 0, 0); | 
 |             } | 
 |         } | 
 |         else if (uNewView == LVS_REPORT) | 
 |         { | 
 |             HDLAYOUT hl; | 
 |             WINDOWPOS wp; | 
 |  | 
 |             hl.prc = &infoPtr->rcList; | 
 |             hl.pwpos = ℘ | 
 |             Header_Layout(infoPtr->hwndHeader, &hl); | 
 |             SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags); | 
 |         } | 
 |  | 
 | 	LISTVIEW_UpdateItemSize(infoPtr); | 
 |     } | 
 |  | 
 |     if (uNewView == LVS_REPORT) | 
 | 	ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL); | 
 |       | 
 |     if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) && | 
 | 	 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) ) | 
 | 	 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT); | 
 |  | 
 |     /* update the size of the client area */ | 
 |     LISTVIEW_UpdateSize(infoPtr); | 
 |  | 
 |     /* add scrollbars if needed */ | 
 |     LISTVIEW_UpdateScroll(infoPtr); | 
 |  | 
 |     /* invalidate client area + erase background */ | 
 |     LISTVIEW_InvalidateList(infoPtr); | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Window procedure of the listview control. | 
 |  * | 
 |  */ | 
 | static LRESULT WINAPI | 
 | LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) | 
 | { | 
 |   LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0); | 
 |  | 
 |   TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam); | 
 |  | 
 |   if (!infoPtr && (uMsg != WM_CREATE)) | 
 |     return DefWindowProcW(hwnd, uMsg, wParam, lParam); | 
 |  | 
 |   switch (uMsg) | 
 |   { | 
 |   case LVM_APPROXIMATEVIEWRECT: | 
 |     return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam, | 
 |                                         LOWORD(lParam), HIWORD(lParam)); | 
 |   case LVM_ARRANGE: | 
 |     return LISTVIEW_Arrange(infoPtr, (INT)wParam); | 
 |  | 
 | /* case LVM_CANCELEDITLABEL: */ | 
 |  | 
 |   case LVM_CREATEDRAGIMAGE: | 
 |     return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam); | 
 |  | 
 |   case LVM_DELETEALLITEMS: | 
 |     return LISTVIEW_DeleteAllItems(infoPtr); | 
 |  | 
 |   case LVM_DELETECOLUMN: | 
 |     return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam); | 
 |  | 
 |   case LVM_DELETEITEM: | 
 |     return LISTVIEW_DeleteItem(infoPtr, (INT)wParam); | 
 |  | 
 |   case LVM_EDITLABELW: | 
 |     return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE); | 
 |  | 
 |   case LVM_EDITLABELA: | 
 |     return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE); | 
 |  | 
 |   /* case LVM_ENABLEGROUPVIEW: */ | 
 |  | 
 |   case LVM_ENSUREVISIBLE: | 
 |     return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam); | 
 |  | 
 |   case LVM_FINDITEMW: | 
 |     return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam); | 
 |  | 
 |   case LVM_FINDITEMA: | 
 |     return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam); | 
 |  | 
 |   case LVM_GETBKCOLOR: | 
 |     return infoPtr->clrBk; | 
 |  | 
 |   /* case LVM_GETBKIMAGE: */ | 
 |  | 
 |   case LVM_GETCALLBACKMASK: | 
 |     return infoPtr->uCallbackMask; | 
 |  | 
 |   case LVM_GETCOLUMNA: | 
 |     return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE); | 
 |  | 
 |   case LVM_GETCOLUMNW: | 
 |     return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE); | 
 |  | 
 |   case LVM_GETCOLUMNORDERARRAY: | 
 |     return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam); | 
 |  | 
 |   case LVM_GETCOLUMNWIDTH: | 
 |     return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam); | 
 |  | 
 |   case LVM_GETCOUNTPERPAGE: | 
 |     return LISTVIEW_GetCountPerPage(infoPtr); | 
 |  | 
 |   case LVM_GETEDITCONTROL: | 
 |     return (LRESULT)infoPtr->hwndEdit; | 
 |  | 
 |   case LVM_GETEXTENDEDLISTVIEWSTYLE: | 
 |     return infoPtr->dwLvExStyle; | 
 |  | 
 |   /* case LVM_GETGROUPINFO: */ | 
 |  | 
 |   /* case LVM_GETGROUPMETRICS: */ | 
 |  | 
 |   case LVM_GETHEADER: | 
 |     return (LRESULT)infoPtr->hwndHeader; | 
 |  | 
 |   case LVM_GETHOTCURSOR: | 
 |     return (LRESULT)infoPtr->hHotCursor; | 
 |  | 
 |   case LVM_GETHOTITEM: | 
 |     return infoPtr->nHotItem; | 
 |  | 
 |   case LVM_GETHOVERTIME: | 
 |     return infoPtr->dwHoverTime; | 
 |  | 
 |   case LVM_GETIMAGELIST: | 
 |     return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam); | 
 |  | 
 |   /* case LVM_GETINSERTMARK: */ | 
 |  | 
 |   /* case LVM_GETINSERTMARKCOLOR: */ | 
 |  | 
 |   /* case LVM_GETINSERTMARKRECT: */ | 
 |  | 
 |   case LVM_GETISEARCHSTRINGA: | 
 |   case LVM_GETISEARCHSTRINGW: | 
 |     FIXME("LVM_GETISEARCHSTRING: unimplemented\n"); | 
 |     return FALSE; | 
 |  | 
 |   case LVM_GETITEMA: | 
 |     return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE); | 
 |  | 
 |   case LVM_GETITEMW: | 
 |     return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE); | 
 |  | 
 |   case LVM_GETITEMCOUNT: | 
 |     return infoPtr->nItemCount; | 
 |  | 
 |   case LVM_GETITEMPOSITION: | 
 |     return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam); | 
 |  | 
 |   case LVM_GETITEMRECT: | 
 |     return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam); | 
 |  | 
 |   case LVM_GETITEMSPACING: | 
 |     return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam); | 
 |  | 
 |   case LVM_GETITEMSTATE: | 
 |     return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam); | 
 |  | 
 |   case LVM_GETITEMTEXTA: | 
 |     return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE); | 
 |  | 
 |   case LVM_GETITEMTEXTW: | 
 |     return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE); | 
 |  | 
 |   case LVM_GETNEXTITEM: | 
 |     return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam)); | 
 |  | 
 |   case LVM_GETNUMBEROFWORKAREAS: | 
 |     FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n"); | 
 |     return 1; | 
 |  | 
 |   case LVM_GETORIGIN: | 
 |     if (!lParam) return FALSE; | 
 |     LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam); | 
 |     return TRUE; | 
 |  | 
 |   /* case LVM_GETOUTLINECOLOR: */ | 
 |  | 
 |   /* case LVM_GETSELECTEDCOLUMN: */ | 
 |  | 
 |   case LVM_GETSELECTEDCOUNT: | 
 |     return LISTVIEW_GetSelectedCount(infoPtr); | 
 |  | 
 |   case LVM_GETSELECTIONMARK: | 
 |     return infoPtr->nSelectionMark; | 
 |  | 
 |   case LVM_GETSTRINGWIDTHA: | 
 |     return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE); | 
 |  | 
 |   case LVM_GETSTRINGWIDTHW: | 
 |     return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE); | 
 |  | 
 |   case LVM_GETSUBITEMRECT: | 
 |     return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam); | 
 |  | 
 |   case LVM_GETTEXTBKCOLOR: | 
 |     return infoPtr->clrTextBk; | 
 |  | 
 |   case LVM_GETTEXTCOLOR: | 
 |     return infoPtr->clrText; | 
 |  | 
 |   /* case LVM_GETTILEINFO: */ | 
 |  | 
 |   /* case LVM_GETTILEVIEWINFO: */ | 
 |  | 
 |   case LVM_GETTOOLTIPS: | 
 |     if( !infoPtr->hwndToolTip ) | 
 |         infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd ); | 
 |     return (LRESULT)infoPtr->hwndToolTip; | 
 |  | 
 |   case LVM_GETTOPINDEX: | 
 |     return LISTVIEW_GetTopIndex(infoPtr); | 
 |  | 
 |   /*case LVM_GETUNICODEFORMAT: | 
 |     FIXME("LVM_GETUNICODEFORMAT: unimplemented\n"); | 
 |     return FALSE;*/ | 
 |  | 
 |   /* case LVM_GETVIEW: */ | 
 |  | 
 |   case LVM_GETVIEWRECT: | 
 |     return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam); | 
 |  | 
 |   case LVM_GETWORKAREAS: | 
 |     FIXME("LVM_GETWORKAREAS: unimplemented\n"); | 
 |     return FALSE; | 
 |  | 
 |   /* case LVM_HASGROUP: */ | 
 |  | 
 |   case LVM_HITTEST: | 
 |     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE); | 
 |  | 
 |   case LVM_INSERTCOLUMNA: | 
 |     return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE); | 
 |  | 
 |   case LVM_INSERTCOLUMNW: | 
 |     return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE); | 
 |  | 
 |   /* case LVM_INSERTGROUP: */ | 
 |  | 
 |   /* case LVM_INSERTGROUPSORTED: */ | 
 |  | 
 |   case LVM_INSERTITEMA: | 
 |     return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE); | 
 |  | 
 |   case LVM_INSERTITEMW: | 
 |     return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE); | 
 |  | 
 |   /* case LVM_INSERTMARKHITTEST: */ | 
 |  | 
 |   /* case LVM_ISGROUPVIEWENABLED: */ | 
 |  | 
 |   /* case LVM_MAPIDTOINDEX: */ | 
 |  | 
 |   /* case LVM_MAPINDEXTOID: */ | 
 |  | 
 |   /* case LVM_MOVEGROUP: */ | 
 |  | 
 |   /* case LVM_MOVEITEMTOGROUP: */ | 
 |  | 
 |   case LVM_REDRAWITEMS: | 
 |     return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam); | 
 |  | 
 |   /* case LVM_REMOVEALLGROUPS: */ | 
 |  | 
 |   /* case LVM_REMOVEGROUP: */ | 
 |  | 
 |   case LVM_SCROLL: | 
 |     return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam); | 
 |  | 
 |   case LVM_SETBKCOLOR: | 
 |     return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam); | 
 |  | 
 |   /* case LVM_SETBKIMAGE: */ | 
 |  | 
 |   case LVM_SETCALLBACKMASK: | 
 |     infoPtr->uCallbackMask = (UINT)wParam; | 
 |     return TRUE; | 
 |  | 
 |   case LVM_SETCOLUMNA: | 
 |     return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE); | 
 |  | 
 |   case LVM_SETCOLUMNW: | 
 |     return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE); | 
 |  | 
 |   case LVM_SETCOLUMNORDERARRAY: | 
 |     return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam); | 
 |  | 
 |   case LVM_SETCOLUMNWIDTH: | 
 |     return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam)); | 
 |  | 
 |   case LVM_SETEXTENDEDLISTVIEWSTYLE: | 
 |     return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam); | 
 |  | 
 |   /* case LVM_SETGROUPINFO: */ | 
 |  | 
 |   /* case LVM_SETGROUPMETRICS: */ | 
 |  | 
 |   case LVM_SETHOTCURSOR: | 
 |     return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam); | 
 |  | 
 |   case LVM_SETHOTITEM: | 
 |     return LISTVIEW_SetHotItem(infoPtr, (INT)wParam); | 
 |  | 
 |   case LVM_SETHOVERTIME: | 
 |     return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam); | 
 |  | 
 |   case LVM_SETICONSPACING: | 
 |     return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam)); | 
 |  | 
 |   case LVM_SETIMAGELIST: | 
 |     return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam); | 
 |  | 
 |   /* case LVM_SETINFOTIP: */ | 
 |  | 
 |   /* case LVM_SETINSERTMARK: */ | 
 |  | 
 |   /* case LVM_SETINSERTMARKCOLOR: */ | 
 |  | 
 |   case LVM_SETITEMA: | 
 |     return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE); | 
 |  | 
 |   case LVM_SETITEMW: | 
 |     return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE); | 
 |  | 
 |   case LVM_SETITEMCOUNT: | 
 |     return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam); | 
 |  | 
 |   case LVM_SETITEMPOSITION: | 
 |     { | 
 | 	POINT pt; | 
 |         pt.x = (short)LOWORD(lParam); | 
 |         pt.y = (short)HIWORD(lParam); | 
 |         return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt); | 
 |     } | 
 |  | 
 |   case LVM_SETITEMPOSITION32: | 
 |     if (lParam == 0) return FALSE; | 
 |     return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam)); | 
 |  | 
 |   case LVM_SETITEMSTATE: | 
 |     return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam); | 
 |  | 
 |   case LVM_SETITEMTEXTA: | 
 |     return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE); | 
 |  | 
 |   case LVM_SETITEMTEXTW: | 
 |     return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE); | 
 |  | 
 |   /* case LVM_SETOUTLINECOLOR: */ | 
 |  | 
 |   /* case LVM_SETSELECTEDCOLUMN: */ | 
 |  | 
 |   case LVM_SETSELECTIONMARK: | 
 |     return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam); | 
 |  | 
 |   case LVM_SETTEXTBKCOLOR: | 
 |     return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam); | 
 |  | 
 |   case LVM_SETTEXTCOLOR: | 
 |     return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam); | 
 |  | 
 |   /* case LVM_SETTILEINFO: */ | 
 |  | 
 |   /* case LVM_SETTILEVIEWINFO: */ | 
 |  | 
 |   /* case LVM_SETTILEWIDTH: */ | 
 |  | 
 |   case LVM_SETTOOLTIPS: | 
 |     return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam); | 
 |  | 
 |   /* case LVM_SETUNICODEFORMAT: */ | 
 |  | 
 |   /* case LVM_SETVIEW: */ | 
 |  | 
 |   /* case LVM_SETWORKAREAS: */ | 
 |  | 
 |   /* case LVM_SORTGROUPS: */ | 
 |  | 
 |   case LVM_SORTITEMS: | 
 |     return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam); | 
 |  | 
 |   /* LVM_SORTITEMSEX: */ | 
 |  | 
 |   case LVM_SUBITEMHITTEST: | 
 |     return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE); | 
 |  | 
 |   case LVM_UPDATE: | 
 |     return LISTVIEW_Update(infoPtr, (INT)wParam); | 
 |  | 
 |   case WM_CHAR: | 
 |     return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam ); | 
 |  | 
 |   case WM_COMMAND: | 
 |     return LISTVIEW_Command(infoPtr, wParam, lParam); | 
 |  | 
 |   case WM_CREATE: | 
 |     return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam); | 
 |  | 
 |   case WM_DESTROY: | 
 |     return LISTVIEW_Destroy(infoPtr); | 
 |  | 
 |   case WM_ENABLE: | 
 |     return LISTVIEW_Enable(infoPtr, (BOOL)wParam); | 
 |  | 
 |   case WM_ERASEBKGND: | 
 |     return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam); | 
 |  | 
 |   case WM_GETDLGCODE: | 
 |     return DLGC_WANTCHARS | DLGC_WANTARROWS; | 
 |  | 
 |   case WM_GETFONT: | 
 |     return (LRESULT)infoPtr->hFont; | 
 |  | 
 |   case WM_HSCROLL: | 
 |     return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam); | 
 |  | 
 |   case WM_KEYDOWN: | 
 |     return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam); | 
 |  | 
 |   case WM_KILLFOCUS: | 
 |     return LISTVIEW_KillFocus(infoPtr); | 
 |  | 
 |   case WM_LBUTTONDBLCLK: | 
 |     return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); | 
 |  | 
 |   case WM_LBUTTONDOWN: | 
 |     return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); | 
 |  | 
 |   case WM_LBUTTONUP: | 
 |     return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); | 
 |  | 
 |   case WM_MOUSEMOVE: | 
 |     return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); | 
 |  | 
 |   case WM_MOUSEHOVER: | 
 |     return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); | 
 |  | 
 |   case WM_NCDESTROY: | 
 |     return LISTVIEW_NCDestroy(infoPtr); | 
 |  | 
 |   case WM_NCPAINT: | 
 |     if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam)) | 
 |         return 0; | 
 |     goto fwd_msg; | 
 |  | 
 |   case WM_NOTIFY: | 
 |     if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader) | 
 |         return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam); | 
 |     else return 0; | 
 |  | 
 |   case WM_NOTIFYFORMAT: | 
 |     return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam); | 
 |  | 
 |   case WM_PRINTCLIENT: | 
 |     return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam); | 
 |  | 
 |   case WM_PAINT: | 
 |     return LISTVIEW_Paint(infoPtr, (HDC)wParam); | 
 |  | 
 |   case WM_RBUTTONDBLCLK: | 
 |     return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); | 
 |  | 
 |   case WM_RBUTTONDOWN: | 
 |     return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); | 
 |  | 
 |   case WM_RBUTTONUP: | 
 |     return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam)); | 
 |  | 
 |   case WM_SETCURSOR: | 
 |     if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam))) | 
 |       return TRUE; | 
 |     goto fwd_msg; | 
 |  | 
 |   case WM_SETFOCUS: | 
 |     return LISTVIEW_SetFocus(infoPtr, (HWND)wParam); | 
 |  | 
 |   case WM_SETFONT: | 
 |     return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam); | 
 |  | 
 |   case WM_SETREDRAW: | 
 |     return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam); | 
 |  | 
 |   case WM_SIZE: | 
 |     return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam)); | 
 |  | 
 |   case WM_STYLECHANGED: | 
 |     return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam); | 
 |  | 
 |   case WM_SYSCOLORCHANGE: | 
 |     COMCTL32_RefreshSysColors(); | 
 |     return 0; | 
 |  | 
 | /*	case WM_TIMER: */ | 
 |   case WM_THEMECHANGED: | 
 |     return LISTVIEW_ThemeChanged(infoPtr); | 
 |  | 
 |   case WM_VSCROLL: | 
 |     return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam); | 
 |  | 
 |   case WM_MOUSEWHEEL: | 
 |       if (wParam & (MK_SHIFT | MK_CONTROL)) | 
 |           return DefWindowProcW(hwnd, uMsg, wParam, lParam); | 
 |       return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam)); | 
 |  | 
 |   case WM_WINDOWPOSCHANGED: | 
 |       if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))  | 
 |       { | 
 |       UINT uView = infoPtr->dwStyle & LVS_TYPEMASK; | 
 | 	  SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE | | 
 | 		       SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE); | 
 |  | 
 |       if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT)) | 
 |       { | 
 |           MEASUREITEMSTRUCT mis; | 
 |           mis.CtlType = ODT_LISTVIEW; | 
 |           mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID); | 
 |           mis.itemID = -1; | 
 |           mis.itemWidth = 0; | 
 |           mis.itemData = 0; | 
 |           mis.itemHeight= infoPtr->nItemHeight; | 
 |           SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis); | 
 |           if (infoPtr->nItemHeight != max(mis.itemHeight, 1)) | 
 |               infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1); | 
 |       } | 
 |  | 
 | 	  LISTVIEW_UpdateSize(infoPtr); | 
 | 	  LISTVIEW_UpdateScroll(infoPtr); | 
 |       } | 
 |       return DefWindowProcW(hwnd, uMsg, wParam, lParam); | 
 |  | 
 | /*	case WM_WININICHANGE: */ | 
 |  | 
 |   default: | 
 |     if ((uMsg >= WM_USER) && (uMsg < WM_APP)) | 
 |       ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam); | 
 |  | 
 |   fwd_msg: | 
 |     /* call default window procedure */ | 
 |     return DefWindowProcW(hwnd, uMsg, wParam, lParam); | 
 |   } | 
 |  | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Registers the window class. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * None | 
 |  * | 
 |  * RETURN: | 
 |  * None | 
 |  */ | 
 | void LISTVIEW_Register(void) | 
 | { | 
 |     WNDCLASSW wndClass; | 
 |  | 
 |     ZeroMemory(&wndClass, sizeof(WNDCLASSW)); | 
 |     wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS; | 
 |     wndClass.lpfnWndProc = LISTVIEW_WindowProc; | 
 |     wndClass.cbClsExtra = 0; | 
 |     wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *); | 
 |     wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW); | 
 |     wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); | 
 |     wndClass.lpszClassName = WC_LISTVIEWW; | 
 |     RegisterClassW(&wndClass); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Unregisters the window class. | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * None | 
 |  * | 
 |  * RETURN: | 
 |  * None | 
 |  */ | 
 | void LISTVIEW_Unregister(void) | 
 | { | 
 |     UnregisterClassW(WC_LISTVIEWW, NULL); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Handle any WM_COMMAND messages | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] wParam : the first message parameter | 
 |  * [I] lParam : the second message parameter | 
 |  * | 
 |  * RETURN: | 
 |  *   Zero. | 
 |  */ | 
 | static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam) | 
 | { | 
 |     switch (HIWORD(wParam)) | 
 |     { | 
 | 	case EN_UPDATE: | 
 | 	{ | 
 | 	    /* | 
 | 	     * Adjust the edit window size | 
 | 	     */ | 
 | 	    WCHAR buffer[1024]; | 
 | 	    HDC           hdc = GetDC(infoPtr->hwndEdit); | 
 |             HFONT         hFont, hOldFont = 0; | 
 | 	    RECT	  rect; | 
 | 	    SIZE	  sz; | 
 | 	    int		  len; | 
 |  | 
 | 	    if (!infoPtr->hwndEdit || !hdc) return 0; | 
 | 	    len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0])); | 
 | 	    GetWindowRect(infoPtr->hwndEdit, &rect); | 
 |  | 
 |             /* Select font to get the right dimension of the string */ | 
 |             hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0); | 
 |             if(hFont != 0) | 
 |             { | 
 |                 hOldFont = SelectObject(hdc, hFont); | 
 |             } | 
 |  | 
 | 	    if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz)) | 
 | 	    { | 
 |                 TEXTMETRICW textMetric; | 
 |  | 
 |                 /* Add Extra spacing for the next character */ | 
 |                 GetTextMetricsW(hdc, &textMetric); | 
 |                 sz.cx += (textMetric.tmMaxCharWidth * 2); | 
 |  | 
 | 		SetWindowPos ( | 
 | 		    infoPtr->hwndEdit, | 
 | 		    HWND_TOP, | 
 | 		    0, | 
 | 		    0, | 
 | 		    sz.cx, | 
 | 		    rect.bottom - rect.top, | 
 | 		    SWP_DRAWFRAME|SWP_NOMOVE); | 
 | 	    } | 
 |             if(hFont != 0) | 
 |                 SelectObject(hdc, hOldFont); | 
 |  | 
 | 	    ReleaseDC(infoPtr->hwndEdit, hdc); | 
 |  | 
 | 	    break; | 
 | 	} | 
 |  | 
 | 	default: | 
 | 	  return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam); | 
 |     } | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Subclassed edit control windproc function | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] hwnd : the edit window handle | 
 |  * [I] uMsg : the message that is to be processed | 
 |  * [I] wParam : first message parameter | 
 |  * [I] lParam : second message parameter | 
 |  * [I] isW : TRUE if input is Unicode | 
 |  * | 
 |  * RETURN: | 
 |  *   Zero. | 
 |  */ | 
 | static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW) | 
 | { | 
 |     LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0); | 
 |     BOOL cancel = FALSE; | 
 |  | 
 |     TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n", | 
 | 	  hwnd, uMsg, wParam, lParam, isW); | 
 |  | 
 |     switch (uMsg) | 
 |     { | 
 | 	case WM_GETDLGCODE: | 
 | 	  return DLGC_WANTARROWS | DLGC_WANTALLKEYS; | 
 |  | 
 | 	case WM_KILLFOCUS: | 
 | 	    break; | 
 |  | 
 | 	case WM_DESTROY: | 
 | 	{ | 
 | 	    WNDPROC editProc = infoPtr->EditWndProc; | 
 | 	    infoPtr->EditWndProc = 0; | 
 | 	    SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc); | 
 | 	    return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW); | 
 | 	} | 
 |  | 
 | 	case WM_KEYDOWN: | 
 | 	    if (VK_ESCAPE == (INT)wParam) | 
 | 	    { | 
 | 		cancel = TRUE; | 
 |                 break; | 
 | 	    } | 
 | 	    else if (VK_RETURN == (INT)wParam) | 
 | 		break; | 
 |  | 
 | 	default: | 
 | 	    return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW); | 
 |     } | 
 |  | 
 |     /* kill the edit */ | 
 |     if (infoPtr->hwndEdit) | 
 |     { | 
 | 	LPWSTR buffer = NULL; | 
 |  | 
 |         infoPtr->hwndEdit = 0; | 
 | 	if (!cancel) | 
 | 	{ | 
 | 	    DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd); | 
 |  | 
 | 	    if (len) | 
 | 	    { | 
 | 		if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) ) | 
 | 		{ | 
 | 		    if (isW) GetWindowTextW(hwnd, buffer, len+1); | 
 | 		    else GetWindowTextA(hwnd, (CHAR*)buffer, len+1); | 
 | 		} | 
 | 	    } | 
 | 	} | 
 | 	LISTVIEW_EndEditLabelT(infoPtr, buffer, isW); | 
 |  | 
 | 	if (buffer) Free(buffer); | 
 |  | 
 |     } | 
 |  | 
 |     SendMessageW(hwnd, WM_CLOSE, 0, 0); | 
 |     return 0; | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Subclassed edit control Unicode windproc function | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] hwnd : the edit window handle | 
 |  * [I] uMsg : the message that is to be processed | 
 |  * [I] wParam : first message parameter | 
 |  * [I] lParam : second message parameter | 
 |  * | 
 |  * RETURN: | 
 |  */ | 
 | LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) | 
 | { | 
 |     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Subclassed edit control ANSI windproc function | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] hwnd : the edit window handle | 
 |  * [I] uMsg : the message that is to be processed | 
 |  * [I] wParam : first message parameter | 
 |  * [I] lParam : second message parameter | 
 |  * | 
 |  * RETURN: | 
 |  */ | 
 | LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) | 
 | { | 
 |     return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE); | 
 | } | 
 |  | 
 | /*** | 
 |  * DESCRIPTION: | 
 |  * Creates a subclassed edit cotrol | 
 |  * | 
 |  * PARAMETER(S): | 
 |  * [I] infoPtr : valid pointer to the listview structure | 
 |  * [I] text : initial text for the edit | 
 |  * [I] style : the window style | 
 |  * [I] isW : TRUE if input is Unicode | 
 |  * | 
 |  * RETURN: | 
 |  */ | 
 | static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style, | 
 | 	INT x, INT y, INT width, INT height, BOOL isW) | 
 | { | 
 |     WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' }; | 
 |     HWND hedit; | 
 |     SIZE sz; | 
 |     HDC hdc; | 
 |     HDC hOldFont=0; | 
 |     TEXTMETRICW textMetric; | 
 |     HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE); | 
 |  | 
 |     TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW); | 
 |  | 
 |     style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER; | 
 |     hdc = GetDC(infoPtr->hwndSelf); | 
 |  | 
 |     /* Select the font to get appropriate metric dimensions */ | 
 |     if(infoPtr->hFont != 0) | 
 |         hOldFont = SelectObject(hdc, infoPtr->hFont); | 
 |  | 
 |     /*Get String Length in pixels */ | 
 |     GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz); | 
 |  | 
 |     /*Add Extra spacing for the next character */ | 
 |     GetTextMetricsW(hdc, &textMetric); | 
 |     sz.cx += (textMetric.tmMaxCharWidth * 2); | 
 |  | 
 |     if(infoPtr->hFont != 0) | 
 |         SelectObject(hdc, hOldFont); | 
 |  | 
 |     ReleaseDC(infoPtr->hwndSelf, hdc); | 
 |     if (isW) | 
 | 	hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0); | 
 |     else | 
 | 	hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0); | 
 |  | 
 |     if (!hedit) return 0; | 
 |  | 
 |     infoPtr->EditWndProc = (WNDPROC) | 
 | 	(isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) : | 
 |                SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) ); | 
 |  | 
 |     SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE); | 
 |  | 
 |     return hedit; | 
 | } |