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

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

Version: ~ [ wine-1.1.3 ] ~ [ wine-1.1.2 ] ~ [ wine-1.1.1 ] ~ [ wine-1.1.0 ] ~ [ wine-1.0 ] ~ [ wine-1.0-rc5 ] ~ [ wine-1.0-rc4 ] ~ [ wine-1.0-rc3 ] ~ [ wine-1.0-rc2 ] ~ [ wine-1.0-rc1 ] ~ [ wine-0.9.61 ] ~ [ wine-0.9.60 ] ~ [ wine-0.9.59 ] ~ [ wine-0.9.58 ] ~ [ wine-0.9.57 ] ~ [ wine-0.9.56 ] ~ [ wine-0.9.55 ] ~ [ wine-0.9.54 ] ~ [ wine-0.9.53 ] ~ [ wine-0.9.52 ] ~ [ wine-0.9.51 ] ~ [ wine-0.9.50 ] ~ [ wine-0.9.49 ] ~ [ wine-0.9.48 ] ~ [ wine-0.9.47 ] ~ [ wine-0.9.46 ] ~ [ wine-0.9.45 ] ~ [ wine-0.9.44 ] ~ [ wine-0.9.43 ] ~ [ wine-0.9.42 ] ~ [ wine-0.9.41 ] ~ [ wine-0.9.40 ] ~ [ wine-0.9.39 ] ~ [ wine-0.9.38 ] ~ [ wine-0.9.37 ] ~ [ wine-0.9.36 ] ~ [ wine-0.9.35 ] ~ [ wine-0.9.34 ] ~ [ wine-0.9.33 ] ~ [ wine-0.9.32 ] ~ [ wine-0.9.31 ] ~ [ wine-0.9.30 ] ~ [ wine-0.9.29 ] ~ [ wine-0.9.28 ] ~ [ wine-0.9.27 ] ~ [ wine-0.9.26 ] ~ [ wine-0.9.25 ] ~ [ wine-0.9.24 ] ~ [ wine-0.9.23 ] ~ [ wine-0.9.22 ] ~ [ wine-0.9.21 ] ~ [ wine-0.9.20 ] ~ [ wine-0.9.19 ] ~ [ wine-0.9.18 ] ~ [ wine-0.9.17 ] ~ [ wine-0.9.16 ] ~ [ wine-0.9.15 ] ~ [ wine-0.9.14 ] ~ [ wine-0.9.13 ] ~ [ wine-0.9.12 ] ~ [ wine-0.9.11 ] ~ [ wine-0.9.10 ] ~ [ wine-0.9.9 ] ~ [ wine-0.9.8 ] ~ [ wine-0.9.7 ] ~ [ wine-0.9.6 ] ~ [ wine-0.9.5 ] ~ [ wine-0.9.4 ] ~ [ wine-0.9.3 ] ~ [ wine-0.9.2 ] ~ [ wine-0.9.1 ] ~ [ wine-0.9 ] ~ [ wine20050930 ] ~ [ wine20050830 ] ~ [ wine20050725 ] ~ [ wine20050628 ] ~ [ wine20050524 ] ~ [ wine20050419 ] ~ [ wine20050310 ] ~ [ wine20050211 ] ~ [ wine20050111 ] ~ [ wine20041201 ] ~ [ wine20041019 ] ~ [ wine20040914 ] ~ [ wine20040813 ] ~ [ wine20040716 ] ~ [ wine20040615 ] ~ [ wine20040505 ] ~ [ wine20040408 ] ~ [ wine20040309 ] ~ [ wine20040213 ] ~ [ wine20040121 ] ~ [ wine20031212 ] ~ [ wine20031118 ] ~ [ wine20031016 ] ~ [ wine20030911 ] ~ [ wine20030813 ] ~ [ wine20030709 ] ~ [ wine20030618 ] ~ [ wine20030508 ] ~ [ wine20030408 ] ~ [ wine20030318 ] ~ [ wine20030219 ] ~ [ wine20030115 ] ~ [ wine20021219 ] ~ [ wine20021125 ] ~ [ wine20021031 ] ~ [ wine20021007 ] ~ [ wine20020904 ] ~ [ wine20020804 ] ~ [ wine20020710 ] ~ [ wine20020605 ] ~ [ wine20020509 ] ~ [ wine20020411 ] ~ [ wine20020310 ] ~ [ wine20020228 ] ~ [ wine20011226 ] ~ [ wine20011108 ] ~ [ wine20011004 ] ~ [ wine20010824 ] ~ [ wine20010731 ] ~ [ wine20010629 ] ~ [ wine20010510 ] ~ [ wine20010418 ] ~ [ wine20010326 ] ~ [ wine20010305 ] ~ [ wine20010216 ] ~ [ wine20010112 ] ~ [ wine20001222 ] ~ [ wine20001202 ] ~ [ wine20001026 ] ~ [ wine20001002 ] ~ [ wine20000909 ] ~ [ wine20000821 ] ~ [ wine20000801 ] ~ [ wine20000716 ] ~ [ wine20000326 ] ~ [ wine20000227 ] ~ [ wine20000130 ] ~ [ wine20000109 ] ~

  1 /*
  2  * Listview control
  3  *
  4  * Copyright 1998, 1999 Eric Kohl
  5  * Copyright 1999 Luc Tourangeau
  6  * Copyright 2000 Jason Mawdsley
  7  * Copyright 2001 CodeWeavers Inc.
  8  * Copyright 2002 Dimitrie O. Paun
  9  *
 10  * This library is free software; you can redistribute it and/or
 11  * modify it under the terms of the GNU Lesser General Public
 12  * License as published by the Free Software Foundation; either
 13  * version 2.1 of the License, or (at your option) any later version.
 14  *
 15  * This library is distributed in the hope that it will be useful,
 16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 18  * Lesser General Public License for more details.
 19  *
 20  * You should have received a copy of the GNU Lesser General Public
 21  * License along with this library; if not, write to the Free Software
 22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 23  *
 24  * NOTES
 25  *
 26  * This code was audited for completeness against the documented features
 27  * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
 28  * 
 29  * Unless otherwise noted, we believe this code to be complete, as per
 30  * the specification mentioned above.
 31  * If you discover missing features, or bugs, please note them below.
 32  * 
 33  * TODO:
 34  *
 35  * Default Message Processing
 36  *   -- EN_KILLFOCUS should be handled in WM_COMMAND
 37  *   -- WM_CREATE: create the icon and small icon image lists at this point only if
 38  *      the LVS_SHAREIMAGELISTS style is not specified.
 39  *   -- WM_ERASEBKGND: forward this message to the parent window if the bkgnd
 40  *      color is CLR_NONE.
 41  *   -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
 42  *      or small icon and the LVS_AUTOARRANGE style is specified.
 43  *   -- WM_TIMER
 44  *   -- WM_WININICHANGE
 45  *
 46  * Features
 47  *   -- Hot item handling, mouse hovering
 48  *   -- Workareas support
 49  *   -- Tilemode support
 50  *   -- Groups support
 51  *
 52  * Bugs
 53  *   -- Expand large item in ICON mode when the cursor is flying over the icon or text.
 54  *   -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
 55  *   -- LVA_SNAPTOGRID not implemented
 56  *   -- LISTVIEW_ApproximateViewRect partially implemented
 57  *   -- LISTVIEW_[GS]etColumnOrderArray stubs
 58  *   -- LISTVIEW_SetColumnWidth ignores header images & bitmap
 59  *   -- LISTVIEW_SetIconSpacing is incomplete
 60  *   -- LISTVIEW_SortItems is broken
 61  *   -- LISTVIEW_StyleChanged doesn't handle some changes too well
 62  *
 63  * Speedups
 64  *   -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
 65  *      linear in the number of items in the list, and this is
 66  *      unacceptable for large lists.
 67  *   -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
 68  *      instead of inserting in the right spot
 69  *   -- we should keep an ordered array of coordinates in iconic mode
 70  *      this would allow to frame items (iterator_frameditems),
 71  *      and find nearest item (LVFI_NEARESTXY) a lot more efficiently
 72  *
 73  * Flags
 74  *   -- LVIF_COLUMNS
 75  *   -- LVIF_GROUPID
 76  *   -- LVIF_NORECOMPUTE
 77  *
 78  * States
 79  *   -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
 80  *   -- LVIS_CUT
 81  *   -- LVIS_DROPHILITED
 82  *   -- LVIS_OVERLAYMASK
 83  *
 84  * Styles
 85  *   -- LVS_NOLABELWRAP
 86  *   -- LVS_NOSCROLL (see Q137520)
 87  *   -- LVS_SORTASCENDING, LVS_SORTDESCENDING
 88  *   -- LVS_ALIGNTOP
 89  *   -- LVS_TYPESTYLEMASK
 90  *
 91  * Extended Styles
 92  *   -- LVS_EX_BORDERSELECT
 93  *   -- LVS_EX_FLATSB
 94  *   -- LVS_EX_HEADERDRAGDROP
 95  *   -- LVS_EX_INFOTIP
 96  *   -- LVS_EX_LABELTIP
 97  *   -- LVS_EX_MULTIWORKAREAS
 98  *   -- LVS_EX_ONECLICKACTIVATE
 99  *   -- LVS_EX_REGIONAL
100  *   -- LVS_EX_SIMPLESELECT
101  *   -- LVS_EX_TRACKSELECT
102  *   -- LVS_EX_TWOCLICKACTIVATE
103  *   -- LVS_EX_UNDERLINECOLD
104  *   -- LVS_EX_UNDERLINEHOT
105  *   
106  * Notifications:
107  *   -- LVN_BEGINSCROLL, LVN_ENDSCROLL
108  *   -- LVN_GETINFOTIP
109  *   -- LVN_HOTTRACK
110  *   -- LVN_MARQUEEBEGIN
111  *   -- LVN_ODFINDITEM
112  *   -- LVN_SETDISPINFO
113  *   -- NM_HOVER
114  *   -- LVN_BEGINRDRAG
115  *
116  * Messages:
117  *   -- LVM_CANCELEDITLABEL
118  *   -- LVM_ENABLEGROUPVIEW
119  *   -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
120  *   -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
121  *   -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
122  *   -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
123  *   -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
124  *   -- LVM_GETINSERTMARKRECT
125  *   -- LVM_GETNUMBEROFWORKAREAS
126  *   -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
127  *   -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
128  *   -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
129  *   -- LVM_GETTILEINFO, LVM_SETTILEINFO
130  *   -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
131  *   -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
132  *   -- LVM_GETVIEW, LVM_SETVIEW
133  *   -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
134  *   -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
135  *   -- LVM_INSERTGROUPSORTED
136  *   -- LVM_INSERTMARKHITTEST
137  *   -- LVM_ISGROUPVIEWENABLED
138  *   -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
139  *   -- LVM_MOVEGROUP
140  *   -- LVM_MOVEITEMTOGROUP
141  *   -- LVM_SETINFOTIP
142  *   -- LVM_SETTILEWIDTH
143  *   -- LVM_SORTGROUPS
144  *   -- LVM_SORTITEMSEX
145  *
146  * Macros:
147  *   -- ListView_GetCheckSate, ListView_SetCheckState
148  *   -- ListView_GetHoverTime, ListView_SetHoverTime
149  *   -- ListView_GetISearchString
150  *   -- ListView_GetNumberOfWorkAreas
151  *   -- ListView_GetOrigin
152  *   -- ListView_GetTextBkColor
153  *   -- ListView_GetUnicodeFormat, ListView_SetUnicodeFormat
154  *   -- ListView_GetWorkAreas, ListView_SetWorkAreas
155  *   -- ListView_SortItemsEx
156  *
157  * Functions:
158  *   -- LVGroupComparE
159  *
160  * Known differences in message stream from native control (not known if
161  * these differences cause problems):
162  *   LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
163  *   LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
164  *   WM_CREATE does not issue WM_QUERYUISTATE and associated registry
165  *     processing for "USEDOUBLECLICKTIME".
166  */
167 
168 #include "config.h"
169 #include "wine/port.h"
170 
171 #include <assert.h>
172 #include <ctype.h>
173 #include <string.h>
174 #include <stdlib.h>
175 #include <stdarg.h>
176 #include <stdio.h>
177 
178 #include "windef.h"
179 #include "winbase.h"
180 #include "winnt.h"
181 #include "wingdi.h"
182 #include "winuser.h"
183 #include "winnls.h"
184 #include "commctrl.h"
185 #include "comctl32.h"
186 #include "uxtheme.h"
187 
188 #include "wine/debug.h"
189 #include "wine/unicode.h"
190 
191 WINE_DEFAULT_DEBUG_CHANNEL(listview);
192 
193 /* make sure you set this to 0 for production use! */
194 #define DEBUG_RANGES 1
195 
196 typedef struct tagCOLUMN_INFO
197 {
198   RECT rcHeader;        /* tracks the header's rectangle */
199   int fmt;              /* same as LVCOLUMN.fmt */
200 } COLUMN_INFO;
201 
202 typedef struct tagITEMHDR
203 {
204   LPWSTR pszText;
205   INT iImage;
206 } ITEMHDR, *LPITEMHDR;
207 
208 typedef struct tagSUBITEM_INFO
209 {
210   ITEMHDR hdr;
211   INT iSubItem;
212 } SUBITEM_INFO;
213 
214 typedef struct tagITEM_INFO
215 {
216   ITEMHDR hdr;
217   UINT state;
218   LPARAM lParam;
219   INT iIndent;
220 } ITEM_INFO;
221 
222 typedef struct tagRANGE
223 {
224   INT lower;
225   INT upper;
226 } RANGE;
227 
228 typedef struct tagRANGES
229 {
230   HDPA hdpa;
231 } *RANGES;
232 
233 typedef struct tagITERATOR
234 {
235   INT nItem;
236   INT nSpecial;
237   RANGE range;
238   RANGES ranges;
239   INT index;
240 } ITERATOR;
241 
242 typedef struct tagDELAYED_ITEM_EDIT
243 {
244   BOOL fEnabled;
245   INT iItem;
246 } DELAYED_ITEM_EDIT;
247 
248 typedef struct tagLISTVIEW_INFO
249 {
250   HWND hwndSelf;
251   HBRUSH hBkBrush;
252   COLORREF clrBk;
253   COLORREF clrText;
254   COLORREF clrTextBk;
255   HIMAGELIST himlNormal;
256   HIMAGELIST himlSmall;
257   HIMAGELIST himlState;
258   BOOL bLButtonDown;
259   BOOL bRButtonDown;
260   POINT ptClickPos;         /* point where the user clicked */ 
261   BOOL bNoItemMetrics;          /* flags if item metrics are not yet computed */
262   INT nItemHeight;
263   INT nItemWidth;
264   RANGES selectionRanges;
265   INT nSelectionMark;
266   INT nHotItem;
267   SHORT notifyFormat;
268   HWND hwndNotify;
269   RECT rcList;                 /* This rectangle is really the window
270                                 * client rectangle possibly reduced by the 
271                                 * horizontal scroll bar and/or header - see 
272                                 * LISTVIEW_UpdateSize. This rectangle offset
273                                 * by the LISTVIEW_GetOrigin value is in
274                                 * client coordinates   */
275   SIZE iconSize;
276   SIZE iconSpacing;
277   SIZE iconStateSize;
278   UINT uCallbackMask;
279   HWND hwndHeader;
280   HCURSOR hHotCursor;
281   HFONT hDefaultFont;
282   HFONT hFont;
283   INT ntmHeight;                /* Some cached metrics of the font used */
284   INT ntmMaxCharWidth;          /* by the listview to draw items */
285   INT nEllipsisWidth;
286   BOOL bRedraw;                 /* Turns on/off repaints & invalidations */
287   BOOL bAutoarrange;            /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
288   BOOL bFocus;
289   BOOL bDoChangeNotify;         /* send change notification messages? */
290   INT nFocusedItem;
291   RECT rcFocus;
292   DWORD dwStyle;                /* the cached window GWL_STYLE */
293   DWORD dwLvExStyle;            /* extended listview style */
294   INT nItemCount;               /* the number of items in the list */
295   HDPA hdpaItems;               /* array ITEM_INFO pointers */
296   HDPA hdpaPosX;                /* maintains the (X, Y) coordinates of the */
297   HDPA hdpaPosY;                /* items in LVS_ICON, and LVS_SMALLICON modes */
298   HDPA hdpaColumns;             /* array of COLUMN_INFO pointers */
299   POINT currIconPos;            /* this is the position next icon will be placed */
300   PFNLVCOMPARE pfnCompare;
301   LPARAM lParamSort;
302   HWND hwndEdit;
303   WNDPROC EditWndProc;
304   INT nEditLabelItem;
305   DWORD dwHoverTime;
306   HWND hwndToolTip;
307 
308   DWORD cditemmode;             /* Keep the custom draw flags for an item/row */
309 
310   DWORD lastKeyPressTimestamp;
311   WPARAM charCode;
312   INT nSearchParamLength;
313   WCHAR szSearchParam[ MAX_PATH ];
314   BOOL bIsDrawing;
315   INT nMeasureItemHeight;
316   INT xTrackLine;               /* The x coefficient of the track line or -1 if none */
317   DELAYED_ITEM_EDIT itemEdit;   /* Pointer to this structure will be the timer ID */
318 } LISTVIEW_INFO;
319 
320 /*
321  * constants
322  */
323 /* How many we debug buffer to allocate */
324 #define DEBUG_BUFFERS 20
325 /* The size of a single debug bbuffer */
326 #define DEBUG_BUFFER_SIZE 256
327 
328 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
329 #define SB_INTERNAL      -1
330 
331 /* maximum size of a label */
332 #define DISP_TEXT_SIZE 512
333 
334 /* padding for items in list and small icon display modes */
335 #define WIDTH_PADDING 12
336 
337 /* padding for items in list, report and small icon display modes */
338 #define HEIGHT_PADDING 1
339 
340 /* offset of items in report display mode */
341 #define REPORT_MARGINX 2
342 
343 /* padding for icon in large icon display mode
344  *   ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
345  *                                 that HITTEST will see.
346  *   ICON_TOP_PADDING_HITABLE - spacing between above and icon.
347  *   ICON_TOP_PADDING - sum of the two above.
348  *   ICON_BOTTOM_PADDING - between bottom of icon and top of text
349  *   LABEL_HOR_PADDING - between text and sides of box
350  *   LABEL_VERT_PADDING - between bottom of text and end of box
351  *
352  *   ICON_LR_PADDING - additional width above icon size.
353  *   ICON_LR_HALF - half of the above value
354  */
355 #define ICON_TOP_PADDING_NOTHITABLE  2
356 #define ICON_TOP_PADDING_HITABLE     2
357 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
358 #define ICON_BOTTOM_PADDING          4
359 #define LABEL_HOR_PADDING            5
360 #define LABEL_VERT_PADDING           7
361 #define ICON_LR_PADDING              16
362 #define ICON_LR_HALF                 (ICON_LR_PADDING/2)
363 
364 /* default label width for items in list and small icon display modes */
365 #define DEFAULT_LABEL_WIDTH 40
366 
367 /* default column width for items in list display mode */
368 #define DEFAULT_COLUMN_WIDTH 128
369 
370 /* Size of "line" scroll for V & H scrolls */
371 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
372 
373 /* Padding between image and label */
374 #define IMAGE_PADDING  2
375 
376 /* Padding behind the label */
377 #define TRAILING_LABEL_PADDING  12
378 #define TRAILING_HEADER_PADDING  11
379 
380 /* Border for the icon caption */
381 #define CAPTION_BORDER  2
382 
383 /* Standard DrawText flags */
384 #define LV_ML_DT_FLAGS  (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
385 #define LV_FL_DT_FLAGS  (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
386 #define LV_SL_DT_FLAGS  (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
387 
388 /* Image index from state */
389 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
390 
391 /* The time in milliseconds to reset the search in the list */
392 #define KEY_DELAY       450
393 
394 /* Dump the LISTVIEW_INFO structure to the debug channel */
395 #define LISTVIEW_DUMP(iP) do { \
396   TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
397         iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
398         iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
399   TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
400         iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
401         iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
402   TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
403         iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
404         iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
405   TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
406 } while(0)
407 
408 static const WCHAR themeClass[] = {'L','i','s','t','V','i','e','w',0};
409 
410 /*
411  * forward declarations
412  */
413 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *, LPLVITEMW, BOOL);
414 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *, INT, LPRECT);
415 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *, INT, LPPOINT);
416 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *, INT, LPPOINT);
417 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *, INT, LPRECT);
418 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *, INT);
419 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *, LPPOINT);
420 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *, LPRECT);
421 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
422 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LVITEMW *, BOOL);
423 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *);
424 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
425 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
426 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
427 static LRESULT LISTVIEW_Command(const LISTVIEW_INFO *, WPARAM, LPARAM);
428 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
429 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *, LPCWSTR, BOOL);
430 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
431 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *, INT, UINT);
432 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
433 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
434 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
435 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *);
436 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
437 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
438 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
439 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
440 
441 /******** Text handling functions *************************************/
442 
443 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
444  * text string. The string may be ANSI or Unicode, in which case
445  * the boolean isW tells us the type of the string.
446  *
447  * The name of the function tell what type of strings it expects:
448  *   W: Unicode, T: ANSI/Unicode - function of isW
449  */
450 
451 static inline BOOL is_textW(LPCWSTR text)
452 {
453     return text != NULL && text != LPSTR_TEXTCALLBACKW;
454 }
455 
456 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
457 {
458     /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
459     return is_textW(text);
460 }
461 
462 static inline int textlenT(LPCWSTR text, BOOL isW)
463 {
464     return !is_textT(text, isW) ? 0 :
465            isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
466 }
467 
468 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
469 {
470     if (isDestW)
471         if (isSrcW) lstrcpynW(dest, src, max);
472         else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
473     else
474         if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
475         else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
476 }
477 
478 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
479 {
480     LPWSTR wstr = (LPWSTR)text;
481 
482     if (!isW && is_textT(text, isW))
483     {
484         INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
485         wstr = Alloc(len * sizeof(WCHAR));
486         if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
487     }
488     TRACE("   wstr=%s\n", text == LPSTR_TEXTCALLBACKW ?  "(callback)" : debugstr_w(wstr));
489     return wstr;
490 }
491 
492 static inline void textfreeT(LPWSTR wstr, BOOL isW)
493 {
494     if (!isW && is_textT(wstr, isW)) Free (wstr);
495 }
496 
497 /*
498  * dest is a pointer to a Unicode string
499  * src is a pointer to a string (Unicode if isW, ANSI if !isW)
500  */
501 static BOOL textsetptrT(LPWSTR *dest, LPCWSTR src, BOOL isW)
502 {
503     BOOL bResult = TRUE;
504     
505     if (src == LPSTR_TEXTCALLBACKW)
506     {
507         if (is_textW(*dest)) Free(*dest);
508         *dest = LPSTR_TEXTCALLBACKW;
509     }
510     else
511     {
512         LPWSTR pszText = textdupTtoW(src, isW);
513         if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
514         bResult = Str_SetPtrW(dest, pszText);
515         textfreeT(pszText, isW);
516     }
517     return bResult;
518 }
519 
520 /*
521  * compares a Unicode to a Unicode/ANSI text string
522  */
523 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
524 {
525     if (!aw) return bt ? -1 : 0;
526     if (!bt) return aw ? 1 : 0;
527     if (aw == LPSTR_TEXTCALLBACKW)
528         return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
529     if (bt != LPSTR_TEXTCALLBACKW)
530     {
531         LPWSTR bw = textdupTtoW(bt, isW);
532         int r = bw ? lstrcmpW(aw, bw) : 1;
533         textfreeT(bw, isW);
534         return r;
535     }       
536             
537     return 1;
538 }
539     
540 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
541 {
542     int res;
543 
544     n = min(min(n, strlenW(s1)), strlenW(s2));
545     res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
546     return res ? res - sizeof(WCHAR) : res;
547 }
548 
549 /******** Debugging functions *****************************************/
550 
551 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
552 {
553     if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
554     return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
555 }
556 
557 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
558 {
559     if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
560     n = min(textlenT(text, isW), n);
561     return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
562 }
563 
564 static char* debug_getbuf(void)
565 {
566     static int index = 0;
567     static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
568     return buffers[index++ % DEBUG_BUFFERS];
569 }
570 
571 static inline const char* debugrange(const RANGE *lprng)
572 {
573     if (!lprng) return "(null)";
574     return wine_dbg_sprintf("[%d, %d]", lprng->lower, lprng->upper);
575 }
576 
577 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
578 {
579     char* buf = debug_getbuf(), *text = buf;
580     int len, size = DEBUG_BUFFER_SIZE;
581 
582     if (pScrollInfo == NULL) return "(null)";
583     len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
584     if (len == -1) goto end; buf += len; size -= len;
585     if (pScrollInfo->fMask & SIF_RANGE)
586         len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
587     else len = 0;
588     if (len == -1) goto end; buf += len; size -= len;
589     if (pScrollInfo->fMask & SIF_PAGE)
590         len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
591     else len = 0;
592     if (len == -1) goto end; buf += len; size -= len;
593     if (pScrollInfo->fMask & SIF_POS)
594         len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
595     else len = 0;
596     if (len == -1) goto end; buf += len; size -= len;
597     if (pScrollInfo->fMask & SIF_TRACKPOS)
598         len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
599     else len = 0;
600     if (len == -1) goto end; buf += len; size -= len;
601     goto undo;
602 end:
603     buf = text + strlen(text);
604 undo:
605     if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
606     return text;
607 } 
608 
609 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
610 {
611     if (!plvnm) return "(null)";
612     return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
613                  " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
614                  plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
615                  plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam);
616 }
617 
618 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
619 {
620     char* buf = debug_getbuf(), *text = buf;
621     int len, size = DEBUG_BUFFER_SIZE;
622     
623     if (lpLVItem == NULL) return "(null)";
624     len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
625     if (len == -1) goto end; buf += len; size -= len;
626     if (lpLVItem->mask & LVIF_STATE)
627         len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
628     else len = 0;
629     if (len == -1) goto end; buf += len; size -= len;
630     if (lpLVItem->mask & LVIF_TEXT)
631         len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
632     else len = 0;
633     if (len == -1) goto end; buf += len; size -= len;
634     if (lpLVItem->mask & LVIF_IMAGE)
635         len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
636     else len = 0;
637     if (len == -1) goto end; buf += len; size -= len;
638     if (lpLVItem->mask & LVIF_PARAM)
639         len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
640     else len = 0;
641     if (len == -1) goto end; buf += len; size -= len;
642     if (lpLVItem->mask & LVIF_INDENT)
643         len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
644     else len = 0;
645     if (len == -1) goto end; buf += len; size -= len;
646     goto undo;
647 end:
648     buf = text + strlen(text);
649 undo:
650     if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
651     return text;
652 }
653 
654 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
655 {
656     char* buf = debug_getbuf(), *text = buf;
657     int len, size = DEBUG_BUFFER_SIZE;
658     
659     if (lpColumn == NULL) return "(null)";
660     len = snprintf(buf, size, "{");
661     if (len == -1) goto end; buf += len; size -= len;
662     if (lpColumn->mask & LVCF_SUBITEM)
663         len = snprintf(buf, size, "iSubItem=%d, ",  lpColumn->iSubItem);
664     else len = 0;
665     if (len == -1) goto end; buf += len; size -= len;
666     if (lpColumn->mask & LVCF_FMT)
667         len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
668     else len = 0;
669     if (len == -1) goto end; buf += len; size -= len;
670     if (lpColumn->mask & LVCF_WIDTH)
671         len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
672     else len = 0;
673     if (len == -1) goto end; buf += len; size -= len;
674     if (lpColumn->mask & LVCF_TEXT)
675         len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
676     else len = 0;
677     if (len == -1) goto end; buf += len; size -= len;
678     if (lpColumn->mask & LVCF_IMAGE)
679         len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
680     else len = 0;
681     if (len == -1) goto end; buf += len; size -= len;
682     if (lpColumn->mask & LVCF_ORDER)
683         len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
684     else len = 0;
685     if (len == -1) goto end; buf += len; size -= len;
686     goto undo;
687 end:
688     buf = text + strlen(text);
689 undo:
690     if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
691     return text;
692 }
693 
694 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
695 {
696     if (!lpht) return "(null)";
697 
698     return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
699                  wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
700 }
701 
702 /* Return the corresponding text for a given scroll value */
703 static inline LPCSTR debugscrollcode(int nScrollCode)
704 {
705   switch(nScrollCode)
706   {
707   case SB_LINELEFT: return "SB_LINELEFT";
708   case SB_LINERIGHT: return "SB_LINERIGHT";
709   case SB_PAGELEFT: return "SB_PAGELEFT";
710   case SB_PAGERIGHT: return "SB_PAGERIGHT";
711   case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
712   case SB_THUMBTRACK: return "SB_THUMBTRACK";
713   case SB_ENDSCROLL: return "SB_ENDSCROLL";
714   case SB_INTERNAL: return "SB_INTERNAL";
715   default: return "unknown";
716   }
717 }
718 
719 
720 /******** Notification functions i************************************/
721 
722 static LRESULT notify_forward_header(const LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
723 {
724     return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
725                         (WPARAM)lpnmh->hdr.idFrom, (LPARAM)lpnmh);
726 }
727 
728 static LRESULT notify_hdr(const LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
729 {
730     LRESULT result;
731     
732     TRACE("(code=%d)\n", code);
733 
734     pnmh->hwndFrom = infoPtr->hwndSelf;
735     pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
736     pnmh->code = code;
737     result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, pnmh->idFrom, (LPARAM)pnmh);
738 
739     TRACE("  <= %ld\n", result);
740 
741     return result;
742 }
743 
744 static inline BOOL notify(const LISTVIEW_INFO *infoPtr, INT code)
745 {
746     NMHDR nmh;
747     HWND hwnd = infoPtr->hwndSelf;
748     notify_hdr(infoPtr, code, &nmh);
749     return IsWindow(hwnd);
750 }
751 
752 static inline void notify_itemactivate(const LISTVIEW_INFO *infoPtr, const LVHITTESTINFO *htInfo)
753 {
754     NMITEMACTIVATE nmia;
755     LVITEMW item;
756 
757     if (htInfo) {
758       nmia.uNewState = 0;
759       nmia.uOldState = 0;
760       nmia.uChanged  = 0;
761       nmia.uKeyFlags = 0;
762       
763       item.mask = LVIF_PARAM|LVIF_STATE;
764       item.iItem = htInfo->iItem;
765       item.iSubItem = 0;
766       if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
767           nmia.lParam = item.lParam;
768           nmia.uOldState = item.state;
769           nmia.uNewState = item.state | LVIS_ACTIVATING;
770           nmia.uChanged  = LVIF_STATE;
771       }
772       
773       nmia.iItem = htInfo->iItem;
774       nmia.iSubItem = htInfo->iSubItem;
775       nmia.ptAction = htInfo->pt;     
776       
777       if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
778       if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
779       if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
780     }
781     notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
782 }
783 
784 static inline LRESULT notify_listview(const LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
785 {
786     TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
787     return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
788 }
789 
790 static BOOL notify_click(const LISTVIEW_INFO *infoPtr, INT code, const LVHITTESTINFO *lvht)
791 {
792     NMLISTVIEW nmlv;
793     LVITEMW item;
794     HWND hwnd = infoPtr->hwndSelf;
795 
796     TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht)); 
797     ZeroMemory(&nmlv, sizeof(nmlv));
798     nmlv.iItem = lvht->iItem;
799     nmlv.iSubItem = lvht->iSubItem;
800     nmlv.ptAction = lvht->pt;
801     item.mask = LVIF_PARAM;
802     item.iItem = lvht->iItem;
803     item.iSubItem = 0;
804     if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
805     notify_listview(infoPtr, code, &nmlv);
806     return IsWindow(hwnd);
807 }
808 
809 static BOOL notify_deleteitem(const LISTVIEW_INFO *infoPtr, INT nItem)
810 {
811     NMLISTVIEW nmlv;
812     LVITEMW item;
813     HWND hwnd = infoPtr->hwndSelf;
814 
815     ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
816     nmlv.iItem = nItem;
817     item.mask = LVIF_PARAM;
818     item.iItem = nItem;
819     item.iSubItem = 0;
820     if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
821     notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
822     return IsWindow(hwnd);
823 }
824 
825 static int get_ansi_notification(INT unicodeNotificationCode)
826 {
827     switch (unicodeNotificationCode)
828     {
829     case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
830     case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
831     case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
832     case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
833     case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
834     case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
835     }
836     ERR("unknown notification %x\n", unicodeNotificationCode);
837     assert(FALSE);
838     return 0;
839 }
840 
841 /*
842   Send notification. depends on dispinfoW having same
843   structure as dispinfoA.
844   infoPtr : listview struct
845   notificationCode : *Unicode* notification code
846   pdi : dispinfo structure (can be unicode or ansi)
847   isW : TRUE if dispinfo is Unicode
848 */
849 static BOOL notify_dispinfoT(const LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
850 {
851     BOOL bResult = FALSE;
852     BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
853     INT cchTempBufMax = 0, savCchTextMax = 0, realNotifCode;
854     LPWSTR pszTempBuf = NULL, savPszText = NULL;
855 
856     if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
857     {
858         convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
859         convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
860     }
861 
862     if (convertToAnsi || convertToUnicode)
863     {
864         if (notificationCode != LVN_GETDISPINFOW)
865         {
866             cchTempBufMax = convertToUnicode ?