msi: Fix handling of REINSTALL overrides.
[wine.git] / dlls / comctl32 / listview.c
blob5379561c278acee9d79328f853c4722a6d20fab4
1 /*
2 * Listview control
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
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.
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.
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
24 * NOTES
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.
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.
33 * TODO:
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
46 * Features
47 * -- Hot item handling, mouse hovering
48 * -- Workareas support
49 * -- Tilemode support
50 * -- Groups support
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
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
73 * Flags
74 * -- LVIF_COLUMNS
75 * -- LVIF_GROUPID
76 * -- LVIF_NORECOMPUTE
78 * States
79 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
80 * -- LVIS_CUT
81 * -- LVIS_DROPHILITED
82 * -- LVIS_OVERLAYMASK
84 * Styles
85 * -- LVS_NOLABELWRAP
86 * -- LVS_NOSCROLL (see Q137520)
87 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
88 * -- LVS_ALIGNTOP
89 * -- LVS_TYPESTYLEMASK
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_REGIONAL
99 * -- LVS_EX_SIMPLESELECT
100 * -- LVS_EX_TWOCLICKACTIVATE
101 * -- LVS_EX_UNDERLINECOLD
102 * -- LVS_EX_UNDERLINEHOT
104 * Notifications:
105 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
106 * -- LVN_GETINFOTIP
107 * -- LVN_HOTTRACK
108 * -- LVN_MARQUEEBEGIN
109 * -- LVN_SETDISPINFO
110 * -- NM_HOVER
111 * -- LVN_BEGINRDRAG
113 * Messages:
114 * -- LVM_CANCELEDITLABEL
115 * -- LVM_ENABLEGROUPVIEW
116 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
117 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
118 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
119 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
120 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
121 * -- LVM_GETINSERTMARKRECT
122 * -- LVM_GETNUMBEROFWORKAREAS
123 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
124 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
125 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
126 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
127 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
128 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
129 * -- LVM_GETVIEW, LVM_SETVIEW
130 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
131 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
132 * -- LVM_INSERTGROUPSORTED
133 * -- LVM_INSERTMARKHITTEST
134 * -- LVM_ISGROUPVIEWENABLED
135 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
136 * -- LVM_MOVEGROUP
137 * -- LVM_MOVEITEMTOGROUP
138 * -- LVM_SETINFOTIP
139 * -- LVM_SETTILEWIDTH
140 * -- LVM_SORTGROUPS
141 * -- LVM_SORTITEMSEX
143 * Macros:
144 * -- ListView_GetCheckSate, ListView_SetCheckState
145 * -- ListView_GetHoverTime, ListView_SetHoverTime
146 * -- ListView_GetISearchString
147 * -- ListView_GetNumberOfWorkAreas
148 * -- ListView_GetOrigin
149 * -- ListView_GetTextBkColor
150 * -- ListView_GetUnicodeFormat, ListView_SetUnicodeFormat
151 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
152 * -- ListView_SortItemsEx
154 * Functions:
155 * -- LVGroupComparE
157 * Known differences in message stream from native control (not known if
158 * these differences cause problems):
159 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
160 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
161 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
162 * processing for "USEDOUBLECLICKTIME".
165 #include "config.h"
166 #include "wine/port.h"
168 #include <assert.h>
169 #include <ctype.h>
170 #include <string.h>
171 #include <stdlib.h>
172 #include <stdarg.h>
173 #include <stdio.h>
175 #include "windef.h"
176 #include "winbase.h"
177 #include "winnt.h"
178 #include "wingdi.h"
179 #include "winuser.h"
180 #include "winnls.h"
181 #include "commctrl.h"
182 #include "comctl32.h"
183 #include "uxtheme.h"
185 #include "wine/debug.h"
186 #include "wine/unicode.h"
188 WINE_DEFAULT_DEBUG_CHANNEL(listview);
190 /* make sure you set this to 0 for production use! */
191 #define DEBUG_RANGES 1
193 typedef struct tagCOLUMN_INFO
195 RECT rcHeader; /* tracks the header's rectangle */
196 int fmt; /* same as LVCOLUMN.fmt */
197 } COLUMN_INFO;
199 typedef struct tagITEMHDR
201 LPWSTR pszText;
202 INT iImage;
203 } ITEMHDR, *LPITEMHDR;
205 typedef struct tagSUBITEM_INFO
207 ITEMHDR hdr;
208 INT iSubItem;
209 } SUBITEM_INFO;
211 typedef struct tagITEM_INFO
213 ITEMHDR hdr;
214 UINT state;
215 LPARAM lParam;
216 INT iIndent;
217 } ITEM_INFO;
219 typedef struct tagRANGE
221 INT lower;
222 INT upper;
223 } RANGE;
225 typedef struct tagRANGES
227 HDPA hdpa;
228 } *RANGES;
230 typedef struct tagITERATOR
232 INT nItem;
233 INT nSpecial;
234 RANGE range;
235 RANGES ranges;
236 INT index;
237 } ITERATOR;
239 typedef struct tagDELAYED_ITEM_EDIT
241 BOOL fEnabled;
242 INT iItem;
243 } DELAYED_ITEM_EDIT;
245 typedef struct tagLISTVIEW_INFO
247 HWND hwndSelf;
248 HBRUSH hBkBrush;
249 COLORREF clrBk;
250 COLORREF clrText;
251 COLORREF clrTextBk;
252 HIMAGELIST himlNormal;
253 HIMAGELIST himlSmall;
254 HIMAGELIST himlState;
255 BOOL bLButtonDown;
256 BOOL bRButtonDown;
257 BOOL bDragging;
258 POINT ptClickPos; /* point where the user clicked */
259 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
260 INT nItemHeight;
261 INT nItemWidth;
262 RANGES selectionRanges;
263 INT nSelectionMark;
264 INT nHotItem;
265 SHORT notifyFormat;
266 HWND hwndNotify;
267 RECT rcList; /* This rectangle is really the window
268 * client rectangle possibly reduced by the
269 * horizontal scroll bar and/or header - see
270 * LISTVIEW_UpdateSize. This rectangle offset
271 * by the LISTVIEW_GetOrigin value is in
272 * client coordinates */
273 SIZE iconSize;
274 SIZE iconSpacing;
275 SIZE iconStateSize;
276 UINT uCallbackMask;
277 HWND hwndHeader;
278 HCURSOR hHotCursor;
279 HFONT hDefaultFont;
280 HFONT hFont;
281 INT ntmHeight; /* Some cached metrics of the font used */
282 INT ntmMaxCharWidth; /* by the listview to draw items */
283 INT nEllipsisWidth;
284 BOOL bRedraw; /* Turns on/off repaints & invalidations */
285 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
286 BOOL bFocus;
287 BOOL bDoChangeNotify; /* send change notification messages? */
288 INT nFocusedItem;
289 RECT rcFocus;
290 DWORD dwStyle; /* the cached window GWL_STYLE */
291 DWORD dwLvExStyle; /* extended listview style */
292 INT nItemCount; /* the number of items in the list */
293 HDPA hdpaItems; /* array ITEM_INFO pointers */
294 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
295 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
296 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
297 POINT currIconPos; /* this is the position next icon will be placed */
298 PFNLVCOMPARE pfnCompare;
299 LPARAM lParamSort;
300 HWND hwndEdit;
301 WNDPROC EditWndProc;
302 INT nEditLabelItem;
303 DWORD dwHoverTime;
304 HWND hwndToolTip;
306 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
308 DWORD lastKeyPressTimestamp;
309 WPARAM charCode;
310 INT nSearchParamLength;
311 WCHAR szSearchParam[ MAX_PATH ];
312 BOOL bIsDrawing;
313 INT nMeasureItemHeight;
314 INT xTrackLine; /* The x coefficient of the track line or -1 if none */
315 DELAYED_ITEM_EDIT itemEdit; /* Pointer to this structure will be the timer ID */
316 } LISTVIEW_INFO;
319 * constants
321 /* How many we debug buffer to allocate */
322 #define DEBUG_BUFFERS 20
323 /* The size of a single debug bbuffer */
324 #define DEBUG_BUFFER_SIZE 256
326 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
327 #define SB_INTERNAL -1
329 /* maximum size of a label */
330 #define DISP_TEXT_SIZE 512
332 /* padding for items in list and small icon display modes */
333 #define WIDTH_PADDING 12
335 /* padding for items in list, report and small icon display modes */
336 #define HEIGHT_PADDING 1
338 /* offset of items in report display mode */
339 #define REPORT_MARGINX 2
341 /* padding for icon in large icon display mode
342 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
343 * that HITTEST will see.
344 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
345 * ICON_TOP_PADDING - sum of the two above.
346 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
347 * LABEL_HOR_PADDING - between text and sides of box
348 * LABEL_VERT_PADDING - between bottom of text and end of box
350 * ICON_LR_PADDING - additional width above icon size.
351 * ICON_LR_HALF - half of the above value
353 #define ICON_TOP_PADDING_NOTHITABLE 2
354 #define ICON_TOP_PADDING_HITABLE 2
355 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
356 #define ICON_BOTTOM_PADDING 4
357 #define LABEL_HOR_PADDING 5
358 #define LABEL_VERT_PADDING 7
359 #define ICON_LR_PADDING 16
360 #define ICON_LR_HALF (ICON_LR_PADDING/2)
362 /* default label width for items in list and small icon display modes */
363 #define DEFAULT_LABEL_WIDTH 40
365 /* default column width for items in list display mode */
366 #define DEFAULT_COLUMN_WIDTH 128
368 /* Size of "line" scroll for V & H scrolls */
369 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
371 /* Padding between image and label */
372 #define IMAGE_PADDING 2
374 /* Padding behind the label */
375 #define TRAILING_LABEL_PADDING 12
376 #define TRAILING_HEADER_PADDING 11
378 /* Border for the icon caption */
379 #define CAPTION_BORDER 2
381 /* Standard DrawText flags */
382 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
383 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
384 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
386 /* Image index from state */
387 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
389 /* The time in milliseconds to reset the search in the list */
390 #define KEY_DELAY 450
392 /* Dump the LISTVIEW_INFO structure to the debug channel */
393 #define LISTVIEW_DUMP(iP) do { \
394 TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
395 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
396 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
397 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
398 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
399 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
400 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
401 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
402 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
403 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
404 } while(0)
406 static const WCHAR themeClass[] = {'L','i','s','t','V','i','e','w',0};
409 * forward declarations
411 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *, LPLVITEMW, BOOL);
412 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *, INT, LPRECT);
413 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *, INT, LPPOINT);
414 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *, INT, LPPOINT);
415 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *, INT, LPRECT);
416 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *, INT);
417 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *, LPPOINT);
418 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *, LPRECT);
419 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
420 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LVITEMW *, BOOL);
421 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *);
422 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
423 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
424 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
425 static LRESULT LISTVIEW_Command(const LISTVIEW_INFO *, WPARAM, LPARAM);
426 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
427 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *, LPCWSTR, BOOL);
428 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT, BOOL);
429 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *, INT, UINT);
430 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
431 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
432 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
433 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *);
434 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
435 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
436 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
437 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
439 /******** Text handling functions *************************************/
441 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
442 * text string. The string may be ANSI or Unicode, in which case
443 * the boolean isW tells us the type of the string.
445 * The name of the function tell what type of strings it expects:
446 * W: Unicode, T: ANSI/Unicode - function of isW
449 static inline BOOL is_textW(LPCWSTR text)
451 return text != NULL && text != LPSTR_TEXTCALLBACKW;
454 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
456 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
457 return is_textW(text);
460 static inline int textlenT(LPCWSTR text, BOOL isW)
462 return !is_textT(text, isW) ? 0 :
463 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
466 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
468 if (isDestW)
469 if (isSrcW) lstrcpynW(dest, src, max);
470 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
471 else
472 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
473 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
476 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
478 LPWSTR wstr = (LPWSTR)text;
480 if (!isW && is_textT(text, isW))
482 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
483 wstr = Alloc(len * sizeof(WCHAR));
484 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
486 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
487 return wstr;
490 static inline void textfreeT(LPWSTR wstr, BOOL isW)
492 if (!isW && is_textT(wstr, isW)) Free (wstr);
496 * dest is a pointer to a Unicode string
497 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
499 static BOOL textsetptrT(LPWSTR *dest, LPCWSTR src, BOOL isW)
501 BOOL bResult = TRUE;
503 if (src == LPSTR_TEXTCALLBACKW)
505 if (is_textW(*dest)) Free(*dest);
506 *dest = LPSTR_TEXTCALLBACKW;
508 else
510 LPWSTR pszText = textdupTtoW(src, isW);
511 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
512 bResult = Str_SetPtrW(dest, pszText);
513 textfreeT(pszText, isW);
515 return bResult;
519 * compares a Unicode to a Unicode/ANSI text string
521 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
523 if (!aw) return bt ? -1 : 0;
524 if (!bt) return aw ? 1 : 0;
525 if (aw == LPSTR_TEXTCALLBACKW)
526 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
527 if (bt != LPSTR_TEXTCALLBACKW)
529 LPWSTR bw = textdupTtoW(bt, isW);
530 int r = bw ? lstrcmpW(aw, bw) : 1;
531 textfreeT(bw, isW);
532 return r;
535 return 1;
538 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
540 int res;
542 n = min(min(n, lstrlenW(s1)), lstrlenW(s2));
543 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
544 return res ? res - sizeof(WCHAR) : res;
547 /******** Debugging functions *****************************************/
549 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
551 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
552 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
555 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
557 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
558 n = min(textlenT(text, isW), n);
559 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
562 static char* debug_getbuf(void)
564 static int index = 0;
565 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
566 return buffers[index++ % DEBUG_BUFFERS];
569 static inline const char* debugrange(const RANGE *lprng)
571 if (!lprng) return "(null)";
572 return wine_dbg_sprintf("[%d, %d]", lprng->lower, lprng->upper);
575 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
577 char* buf = debug_getbuf(), *text = buf;
578 int len, size = DEBUG_BUFFER_SIZE;
580 if (pScrollInfo == NULL) return "(null)";
581 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
582 if (len == -1) goto end; buf += len; size -= len;
583 if (pScrollInfo->fMask & SIF_RANGE)
584 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
585 else len = 0;
586 if (len == -1) goto end; buf += len; size -= len;
587 if (pScrollInfo->fMask & SIF_PAGE)
588 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
589 else len = 0;
590 if (len == -1) goto end; buf += len; size -= len;
591 if (pScrollInfo->fMask & SIF_POS)
592 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
593 else len = 0;
594 if (len == -1) goto end; buf += len; size -= len;
595 if (pScrollInfo->fMask & SIF_TRACKPOS)
596 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
597 else len = 0;
598 if (len == -1) goto end; buf += len; size -= len;
599 goto undo;
600 end:
601 buf = text + strlen(text);
602 undo:
603 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
604 return text;
607 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
609 if (!plvnm) return "(null)";
610 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
611 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
612 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
613 plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam);
616 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
618 char* buf = debug_getbuf(), *text = buf;
619 int len, size = DEBUG_BUFFER_SIZE;
621 if (lpLVItem == NULL) return "(null)";
622 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
623 if (len == -1) goto end; buf += len; size -= len;
624 if (lpLVItem->mask & LVIF_STATE)
625 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
626 else len = 0;
627 if (len == -1) goto end; buf += len; size -= len;
628 if (lpLVItem->mask & LVIF_TEXT)
629 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
630 else len = 0;
631 if (len == -1) goto end; buf += len; size -= len;
632 if (lpLVItem->mask & LVIF_IMAGE)
633 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
634 else len = 0;
635 if (len == -1) goto end; buf += len; size -= len;
636 if (lpLVItem->mask & LVIF_PARAM)
637 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
638 else len = 0;
639 if (len == -1) goto end; buf += len; size -= len;
640 if (lpLVItem->mask & LVIF_INDENT)
641 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
642 else len = 0;
643 if (len == -1) goto end; buf += len; size -= len;
644 goto undo;
645 end:
646 buf = text + strlen(text);
647 undo:
648 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
649 return text;
652 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
654 char* buf = debug_getbuf(), *text = buf;
655 int len, size = DEBUG_BUFFER_SIZE;
657 if (lpColumn == NULL) return "(null)";
658 len = snprintf(buf, size, "{");
659 if (len == -1) goto end; buf += len; size -= len;
660 if (lpColumn->mask & LVCF_SUBITEM)
661 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
662 else len = 0;
663 if (len == -1) goto end; buf += len; size -= len;
664 if (lpColumn->mask & LVCF_FMT)
665 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
666 else len = 0;
667 if (len == -1) goto end; buf += len; size -= len;
668 if (lpColumn->mask & LVCF_WIDTH)
669 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
670 else len = 0;
671 if (len == -1) goto end; buf += len; size -= len;
672 if (lpColumn->mask & LVCF_TEXT)
673 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
674 else len = 0;
675 if (len == -1) goto end; buf += len; size -= len;
676 if (lpColumn->mask & LVCF_IMAGE)
677 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
678 else len = 0;
679 if (len == -1) goto end; buf += len; size -= len;
680 if (lpColumn->mask & LVCF_ORDER)
681 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
682 else len = 0;
683 if (len == -1) goto end; buf += len; size -= len;
684 goto undo;
685 end:
686 buf = text + strlen(text);
687 undo:
688 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
689 return text;
692 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
694 if (!lpht) return "(null)";
696 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
697 wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
700 /* Return the corresponding text for a given scroll value */
701 static inline LPCSTR debugscrollcode(int nScrollCode)
703 switch(nScrollCode)
705 case SB_LINELEFT: return "SB_LINELEFT";
706 case SB_LINERIGHT: return "SB_LINERIGHT";
707 case SB_PAGELEFT: return "SB_PAGELEFT";
708 case SB_PAGERIGHT: return "SB_PAGERIGHT";
709 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
710 case SB_THUMBTRACK: return "SB_THUMBTRACK";
711 case SB_ENDSCROLL: return "SB_ENDSCROLL";
712 case SB_INTERNAL: return "SB_INTERNAL";
713 default: return "unknown";
718 /******** Notification functions i************************************/
720 static LRESULT notify_forward_header(const LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
722 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
723 (WPARAM)lpnmh->hdr.idFrom, (LPARAM)lpnmh);
726 static LRESULT notify_hdr(const LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
728 LRESULT result;
730 TRACE("(code=%d)\n", code);
732 pnmh->hwndFrom = infoPtr->hwndSelf;
733 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
734 pnmh->code = code;
735 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, pnmh->idFrom, (LPARAM)pnmh);
737 TRACE(" <= %ld\n", result);
739 return result;
742 static inline BOOL notify(const LISTVIEW_INFO *infoPtr, INT code)
744 NMHDR nmh;
745 HWND hwnd = infoPtr->hwndSelf;
746 notify_hdr(infoPtr, code, &nmh);
747 return IsWindow(hwnd);
750 static inline void notify_itemactivate(const LISTVIEW_INFO *infoPtr, const LVHITTESTINFO *htInfo)
752 NMITEMACTIVATE nmia;
753 LVITEMW item;
755 if (htInfo) {
756 nmia.uNewState = 0;
757 nmia.uOldState = 0;
758 nmia.uChanged = 0;
759 nmia.uKeyFlags = 0;
761 item.mask = LVIF_PARAM|LVIF_STATE;
762 item.iItem = htInfo->iItem;
763 item.iSubItem = 0;
764 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
765 nmia.lParam = item.lParam;
766 nmia.uOldState = item.state;
767 nmia.uNewState = item.state | LVIS_ACTIVATING;
768 nmia.uChanged = LVIF_STATE;
771 nmia.iItem = htInfo->iItem;
772 nmia.iSubItem = htInfo->iSubItem;
773 nmia.ptAction = htInfo->pt;
775 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
776 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
777 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
779 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
782 static inline LRESULT notify_listview(const LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
784 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
785 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
788 static BOOL notify_click(const LISTVIEW_INFO *infoPtr, INT code, const LVHITTESTINFO *lvht)
790 NMLISTVIEW nmlv;
791 LVITEMW item;
792 HWND hwnd = infoPtr->hwndSelf;
794 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
795 ZeroMemory(&nmlv, sizeof(nmlv));
796 nmlv.iItem = lvht->iItem;
797 nmlv.iSubItem = lvht->iSubItem;
798 nmlv.ptAction = lvht->pt;
799 item.mask = LVIF_PARAM;
800 item.iItem = lvht->iItem;
801 item.iSubItem = 0;
802 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
803 notify_listview(infoPtr, code, &nmlv);
804 return IsWindow(hwnd);
807 static BOOL notify_deleteitem(const LISTVIEW_INFO *infoPtr, INT nItem)
809 NMLISTVIEW nmlv;
810 LVITEMW item;
811 HWND hwnd = infoPtr->hwndSelf;
813 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
814 nmlv.iItem = nItem;
815 item.mask = LVIF_PARAM;
816 item.iItem = nItem;
817 item.iSubItem = 0;
818 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
819 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
820 return IsWindow(hwnd);
823 static int get_ansi_notification(UINT unicodeNotificationCode)
825 switch (unicodeNotificationCode)
827 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
828 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
829 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
830 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
831 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
832 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
834 ERR("unknown notification %x\n", unicodeNotificationCode);
835 assert(FALSE);
836 return 0;
840 Send notification. depends on dispinfoW having same
841 structure as dispinfoA.
842 infoPtr : listview struct
843 notificationCode : *Unicode* notification code
844 pdi : dispinfo structure (can be unicode or ansi)
845 isW : TRUE if dispinfo is Unicode
847 static BOOL notify_dispinfoT(const LISTVIEW_INFO *infoPtr, UINT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
849 BOOL bResult = FALSE;
850 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
851 INT cchTempBufMax = 0, savCchTextMax = 0;
852 UINT realNotifCode;
853 LPWSTR pszTempBuf = NULL, savPszText = NULL;
855 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
857 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
858 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
861 if (convertToAnsi || convertToUnicode)
863 if (notificationCode != LVN_GETDISPINFOW)
865 cchTempBufMax = convertToUnicode ?
866 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
867 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
869 else
871 cchTempBufMax = pdi->item.cchTextMax;
872 *pdi->item.pszText = 0; /* make sure we don't process garbage */
875 pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
876 if (!pszTempBuf) return FALSE;
878 if (convertToUnicode)
879 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
880 pszTempBuf, cchTempBufMax);
881 else
882 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
883 cchTempBufMax, NULL, NULL);
885 savCchTextMax = pdi->item.cchTextMax;
886 savPszText = pdi->item.pszText;
887 pdi->item.pszText = pszTempBuf;
888 pdi->item.cchTextMax = cchTempBufMax;
891 if (infoPtr->notifyFormat == NFR_ANSI)
892 realNotifCode = get_ansi_notification(notificationCode);
893 else
894 realNotifCode = notificationCode;
895 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
896 bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
898 if (convertToUnicode || convertToAnsi)
900 if (convertToUnicode) /* note : pointer can be changed by app ! */
901 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
902 savCchTextMax, NULL, NULL);
903 else
904 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
905 savPszText, savCchTextMax);
906 pdi->item.pszText = savPszText; /* restores our buffer */
907 pdi->item.cchTextMax = savCchTextMax;
908 Free (pszTempBuf);
910 return bResult;
913 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, const LISTVIEW_INFO *infoPtr, HDC hdc,
914 const RECT *rcBounds, const LVITEMW *lplvItem)
916 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
917 lpnmlvcd->nmcd.hdc = hdc;
918 lpnmlvcd->nmcd.rc = *rcBounds;
919 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
920 lpnmlvcd->clrText = infoPtr->clrText;
921 if (!lplvItem) return;
922 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
923 lpnmlvcd->iSubItem = lplvItem->iSubItem;
924 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
925 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
926 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
927 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
930 static inline DWORD notify_customdraw (const LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
932 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
933 DWORD result;
935 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
936 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
937 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
938 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
939 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
940 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
941 return result;
944 static void prepaint_setup (const LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd, BOOL SubItem)
946 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
947 lpnmlvcd->clrTextBk = comctl32_color.clrWindow;
948 if (lpnmlvcd->clrText == CLR_DEFAULT)
949 lpnmlvcd->clrText = comctl32_color.clrWindowText;
951 /* apparently, for selected items, we have to override the returned values */
952 if (!SubItem)
954 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
956 if (infoPtr->bFocus)
958 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
959 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
961 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
963 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
964 lpnmlvcd->clrText = comctl32_color.clrBtnText;
969 /* Set the text attributes */
970 if (lpnmlvcd->clrTextBk != CLR_NONE)
972 SetBkMode(hdc, OPAQUE);
973 SetBkColor(hdc,lpnmlvcd->clrTextBk);
975 else
976 SetBkMode(hdc, TRANSPARENT);
977 SetTextColor(hdc, lpnmlvcd->clrText);
980 static inline DWORD notify_postpaint (const LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
982 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
985 /******** Item iterator functions **********************************/
987 static RANGES ranges_create(int count);
988 static void ranges_destroy(RANGES ranges);
989 static BOOL ranges_add(RANGES ranges, RANGE range);
990 static BOOL ranges_del(RANGES ranges, RANGE range);
991 static void ranges_dump(RANGES ranges);
993 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
995 RANGE range = { nItem, nItem + 1 };
997 return ranges_add(ranges, range);
1000 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
1002 RANGE range = { nItem, nItem + 1 };
1004 return ranges_del(ranges, range);
1007 /***
1008 * ITERATOR DOCUMENTATION
1010 * The iterator functions allow for easy, and convenient iteration
1011 * over items of interest in the list. Typically, you create a
1012 * iterator, use it, and destroy it, as such:
1013 * ITERATOR i;
1015 * iterator_xxxitems(&i, ...);
1016 * while (iterator_{prev,next}(&i)
1018 * //code which uses i.nItem
1020 * iterator_destroy(&i);
1022 * where xxx is either: framed, or visible.
1023 * Note that it is important that the code destroys the iterator
1024 * after it's done with it, as the creation of the iterator may
1025 * allocate memory, which thus needs to be freed.
1027 * You can iterate both forwards, and backwards through the list,
1028 * by using iterator_next or iterator_prev respectively.
1030 * Lower numbered items are draw on top of higher number items in
1031 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1032 * items may overlap). So, to test items, you should use
1033 * iterator_next
1034 * which lists the items top to bottom (in Z-order).
1035 * For drawing items, you should use
1036 * iterator_prev
1037 * which lists the items bottom to top (in Z-order).
1038 * If you keep iterating over the items after the end-of-items
1039 * marker (-1) is returned, the iterator will start from the
1040 * beginning. Typically, you don't need to test for -1,
1041 * because iterator_{next,prev} will return TRUE if more items
1042 * are to be iterated over, or FALSE otherwise.
1044 * Note: the iterator is defined to be bidirectional. That is,
1045 * any number of prev followed by any number of next, or
1046 * five versa, should leave the iterator at the same item:
1047 * prev * n, next * n = next * n, prev * n
1049 * The iterator has a notion of an out-of-order, special item,
1050 * which sits at the start of the list. This is used in
1051 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1052 * which needs to be first, as it may overlap other items.
1054 * The code is a bit messy because we have:
1055 * - a special item to deal with
1056 * - simple range, or composite range
1057 * - empty range.
1058 * If you find bugs, or want to add features, please make sure you
1059 * always check/modify *both* iterator_prev, and iterator_next.
1062 /****
1063 * This function iterates through the items in increasing order,
1064 * but prefixed by the special item, then -1. That is:
1065 * special, 1, 2, 3, ..., n, -1.
1066 * Each item is listed only once.
1068 static inline BOOL iterator_next(ITERATOR* i)
1070 if (i->nItem == -1)
1072 i->nItem = i->nSpecial;
1073 if (i->nItem != -1) return TRUE;
1075 if (i->nItem == i->nSpecial)
1077 if (i->ranges) i->index = 0;
1078 goto pickarange;
1081 i->nItem++;
1082 testitem:
1083 if (i->nItem == i->nSpecial) i->nItem++;
1084 if (i->nItem < i->range.upper) return TRUE;
1086 pickarange:
1087 if (i->ranges)
1089 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1090 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1091 else goto end;
1093 else if (i->nItem >= i->range.upper) goto end;
1095 i->nItem = i->range.lower;
1096 if (i->nItem >= 0) goto testitem;
1097 end:
1098 i->nItem = -1;
1099 return FALSE;
1102 /****
1103 * This function iterates through the items in decreasing order,
1104 * followed by the special item, then -1. That is:
1105 * n, n-1, ..., 3, 2, 1, special, -1.
1106 * Each item is listed only once.
1108 static inline BOOL iterator_prev(ITERATOR* i)
1110 BOOL start = FALSE;
1112 if (i->nItem == -1)
1114 start = TRUE;
1115 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1116 goto pickarange;
1118 if (i->nItem == i->nSpecial)
1120 i->nItem = -1;
1121 return FALSE;
1124 testitem:
1125 i->nItem--;
1126 if (i->nItem == i->nSpecial) i->nItem--;
1127 if (i->nItem >= i->range.lower) return TRUE;
1129 pickarange:
1130 if (i->ranges)
1132 if (i->index > 0)
1133 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1134 else goto end;
1136 else if (!start && i->nItem < i->range.lower) goto end;
1138 i->nItem = i->range.upper;
1139 if (i->nItem > 0) goto testitem;
1140 end:
1141 return (i->nItem = i->nSpecial) != -1;
1144 static RANGE iterator_range(const ITERATOR *i)
1146 RANGE range;
1148 if (!i->ranges) return i->range;
1150 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1152 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1153 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1155 else range.lower = range.upper = 0;
1157 return range;
1160 /***
1161 * Releases resources associated with this ierator.
1163 static inline void iterator_destroy(const ITERATOR *i)
1165 ranges_destroy(i->ranges);
1168 /***
1169 * Create an empty iterator.
1171 static inline BOOL iterator_empty(ITERATOR* i)
1173 ZeroMemory(i, sizeof(*i));
1174 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1175 return TRUE;
1178 /***
1179 * Create an iterator over a range.
1181 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1183 iterator_empty(i);
1184 i->range = range;
1185 return TRUE;
1188 /***
1189 * Create an iterator over a bunch of ranges.
1190 * Please note that the iterator will take ownership of the ranges,
1191 * and will free them upon destruction.
1193 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1195 iterator_empty(i);
1196 i->ranges = ranges;
1197 return TRUE;
1200 /***
1201 * Creates an iterator over the items which intersect lprc.
1203 static BOOL iterator_frameditems(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *lprc)
1205 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1206 RECT frame = *lprc, rcItem, rcTemp;
1207 POINT Origin;
1209 /* in case we fail, we want to return an empty iterator */
1210 if (!iterator_empty(i)) return FALSE;
1212 LISTVIEW_GetOrigin(infoPtr, &Origin);
1214 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc));
1215 OffsetRect(&frame, -Origin.x, -Origin.y);
1217 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1219 INT nItem;
1221 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1223 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1224 if (IntersectRect(&rcTemp, &rcItem, lprc))
1225 i->nSpecial = infoPtr->nFocusedItem;
1227 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1228 /* to do better here, we need to have PosX, and PosY sorted */
1229 TRACE("building icon ranges:\n");
1230 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1232 rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1233 rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1234 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1235 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1236 if (IntersectRect(&rcTemp, &rcItem, &frame))
1237 ranges_additem(i->ranges, nItem);
1239 return TRUE;
1241 else if (uView == LVS_REPORT)
1243 RANGE range;
1245 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1246 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1248 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1249 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1250 if (range.upper <= range.lower) return TRUE;
1251 if (!iterator_rangeitems(i, range)) return FALSE;
1252 TRACE(" report=%s\n", debugrange(&i->range));
1254 else
1256 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1257 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1258 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1259 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1260 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1261 INT lower = nFirstCol * nPerCol + nFirstRow;
1262 RANGE item_range;
1263 INT nCol;
1265 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1266 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1268 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1270 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1271 TRACE("building list ranges:\n");
1272 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1274 item_range.lower = nCol * nPerCol + nFirstRow;
1275 if(item_range.lower >= infoPtr->nItemCount) break;
1276 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1277 TRACE(" list=%s\n", debugrange(&item_range));
1278 ranges_add(i->ranges, item_range);
1282 return TRUE;
1285 /***
1286 * Creates an iterator over the items which intersect the visible region of hdc.
1288 static BOOL iterator_visibleitems(ITERATOR *i, const LISTVIEW_INFO *infoPtr, HDC hdc)
1290 POINT Origin, Position;
1291 RECT rcItem, rcClip;
1292 INT rgntype;
1294 rgntype = GetClipBox(hdc, &rcClip);
1295 if (rgntype == NULLREGION) return iterator_empty(i);
1296 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1297 if (rgntype == SIMPLEREGION) return TRUE;
1299 /* first deal with the special item */
1300 if (i->nSpecial != -1)
1302 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1303 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1306 /* if we can't deal with the region, we'll just go with the simple range */
1307 LISTVIEW_GetOrigin(infoPtr, &Origin);
1308 TRACE("building visible range:\n");
1309 if (!i->ranges && i->range.lower < i->range.upper)
1311 if (!(i->ranges = ranges_create(50))) return TRUE;
1312 if (!ranges_add(i->ranges, i->range))
1314 ranges_destroy(i->ranges);
1315 i->ranges = 0;
1316 return TRUE;
1320 /* now delete the invisible items from the list */
1321 while(iterator_next(i))
1323 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1324 rcItem.left = Position.x + Origin.x;
1325 rcItem.top = Position.y + Origin.y;
1326 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1327 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1328 if (!RectVisible(hdc, &rcItem))
1329 ranges_delitem(i->ranges, i->nItem);
1331 /* the iterator should restart on the next iterator_next */
1332 TRACE("done\n");
1334 return TRUE;
1337 /******** Misc helper functions ************************************/
1339 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1340 WPARAM wParam, LPARAM lParam, BOOL isW)
1342 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1343 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1346 static inline BOOL is_autoarrange(const LISTVIEW_INFO *infoPtr)
1348 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1350 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1351 (uView == LVS_ICON || uView == LVS_SMALLICON);
1354 static void toggle_checkbox_state(LISTVIEW_INFO *infoPtr, INT nItem)
1356 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
1357 if(state == 1 || state == 2)
1359 LVITEMW lvitem;
1360 state ^= 3;
1361 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
1362 lvitem.stateMask = LVIS_STATEIMAGEMASK;
1363 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
1367 /******** Internal API functions ************************************/
1369 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(const LISTVIEW_INFO *infoPtr, INT nSubItem)
1371 static COLUMN_INFO mainItem;
1373 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1374 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1375 return DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1378 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO *infoPtr, INT nSubItem, LPRECT lprc)
1380 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1383 static inline BOOL LISTVIEW_GetItemW(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1385 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1388 /* Listview invalidation functions: use _only_ these functions to invalidate */
1390 static inline BOOL is_redrawing(const LISTVIEW_INFO *infoPtr)
1392 return infoPtr->bRedraw;
1395 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO *infoPtr, const RECT* rect)
1397 if(!is_redrawing(infoPtr)) return;
1398 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect));
1399 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1402 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO *infoPtr, INT nItem)
1404 RECT rcBox;
1406 if(!is_redrawing(infoPtr)) return;
1407 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1408 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1411 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1413 POINT Origin, Position;
1414 RECT rcBox;
1416 if(!is_redrawing(infoPtr)) return;
1417 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1418 LISTVIEW_GetOrigin(infoPtr, &Origin);
1419 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1420 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1421 rcBox.top = 0;
1422 rcBox.bottom = infoPtr->nItemHeight;
1423 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1424 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1427 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO *infoPtr)
1429 LISTVIEW_InvalidateRect(infoPtr, NULL);
1432 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO *infoPtr, INT nColumn)
1434 RECT rcCol;
1436 if(!is_redrawing(infoPtr)) return;
1437 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1438 rcCol.top = infoPtr->rcList.top;
1439 rcCol.bottom = infoPtr->rcList.bottom;
1440 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1443 /***
1444 * DESCRIPTION:
1445 * Retrieves the number of items that can fit vertically in the client area.
1447 * PARAMETER(S):
1448 * [I] infoPtr : valid pointer to the listview structure
1450 * RETURN:
1451 * Number of items per row.
1453 static inline INT LISTVIEW_GetCountPerRow(const LISTVIEW_INFO *infoPtr)
1455 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1457 return max(nListWidth/infoPtr->nItemWidth, 1);
1460 /***
1461 * DESCRIPTION:
1462 * Retrieves the number of items that can fit horizontally in the client
1463 * area.
1465 * PARAMETER(S):
1466 * [I] infoPtr : valid pointer to the listview structure
1468 * RETURN:
1469 * Number of items per column.
1471 static inline INT LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO *infoPtr)
1473 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1475 return max(nListHeight / infoPtr->nItemHeight, 1);
1479 /*************************************************************************
1480 * LISTVIEW_ProcessLetterKeys
1482 * Processes keyboard messages generated by pressing the letter keys
1483 * on the keyboard.
1484 * What this does is perform a case insensitive search from the
1485 * current position with the following quirks:
1486 * - If two chars or more are pressed in quick succession we search
1487 * for the corresponding string (e.g. 'abc').
1488 * - If there is a delay we wipe away the current search string and
1489 * restart with just that char.
1490 * - If the user keeps pressing the same character, whether slowly or
1491 * fast, so that the search string is entirely composed of this
1492 * character ('aaaaa' for instance), then we search for first item
1493 * that starting with that character.
1494 * - If the user types the above character in quick succession, then
1495 * we must also search for the corresponding string ('aaaaa'), and
1496 * go to that string if there is a match.
1498 * PARAMETERS
1499 * [I] hwnd : handle to the window
1500 * [I] charCode : the character code, the actual character
1501 * [I] keyData : key data
1503 * RETURNS
1505 * Zero.
1507 * BUGS
1509 * - The current implementation has a list of characters it will
1510 * accept and it ignores everything else. In particular it will
1511 * ignore accentuated characters which seems to match what
1512 * Windows does. But I'm not sure it makes sense to follow
1513 * Windows there.
1514 * - We don't sound a beep when the search fails.
1516 * SEE ALSO
1518 * TREEVIEW_ProcessLetterKeys
1520 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1522 INT nItem;
1523 INT endidx,idx;
1524 LVITEMW item;
1525 WCHAR buffer[MAX_PATH];
1526 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1528 /* simple parameter checking */
1529 if (!charCode || !keyData) return 0;
1531 /* only allow the valid WM_CHARs through */
1532 if (!isalnumW(charCode) &&
1533 charCode != '.' && charCode != '`' && charCode != '!' &&
1534 charCode != '@' && charCode != '#' && charCode != '$' &&
1535 charCode != '%' && charCode != '^' && charCode != '&' &&
1536 charCode != '*' && charCode != '(' && charCode != ')' &&
1537 charCode != '-' && charCode != '_' && charCode != '+' &&
1538 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1539 charCode != '}' && charCode != '[' && charCode != '{' &&
1540 charCode != '/' && charCode != '?' && charCode != '>' &&
1541 charCode != '<' && charCode != ',' && charCode != '~')
1542 return 0;
1544 /* if there's one item or less, there is no where to go */
1545 if (infoPtr->nItemCount <= 1) return 0;
1547 /* update the search parameters */
1548 infoPtr->lastKeyPressTimestamp = GetTickCount();
1549 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1550 if (infoPtr->nSearchParamLength < MAX_PATH)
1551 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1552 if (infoPtr->charCode != charCode)
1553 infoPtr->charCode = charCode = 0;
1554 } else {
1555 infoPtr->charCode=charCode;
1556 infoPtr->szSearchParam[0]=charCode;
1557 infoPtr->nSearchParamLength=1;
1558 /* Redundant with the 1 char string */
1559 charCode=0;
1562 /* and search from the current position */
1563 nItem=-1;
1564 if (infoPtr->nFocusedItem >= 0) {
1565 endidx=infoPtr->nFocusedItem;
1566 idx=endidx;
1567 /* if looking for single character match,
1568 * then we must always move forward
1570 if (infoPtr->nSearchParamLength == 1)
1571 idx++;
1572 } else {
1573 endidx=infoPtr->nItemCount;
1574 idx=0;
1577 /* Let application handle this for virtual listview */
1578 if (infoPtr->dwStyle & LVS_OWNERDATA)
1580 NMLVFINDITEMW nmlv;
1581 LVFINDINFOW lvfi;
1583 ZeroMemory(&lvfi, sizeof(lvfi));
1584 lvfi.flags = (LVFI_WRAP | LVFI_PARTIAL);
1585 infoPtr->szSearchParam[infoPtr->nSearchParamLength] = '\0';
1586 lvfi.psz = infoPtr->szSearchParam;
1587 nmlv.iStart = idx;
1588 nmlv.lvfi = lvfi;
1590 nItem = notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
1592 if (nItem != -1)
1593 LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1595 return 0;
1598 do {
1599 if (idx == infoPtr->nItemCount) {
1600 if (endidx == infoPtr->nItemCount || endidx == 0)
1601 break;
1602 idx=0;
1605 /* get item */
1606 item.mask = LVIF_TEXT;
1607 item.iItem = idx;
1608 item.iSubItem = 0;
1609 item.pszText = buffer;
1610 item.cchTextMax = MAX_PATH;
1611 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1613 /* check for a match */
1614 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1615 nItem=idx;
1616 break;
1617 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1618 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1619 /* This would work but we must keep looking for a longer match */
1620 nItem=idx;
1622 idx++;
1623 } while (idx != endidx);
1625 if (nItem != -1)
1626 LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1628 return 0;
1631 /*************************************************************************
1632 * LISTVIEW_UpdateHeaderSize [Internal]
1634 * Function to resize the header control
1636 * PARAMS
1637 * [I] hwnd : handle to a window
1638 * [I] nNewScrollPos : scroll pos to set
1640 * RETURNS
1641 * None.
1643 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1645 RECT winRect;
1646 POINT point[2];
1648 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1650 GetWindowRect(infoPtr->hwndHeader, &winRect);
1651 point[0].x = winRect.left;
1652 point[0].y = winRect.top;
1653 point[1].x = winRect.right;
1654 point[1].y = winRect.bottom;
1656 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1657 point[0].x = -nNewScrollPos;
1658 point[1].x += nNewScrollPos;
1660 SetWindowPos(infoPtr->hwndHeader,0,
1661 point[0].x,point[0].y,point[1].x,point[1].y,
1662 (infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW |
1663 SWP_NOZORDER | SWP_NOACTIVATE);
1666 /***
1667 * DESCRIPTION:
1668 * Update the scrollbars. This functions should be called whenever
1669 * the content, size or view changes.
1671 * PARAMETER(S):
1672 * [I] infoPtr : valid pointer to the listview structure
1674 * RETURN:
1675 * None
1677 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *infoPtr)
1679 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1680 SCROLLINFO horzInfo, vertInfo;
1681 INT dx, dy;
1683 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1685 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1686 horzInfo.cbSize = sizeof(SCROLLINFO);
1687 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1689 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1690 if (uView == LVS_LIST)
1692 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1693 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1695 /* scroll by at least one column per page */
1696 if(horzInfo.nPage < infoPtr->nItemWidth)
1697 horzInfo.nPage = infoPtr->nItemWidth;
1699 horzInfo.nPage /= infoPtr->nItemWidth;
1701 else if (uView == LVS_REPORT)
1703 horzInfo.nMax = infoPtr->nItemWidth;
1705 else /* LVS_ICON, or LVS_SMALLICON */
1707 RECT rcView;
1709 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1712 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1713 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1714 dx = GetScrollPos(infoPtr->hwndSelf, SB_HORZ);
1715 dx -= SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1716 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1718 /* Setting the horizontal scroll can change the listview size
1719 * (and potentially everything else) so we need to recompute
1720 * everything again for the vertical scroll
1723 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1724 vertInfo.cbSize = sizeof(SCROLLINFO);
1725 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1727 if (uView == LVS_REPORT)
1729 vertInfo.nMax = infoPtr->nItemCount;
1731 /* scroll by at least one page */
1732 if(vertInfo.nPage < infoPtr->nItemHeight)
1733 vertInfo.nPage = infoPtr->nItemHeight;
1735 if (infoPtr->nItemHeight > 0)
1736 vertInfo.nPage /= infoPtr->nItemHeight;
1738 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1740 RECT rcView;
1742 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1745 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1746 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1747 dy = GetScrollPos(infoPtr->hwndSelf, SB_VERT);
1748 dy -= SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1749 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1751 /* Change of the range may have changed the scroll pos. If so move the content */
1752 if (dx != 0 || dy != 0)
1754 RECT listRect;
1755 listRect = infoPtr->rcList;
1756 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &listRect, &listRect, 0, 0,
1757 SW_ERASE | SW_INVALIDATE);
1760 /* Update the Header Control */
1761 if (uView == LVS_REPORT)
1763 horzInfo.fMask = SIF_POS;
1764 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1765 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1770 /***
1771 * DESCRIPTION:
1772 * Shows/hides the focus rectangle.
1774 * PARAMETER(S):
1775 * [I] infoPtr : valid pointer to the listview structure
1776 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1778 * RETURN:
1779 * None
1781 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO *infoPtr, BOOL fShow)
1783 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1784 HDC hdc;
1786 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1788 if (infoPtr->nFocusedItem < 0) return;
1790 /* we need some gymnastics in ICON mode to handle large items */
1791 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1793 RECT rcBox;
1795 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1796 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1798 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1799 return;
1803 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1805 /* for some reason, owner draw should work only in report mode */
1806 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1808 DRAWITEMSTRUCT dis;
1809 LVITEMW item;
1811 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1812 HFONT hOldFont = SelectObject(hdc, hFont);
1814 item.iItem = infoPtr->nFocusedItem;
1815 item.iSubItem = 0;
1816 item.mask = LVIF_PARAM;
1817 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1819 ZeroMemory(&dis, sizeof(dis));
1820 dis.CtlType = ODT_LISTVIEW;
1821 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1822 dis.itemID = item.iItem;
1823 dis.itemAction = ODA_FOCUS;
1824 if (fShow) dis.itemState |= ODS_FOCUS;
1825 dis.hwndItem = infoPtr->hwndSelf;
1826 dis.hDC = hdc;
1827 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1828 dis.itemData = item.lParam;
1830 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1832 SelectObject(hdc, hOldFont);
1834 else
1836 DrawFocusRect(hdc, &infoPtr->rcFocus);
1838 done:
1839 ReleaseDC(infoPtr->hwndSelf, hdc);
1842 /***
1843 * Invalidates all visible selected items.
1845 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO *infoPtr)
1847 ITERATOR i;
1849 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1850 while(iterator_next(&i))
1852 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1853 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1855 iterator_destroy(&i);
1859 /***
1860 * DESCRIPTION: [INTERNAL]
1861 * Computes an item's (left,top) corner, relative to rcView.
1862 * That is, the position has NOT been made relative to the Origin.
1863 * This is deliberate, to avoid computing the Origin over, and
1864 * over again, when this function is called in a loop. Instead,
1865 * one can factor the computation of the Origin before the loop,
1866 * and offset the value returned by this function, on every iteration.
1868 * PARAMETER(S):
1869 * [I] infoPtr : valid pointer to the listview structure
1870 * [I] nItem : item number
1871 * [O] lpptOrig : item top, left corner
1873 * RETURN:
1874 * None.
1876 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1878 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1880 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1882 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1884 lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1885 lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1887 else if (uView == LVS_LIST)
1889 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1890 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1891 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1893 else /* LVS_REPORT */
1895 lpptPosition->x = 0;
1896 lpptPosition->y = nItem * infoPtr->nItemHeight;
1900 /***
1901 * DESCRIPTION: [INTERNAL]
1902 * Compute the rectangles of an item. This is to localize all
1903 * the computations in one place. If you are not interested in some
1904 * of these values, simply pass in a NULL -- the function is smart
1905 * enough to compute only what's necessary. The function computes
1906 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1907 * one, the BOX rectangle. This rectangle is very cheap to compute,
1908 * and is guaranteed to contain all the other rectangles. Computing
1909 * the ICON rect is also cheap, but all the others are potentially
1910 * expensive. This gives an easy and effective optimization when
1911 * searching (like point inclusion, or rectangle intersection):
1912 * first test against the BOX, and if TRUE, test against the desired
1913 * rectangle.
1914 * If the function does not have all the necessary information
1915 * to computed the requested rectangles, will crash with a
1916 * failed assertion. This is done so we catch all programming
1917 * errors, given that the function is called only from our code.
1919 * We have the following 'special' meanings for a few fields:
1920 * * If LVIS_FOCUSED is set, we assume the item has the focus
1921 * This is important in ICON mode, where it might get a larger
1922 * then usual rectangle
1924 * Please note that subitem support works only in REPORT mode.
1926 * PARAMETER(S):
1927 * [I] infoPtr : valid pointer to the listview structure
1928 * [I] lpLVItem : item to compute the measures for
1929 * [O] lprcBox : ptr to Box rectangle
1930 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1931 * [0] lprcSelectBox : ptr to select box rectangle
1932 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
1933 * [O] lprcIcon : ptr to Icon rectangle
1934 * Same as LVM_GETITEMRECT with LVIR_ICON
1935 * [O] lprcStateIcon: ptr to State Icon rectangle
1936 * [O] lprcLabel : ptr to Label rectangle
1937 * Same as LVM_GETITEMRECT with LVIR_LABEL
1939 * RETURN:
1940 * None.
1942 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1943 LPRECT lprcBox, LPRECT lprcSelectBox,
1944 LPRECT lprcIcon, LPRECT lprcStateIcon, LPRECT lprcLabel)
1946 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1947 BOOL doSelectBox = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1948 RECT Box, SelectBox, Icon, Label;
1949 COLUMN_INFO *lpColumnInfo = NULL;
1950 SIZE labelSize = { 0, 0 };
1952 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1954 /* Be smart and try to figure out the minimum we have to do */
1955 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1956 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1958 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1959 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1961 if (lprcSelectBox) doSelectBox = TRUE;
1962 if (lprcLabel) doLabel = TRUE;
1963 if (doLabel || lprcIcon || lprcStateIcon) doIcon = TRUE;
1964 if (doSelectBox)
1966 doIcon = TRUE;
1967 doLabel = TRUE;
1970 /************************************************************/
1971 /* compute the box rectangle (it should be cheap to do) */
1972 /************************************************************/
1973 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1974 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1976 if (lpLVItem->iSubItem)
1978 Box = lpColumnInfo->rcHeader;
1980 else
1982 Box.left = 0;
1983 Box.right = infoPtr->nItemWidth;
1985 Box.top = 0;
1986 Box.bottom = infoPtr->nItemHeight;
1988 /******************************************************************/
1989 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
1990 /******************************************************************/
1991 if (doIcon)
1993 LONG state_width = 0;
1995 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1996 state_width = infoPtr->iconStateSize.cx;
1998 if (uView == LVS_ICON)
2000 Icon.left = Box.left + state_width;
2001 if (infoPtr->himlNormal)
2002 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx - state_width) / 2;
2003 Icon.top = Box.top + ICON_TOP_PADDING;
2004 Icon.right = Icon.left;
2005 Icon.bottom = Icon.top;
2006 if (infoPtr->himlNormal)
2008 Icon.right += infoPtr->iconSize.cx;
2009 Icon.bottom += infoPtr->iconSize.cy;
2012 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2014 Icon.left = Box.left + state_width;
2016 if (uView == LVS_REPORT)
2017 Icon.left += REPORT_MARGINX;
2019 Icon.top = Box.top;
2020 Icon.right = Icon.left;
2021 if (infoPtr->himlSmall &&
2022 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
2023 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
2024 Icon.right += infoPtr->iconSize.cx;
2025 Icon.bottom = Icon.top + infoPtr->iconSize.cy;
2027 if(lprcIcon) *lprcIcon = Icon;
2028 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon));
2030 /* TODO: is this correct? */
2031 if (lprcStateIcon)
2033 lprcStateIcon->left = Icon.left - state_width;
2034 lprcStateIcon->right = Icon.left;
2035 lprcStateIcon->top = Icon.top;
2036 lprcStateIcon->bottom = lprcStateIcon->top + infoPtr->iconSize.cy;
2037 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon));
2040 else Icon.right = 0;
2042 /************************************************************/
2043 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2044 /************************************************************/
2045 if (doLabel)
2047 /* calculate how far to the right can the label stretch */
2048 Label.right = Box.right;
2049 if (uView == LVS_REPORT)
2051 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
2054 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
2056 labelSize.cx = infoPtr->nItemWidth;
2057 labelSize.cy = infoPtr->nItemHeight;
2058 goto calc_label;
2061 /* we need the text in non owner draw mode */
2062 assert(lpLVItem->mask & LVIF_TEXT);
2063 if (is_textT(lpLVItem->pszText, TRUE))
2065 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2066 HDC hdc = GetDC(infoPtr->hwndSelf);
2067 HFONT hOldFont = SelectObject(hdc, hFont);
2068 UINT uFormat;
2069 RECT rcText;
2071 /* compute rough rectangle where the label will go */
2072 SetRectEmpty(&rcText);
2073 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2074 rcText.bottom = infoPtr->nItemHeight;
2075 if (uView == LVS_ICON)
2076 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2078 /* now figure out the flags */
2079 if (uView == LVS_ICON)
2080 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2081 else
2082 uFormat = LV_SL_DT_FLAGS;
2084 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2086 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2087 labelSize.cy = rcText.bottom - rcText.top;
2089 SelectObject(hdc, hOldFont);
2090 ReleaseDC(infoPtr->hwndSelf, hdc);
2093 calc_label:
2094 if (uView == LVS_ICON)
2096 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2097 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2098 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2099 Label.right = Label.left + labelSize.cx;
2100 Label.bottom = Label.top + infoPtr->nItemHeight;
2101 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2103 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2104 labelSize.cy /= infoPtr->ntmHeight;
2105 labelSize.cy = max(labelSize.cy, 1);
2106 labelSize.cy *= infoPtr->ntmHeight;
2108 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2110 else if (uView == LVS_REPORT)
2112 Label.left = Icon.right;
2113 Label.top = Box.top;
2114 Label.right = lpColumnInfo->rcHeader.right;
2115 Label.bottom = Label.top + infoPtr->nItemHeight;
2117 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2119 Label.left = Icon.right;
2120 Label.top = Box.top;
2121 Label.right = min(Label.left + labelSize.cx, Label.right);
2122 Label.bottom = Label.top + infoPtr->nItemHeight;
2125 if (lprcLabel) *lprcLabel = Label;
2126 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label));
2129 /************************************************************/
2130 /* compute STATEICON bounding box */
2131 /************************************************************/
2132 if (doSelectBox)
2134 if (uView == LVS_REPORT)
2136 SelectBox.left = Icon.right; /* FIXME: should be Icon.left */
2137 SelectBox.top = Box.top;
2138 SelectBox.bottom = Box.bottom;
2139 if (lpLVItem->iSubItem == 0)
2141 /* we need the indent in report mode */
2142 assert(lpLVItem->mask & LVIF_INDENT);
2143 SelectBox.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
2145 SelectBox.right = min(SelectBox.left + labelSize.cx, Label.right);
2147 else
2149 UnionRect(&SelectBox, &Icon, &Label);
2151 if (lprcSelectBox) *lprcSelectBox = SelectBox;
2152 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox));
2155 /* Fix the Box if necessary */
2156 if (lprcBox)
2158 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2159 else *lprcBox = Box;
2161 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box));
2164 /***
2165 * DESCRIPTION: [INTERNAL]
2167 * PARAMETER(S):
2168 * [I] infoPtr : valid pointer to the listview structure
2169 * [I] nItem : item number
2170 * [O] lprcBox : ptr to Box rectangle
2172 * RETURN:
2173 * None.
2175 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2177 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2178 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2179 POINT Position, Origin;
2180 LVITEMW lvItem;
2182 LISTVIEW_GetOrigin(infoPtr, &Origin);
2183 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2185 /* Be smart and try to figure out the minimum we have to do */
2186 lvItem.mask = 0;
2187 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2188 lvItem.mask |= LVIF_TEXT;
2189 lvItem.iItem = nItem;
2190 lvItem.iSubItem = 0;
2191 lvItem.pszText = szDispText;
2192 lvItem.cchTextMax = DISP_TEXT_SIZE;
2193 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2194 if (uView == LVS_ICON)
2196 lvItem.mask |= LVIF_STATE;
2197 lvItem.stateMask = LVIS_FOCUSED;
2198 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2200 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0, 0);
2202 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2206 /***
2207 * DESCRIPTION:
2208 * Returns the current icon position, and advances it along the top.
2209 * The returned position is not offset by Origin.
2211 * PARAMETER(S):
2212 * [I] infoPtr : valid pointer to the listview structure
2213 * [O] lpPos : will get the current icon position
2215 * RETURN:
2216 * None
2218 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2220 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2222 *lpPos = infoPtr->currIconPos;
2224 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2225 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2227 infoPtr->currIconPos.x = 0;
2228 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2232 /***
2233 * DESCRIPTION:
2234 * Returns the current icon position, and advances it down the left edge.
2235 * The returned position is not offset by Origin.
2237 * PARAMETER(S):
2238 * [I] infoPtr : valid pointer to the listview structure
2239 * [O] lpPos : will get the current icon position
2241 * RETURN:
2242 * None
2244 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2246 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2248 *lpPos = infoPtr->currIconPos;
2250 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2251 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2253 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2254 infoPtr->currIconPos.y = 0;
2258 /***
2259 * DESCRIPTION:
2260 * Moves an icon to the specified position.
2261 * It takes care of invalidating the item, etc.
2263 * PARAMETER(S):
2264 * [I] infoPtr : valid pointer to the listview structure
2265 * [I] nItem : the item to move
2266 * [I] lpPos : the new icon position
2267 * [I] isNew : flags the item as being new
2269 * RETURN:
2270 * Success: TRUE
2271 * Failure: FALSE
2273 static BOOL LISTVIEW_MoveIconTo(const LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2275 POINT old;
2277 if (!isNew)
2279 old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2280 old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2282 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2283 LISTVIEW_InvalidateItem(infoPtr, nItem);
2286 /* Allocating a POINTER for every item is too resource intensive,
2287 * so we'll keep the (x,y) in different arrays */
2288 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2289 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2291 LISTVIEW_InvalidateItem(infoPtr, nItem);
2293 return TRUE;
2296 /***
2297 * DESCRIPTION:
2298 * Arranges listview items in icon display mode.
2300 * PARAMETER(S):
2301 * [I] infoPtr : valid pointer to the listview structure
2302 * [I] nAlignCode : alignment code
2304 * RETURN:
2305 * SUCCESS : TRUE
2306 * FAILURE : FALSE
2308 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2310 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2311 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2312 POINT pos;
2313 INT i;
2315 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2317 TRACE("nAlignCode=%d\n", nAlignCode);
2319 if (nAlignCode == LVA_DEFAULT)
2321 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2322 else nAlignCode = LVA_ALIGNTOP;
2325 switch (nAlignCode)
2327 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2328 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2329 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2330 default: return FALSE;
2333 infoPtr->bAutoarrange = TRUE;
2334 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2335 for (i = 0; i < infoPtr->nItemCount; i++)
2337 next_pos(infoPtr, &pos);
2338 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2341 return TRUE;
2344 /***
2345 * DESCRIPTION:
2346 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2348 * PARAMETER(S):
2349 * [I] infoPtr : valid pointer to the listview structure
2350 * [O] lprcView : bounding rectangle
2352 * RETURN:
2353 * SUCCESS : TRUE
2354 * FAILURE : FALSE
2356 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2358 INT i, x, y;
2360 SetRectEmpty(lprcView);
2362 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2364 case LVS_ICON:
2365 case LVS_SMALLICON:
2366 for (i = 0; i < infoPtr->nItemCount; i++)
2368 x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2369 y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2370 lprcView->right = max(lprcView->right, x);
2371 lprcView->bottom = max(lprcView->bottom, y);
2373 if (infoPtr->nItemCount > 0)
2375 lprcView->right += infoPtr->nItemWidth;
2376 lprcView->bottom += infoPtr->nItemHeight;
2378 break;
2380 case LVS_LIST:
2381 y = LISTVIEW_GetCountPerColumn(infoPtr);
2382 x = infoPtr->nItemCount / y;
2383 if (infoPtr->nItemCount % y) x++;
2384 lprcView->right = x * infoPtr->nItemWidth;
2385 lprcView->bottom = y * infoPtr->nItemHeight;
2386 break;
2388 case LVS_REPORT:
2389 lprcView->right = infoPtr->nItemWidth;
2390 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2391 break;
2395 /***
2396 * DESCRIPTION:
2397 * Retrieves the bounding rectangle of all the items.
2399 * PARAMETER(S):
2400 * [I] infoPtr : valid pointer to the listview structure
2401 * [O] lprcView : bounding rectangle
2403 * RETURN:
2404 * SUCCESS : TRUE
2405 * FAILURE : FALSE
2407 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2409 POINT ptOrigin;
2411 TRACE("(lprcView=%p)\n", lprcView);
2413 if (!lprcView) return FALSE;
2415 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2416 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2417 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2419 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView));
2421 return TRUE;
2424 /***
2425 * DESCRIPTION:
2426 * Retrieves the subitem pointer associated with the subitem index.
2428 * PARAMETER(S):
2429 * [I] hdpaSubItems : DPA handle for a specific item
2430 * [I] nSubItem : index of subitem
2432 * RETURN:
2433 * SUCCESS : subitem pointer
2434 * FAILURE : NULL
2436 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2438 SUBITEM_INFO *lpSubItem;
2439 INT i;
2441 /* we should binary search here if need be */
2442 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2444 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
2445 if (lpSubItem->iSubItem == nSubItem)
2446 return lpSubItem;
2449 return NULL;
2453 /***
2454 * DESCRIPTION:
2455 * Calculates the desired item width.
2457 * PARAMETER(S):
2458 * [I] infoPtr : valid pointer to the listview structure
2460 * RETURN:
2461 * The desired item width.
2463 static INT LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO *infoPtr)
2465 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2466 INT nItemWidth = 0;
2468 TRACE("uView=%d\n", uView);
2470 if (uView == LVS_ICON)
2471 nItemWidth = infoPtr->iconSpacing.cx;
2472 else if (uView == LVS_REPORT)
2474 RECT rcHeader;
2476 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2478 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2479 nItemWidth = rcHeader.right;
2482 else /* LVS_SMALLICON, or LVS_LIST */
2484 INT i;
2486 for (i = 0; i < infoPtr->nItemCount; i++)
2487 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2489 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2490 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2492 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2495 return max(nItemWidth, 1);
2498 /***
2499 * DESCRIPTION:
2500 * Calculates the desired item height.
2502 * PARAMETER(S):
2503 * [I] infoPtr : valid pointer to the listview structure
2505 * RETURN:
2506 * The desired item height.
2508 static INT LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO *infoPtr)
2510 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2511 INT nItemHeight;
2513 TRACE("uView=%d\n", uView);
2515 if (uView == LVS_ICON)
2516 nItemHeight = infoPtr->iconSpacing.cy;
2517 else
2519 nItemHeight = infoPtr->ntmHeight;
2520 if (uView == LVS_REPORT && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
2521 nItemHeight++;
2522 if (infoPtr->himlState)
2523 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2524 if (infoPtr->himlSmall)
2525 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2526 if (infoPtr->himlState || infoPtr->himlSmall)
2527 nItemHeight += HEIGHT_PADDING;
2528 if (infoPtr->nMeasureItemHeight > 0)
2529 nItemHeight = infoPtr->nMeasureItemHeight;
2532 return max(nItemHeight, 1);
2535 /***
2536 * DESCRIPTION:
2537 * Updates the width, and height of an item.
2539 * PARAMETER(S):
2540 * [I] infoPtr : valid pointer to the listview structure
2542 * RETURN:
2543 * None.
2545 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2547 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2548 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2552 /***
2553 * DESCRIPTION:
2554 * Retrieves and saves important text metrics info for the current
2555 * Listview font.
2557 * PARAMETER(S):
2558 * [I] infoPtr : valid pointer to the listview structure
2561 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2563 HDC hdc = GetDC(infoPtr->hwndSelf);
2564 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2565 HFONT hOldFont = SelectObject(hdc, hFont);
2566 TEXTMETRICW tm;
2567 SIZE sz;
2569 if (GetTextMetricsW(hdc, &tm))
2571 infoPtr->ntmHeight = tm.tmHeight;
2572 infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
2575 if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
2576 infoPtr->nEllipsisWidth = sz.cx;
2578 SelectObject(hdc, hOldFont);
2579 ReleaseDC(infoPtr->hwndSelf, hdc);
2581 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2584 /***
2585 * DESCRIPTION:
2586 * A compare function for ranges
2588 * PARAMETER(S)
2589 * [I] range1 : pointer to range 1;
2590 * [I] range2 : pointer to range 2;
2591 * [I] flags : flags
2593 * RETURNS:
2594 * > 0 : if range 1 > range 2
2595 * < 0 : if range 2 > range 1
2596 * = 0 : if range intersects range 2
2598 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2600 INT cmp;
2602 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2603 cmp = -1;
2604 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2605 cmp = 1;
2606 else
2607 cmp = 0;
2609 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1), debugrange(range2), cmp);
2611 return cmp;
2614 #if DEBUG_RANGES
2615 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2616 #else
2617 #define ranges_check(ranges, desc) do { } while(0)
2618 #endif
2620 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2622 INT i;
2623 RANGE *prev, *curr;
2625 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2626 assert (ranges);
2627 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2628 ranges_dump(ranges);
2629 prev = DPA_GetPtr(ranges->hdpa, 0);
2630 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2631 assert (prev->lower >= 0 && prev->lower < prev->upper);
2632 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2634 curr = DPA_GetPtr(ranges->hdpa, i);
2635 assert (prev->upper <= curr->lower);
2636 assert (curr->lower < curr->upper);
2637 prev = curr;
2639 TRACE("--- Done checking---\n");
2642 static RANGES ranges_create(int count)
2644 RANGES ranges = Alloc(sizeof(struct tagRANGES));
2645 if (!ranges) return NULL;
2646 ranges->hdpa = DPA_Create(count);
2647 if (ranges->hdpa) return ranges;
2648 Free(ranges);
2649 return NULL;
2652 static void ranges_clear(RANGES ranges)
2654 INT i;
2656 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2657 Free(DPA_GetPtr(ranges->hdpa, i));
2658 DPA_DeleteAllPtrs(ranges->hdpa);
2662 static void ranges_destroy(RANGES ranges)
2664 if (!ranges) return;
2665 ranges_clear(ranges);
2666 DPA_Destroy(ranges->hdpa);
2667 Free(ranges);
2670 static RANGES ranges_clone(RANGES ranges)
2672 RANGES clone;
2673 INT i;
2675 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2677 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2679 RANGE *newrng = Alloc(sizeof(RANGE));
2680 if (!newrng) goto fail;
2681 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2682 DPA_SetPtr(clone->hdpa, i, newrng);
2684 return clone;
2686 fail:
2687 TRACE ("clone failed\n");
2688 ranges_destroy(clone);
2689 return NULL;
2692 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2694 INT i;
2696 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2697 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2699 return ranges;
2702 static void ranges_dump(RANGES ranges)
2704 INT i;
2706 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2707 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2710 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2712 RANGE srchrng = { nItem, nItem + 1 };
2714 TRACE("(nItem=%d)\n", nItem);
2715 ranges_check(ranges, "before contain");
2716 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2719 static INT ranges_itemcount(RANGES ranges)
2721 INT i, count = 0;
2723 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2725 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2726 count += sel->upper - sel->lower;
2729 return count;
2732 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2734 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2735 INT index;
2737 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2738 if (index == -1) return TRUE;
2740 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2742 chkrng = DPA_GetPtr(ranges->hdpa, index);
2743 if (chkrng->lower >= nItem)
2744 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2745 if (chkrng->upper > nItem)
2746 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2748 return TRUE;
2751 static BOOL ranges_add(RANGES ranges, RANGE range)
2753 RANGE srchrgn;
2754 INT index;
2756 TRACE("(%s)\n", debugrange(&range));
2757 ranges_check(ranges, "before add");
2759 /* try find overlapping regions first */
2760 srchrgn.lower = range.lower - 1;
2761 srchrgn.upper = range.upper + 1;
2762 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2764 if (index == -1)
2766 RANGE *newrgn;
2768 TRACE("Adding new range\n");
2770 /* create the brand new range to insert */
2771 newrgn = Alloc(sizeof(RANGE));
2772 if(!newrgn) goto fail;
2773 *newrgn = range;
2775 /* figure out where to insert it */
2776 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2777 TRACE("index=%d\n", index);
2778 if (index == -1) index = 0;
2780 /* and get it over with */
2781 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2783 Free(newrgn);
2784 goto fail;
2787 else
2789 RANGE *chkrgn, *mrgrgn;
2790 INT fromindex, mergeindex;
2792 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2793 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2795 chkrgn->lower = min(range.lower, chkrgn->lower);
2796 chkrgn->upper = max(range.upper, chkrgn->upper);
2798 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2800 /* merge now common ranges */
2801 fromindex = 0;
2802 srchrgn.lower = chkrgn->lower - 1;
2803 srchrgn.upper = chkrgn->upper + 1;
2807 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2808 if (mergeindex == -1) break;
2809 if (mergeindex == index)
2811 fromindex = index + 1;
2812 continue;
2815 TRACE("Merge with index %i\n", mergeindex);
2817 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2818 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2819 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2820 Free(mrgrgn);
2821 DPA_DeletePtr(ranges->hdpa, mergeindex);
2822 if (mergeindex < index) index --;
2823 } while(1);
2826 ranges_check(ranges, "after add");
2827 return TRUE;
2829 fail:
2830 ranges_check(ranges, "failed add");
2831 return FALSE;
2834 static BOOL ranges_del(RANGES ranges, RANGE range)
2836 RANGE *chkrgn;
2837 INT index;
2839 TRACE("(%s)\n", debugrange(&range));
2840 ranges_check(ranges, "before del");
2842 /* we don't use DPAS_SORTED here, since we need *
2843 * to find the first overlapping range */
2844 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2845 while(index != -1)
2847 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2849 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2851 /* case 1: Same range */
2852 if ( (chkrgn->upper == range.upper) &&
2853 (chkrgn->lower == range.lower) )
2855 DPA_DeletePtr(ranges->hdpa, index);
2856 break;
2858 /* case 2: engulf */
2859 else if ( (chkrgn->upper <= range.upper) &&
2860 (chkrgn->lower >= range.lower) )
2862 DPA_DeletePtr(ranges->hdpa, index);
2864 /* case 3: overlap upper */
2865 else if ( (chkrgn->upper <= range.upper) &&
2866 (chkrgn->lower < range.lower) )
2868 chkrgn->upper = range.lower;
2870 /* case 4: overlap lower */
2871 else if ( (chkrgn->upper > range.upper) &&
2872 (chkrgn->lower >= range.lower) )
2874 chkrgn->lower = range.upper;
2875 break;
2877 /* case 5: fully internal */
2878 else
2880 RANGE tmprgn = *chkrgn, *newrgn;
2882 if (!(newrgn = Alloc(sizeof(RANGE)))) goto fail;
2883 newrgn->lower = chkrgn->lower;
2884 newrgn->upper = range.lower;
2885 chkrgn->lower = range.upper;
2886 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2888 Free(newrgn);
2889 goto fail;
2891 chkrgn = &tmprgn;
2892 break;
2895 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2898 ranges_check(ranges, "after del");
2899 return TRUE;
2901 fail:
2902 ranges_check(ranges, "failed del");
2903 return FALSE;
2906 /***
2907 * DESCRIPTION:
2908 * Removes all selection ranges
2910 * Parameters(s):
2911 * [I] infoPtr : valid pointer to the listview structure
2912 * [I] toSkip : item range to skip removing the selection
2914 * RETURNS:
2915 * SUCCESS : TRUE
2916 * FAILURE : TRUE
2918 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2920 LVITEMW lvItem;
2921 ITERATOR i;
2922 RANGES clone;
2924 TRACE("()\n");
2926 lvItem.state = 0;
2927 lvItem.stateMask = LVIS_SELECTED;
2929 /* need to clone the DPA because callbacks can change it */
2930 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2931 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2932 while(iterator_next(&i))
2933 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2934 /* note that the iterator destructor will free the cloned range */
2935 iterator_destroy(&i);
2937 return TRUE;
2940 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2942 RANGES toSkip;
2944 if (!(toSkip = ranges_create(1))) return FALSE;
2945 if (nItem != -1) ranges_additem(toSkip, nItem);
2946 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2947 ranges_destroy(toSkip);
2948 return TRUE;
2951 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2953 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2956 /***
2957 * DESCRIPTION:
2958 * Retrieves the number of items that are marked as selected.
2960 * PARAMETER(S):
2961 * [I] infoPtr : valid pointer to the listview structure
2963 * RETURN:
2964 * Number of items selected.
2966 static INT LISTVIEW_GetSelectedCount(const LISTVIEW_INFO *infoPtr)
2968 INT nSelectedCount = 0;
2970 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2972 INT i;
2973 for (i = 0; i < infoPtr->nItemCount; i++)
2975 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2976 nSelectedCount++;
2979 else
2980 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2982 TRACE("nSelectedCount=%d\n", nSelectedCount);
2983 return nSelectedCount;
2986 /***
2987 * DESCRIPTION:
2988 * Manages the item focus.
2990 * PARAMETER(S):
2991 * [I] infoPtr : valid pointer to the listview structure
2992 * [I] nItem : item index
2994 * RETURN:
2995 * TRUE : focused item changed
2996 * FALSE : focused item has NOT changed
2998 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
3000 INT oldFocus = infoPtr->nFocusedItem;
3001 LVITEMW lvItem;
3003 if (nItem == infoPtr->nFocusedItem) return FALSE;
3005 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
3006 lvItem.stateMask = LVIS_FOCUSED;
3007 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
3009 return oldFocus != infoPtr->nFocusedItem;
3012 /* Helper function for LISTVIEW_ShiftIndices *only* */
3013 static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
3015 if (nShiftItem < nItem) return nShiftItem;
3017 if (nShiftItem > nItem) return nShiftItem + direction;
3019 if (direction > 0) return nShiftItem + direction;
3021 return min(nShiftItem, infoPtr->nItemCount - 1);
3025 * DESCRIPTION:
3026 * Updates the various indices after an item has been inserted or deleted.
3028 * PARAMETER(S):
3029 * [I] infoPtr : valid pointer to the listview structure
3030 * [I] nItem : item index
3031 * [I] direction : Direction of shift, +1 or -1.
3033 * RETURN:
3034 * None
3036 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
3038 INT nNewFocus;
3039 BOOL bOldChange;
3041 /* temporarily disable change notification while shifting items */
3042 bOldChange = infoPtr->bDoChangeNotify;
3043 infoPtr->bDoChangeNotify = FALSE;
3045 TRACE("Shifting %iu, %i steps\n", nItem, direction);
3047 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
3049 assert(abs(direction) == 1);
3051 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
3053 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
3054 if (nNewFocus != infoPtr->nFocusedItem)
3055 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
3057 /* But we are not supposed to modify nHotItem! */
3059 infoPtr->bDoChangeNotify = bOldChange;
3064 * DESCRIPTION:
3065 * Adds a block of selections.
3067 * PARAMETER(S):
3068 * [I] infoPtr : valid pointer to the listview structure
3069 * [I] nItem : item index
3071 * RETURN:
3072 * Whether the window is still valid.
3074 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3076 INT nFirst = min(infoPtr->nSelectionMark, nItem);
3077 INT nLast = max(infoPtr->nSelectionMark, nItem);
3078 HWND hwndSelf = infoPtr->hwndSelf;
3079 NMLVODSTATECHANGE nmlv;
3080 LVITEMW item;
3081 BOOL bOldChange;
3082 INT i;
3084 /* Temporarily disable change notification
3085 * If the control is LVS_OWNERDATA, we need to send
3086 * only one LVN_ODSTATECHANGED notification.
3087 * See MSDN documentation for LVN_ITEMCHANGED.
3089 bOldChange = infoPtr->bDoChangeNotify;
3090 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3092 if (nFirst == -1) nFirst = nItem;
3094 item.state = LVIS_SELECTED;
3095 item.stateMask = LVIS_SELECTED;
3097 for (i = nFirst; i <= nLast; i++)
3098 LISTVIEW_SetItemState(infoPtr,i,&item);
3100 ZeroMemory(&nmlv, sizeof(nmlv));
3101 nmlv.iFrom = nFirst;
3102 nmlv.iTo = nLast;
3103 nmlv.uNewState = 0;
3104 nmlv.uOldState = item.state;
3106 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3107 if (!IsWindow(hwndSelf))
3108 return FALSE;
3109 infoPtr->bDoChangeNotify = bOldChange;
3110 return TRUE;
3114 /***
3115 * DESCRIPTION:
3116 * Sets a single group selection.
3118 * PARAMETER(S):
3119 * [I] infoPtr : valid pointer to the listview structure
3120 * [I] nItem : item index
3122 * RETURN:
3123 * None
3125 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3127 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3128 RANGES selection;
3129 LVITEMW item;
3130 ITERATOR i;
3131 BOOL bOldChange;
3133 if (!(selection = ranges_create(100))) return;
3135 item.state = LVIS_SELECTED;
3136 item.stateMask = LVIS_SELECTED;
3138 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3140 if (infoPtr->nSelectionMark == -1)
3142 infoPtr->nSelectionMark = nItem;
3143 ranges_additem(selection, nItem);
3145 else
3147 RANGE sel;
3149 sel.lower = min(infoPtr->nSelectionMark, nItem);
3150 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3151 ranges_add(selection, sel);
3154 else
3156 RECT rcItem, rcSel, rcSelMark;
3157 POINT ptItem;
3159 rcItem.left = LVIR_BOUNDS;
3160 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3161 rcSelMark.left = LVIR_BOUNDS;
3162 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3163 UnionRect(&rcSel, &rcItem, &rcSelMark);
3164 iterator_frameditems(&i, infoPtr, &rcSel);
3165 while(iterator_next(&i))
3167 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3168 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3170 iterator_destroy(&i);
3173 bOldChange = infoPtr->bDoChangeNotify;
3174 infoPtr->bDoChangeNotify = FALSE;
3176 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3179 iterator_rangesitems(&i, selection);
3180 while(iterator_next(&i))
3181 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3182 /* this will also destroy the selection */
3183 iterator_destroy(&i);
3185 infoPtr->bDoChangeNotify = bOldChange;
3187 LISTVIEW_SetItemFocus(infoPtr, nItem);
3190 /***
3191 * DESCRIPTION:
3192 * Sets a single selection.
3194 * PARAMETER(S):
3195 * [I] infoPtr : valid pointer to the listview structure
3196 * [I] nItem : item index
3198 * RETURN:
3199 * None
3201 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3203 LVITEMW lvItem;
3205 TRACE("nItem=%d\n", nItem);
3207 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3209 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3210 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3211 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3213 infoPtr->nSelectionMark = nItem;
3216 /***
3217 * DESCRIPTION:
3218 * Set selection(s) with keyboard.
3220 * PARAMETER(S):
3221 * [I] infoPtr : valid pointer to the listview structure
3222 * [I] nItem : item index
3223 * [I] space : VK_SPACE code sent
3225 * RETURN:
3226 * SUCCESS : TRUE (needs to be repainted)
3227 * FAILURE : FALSE (nothing has changed)
3229 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem, BOOL space)
3231 /* FIXME: pass in the state */
3232 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3233 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3234 BOOL bResult = FALSE;
3236 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3237 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3239 if (infoPtr->dwStyle & LVS_SINGLESEL)
3241 bResult = TRUE;
3242 LISTVIEW_SetSelection(infoPtr, nItem);
3244 else
3246 if (wShift)
3248 bResult = TRUE;
3249 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3251 else if (wCtrl)
3253 LVITEMW lvItem;
3254 lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3255 lvItem.stateMask = LVIS_SELECTED;
3256 if (space)
3258 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3259 if (lvItem.state & LVIS_SELECTED)
3260 infoPtr->nSelectionMark = nItem;
3262 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3264 else
3266 bResult = TRUE;
3267 LISTVIEW_SetSelection(infoPtr, nItem);
3270 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3273 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3274 return bResult;
3277 static BOOL LISTVIEW_GetItemAtPt(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3279 LVHITTESTINFO lvHitTestInfo;
3281 ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3282 lvHitTestInfo.pt.x = pt.x;
3283 lvHitTestInfo.pt.y = pt.y;
3285 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3287 lpLVItem->mask = LVIF_PARAM;
3288 lpLVItem->iItem = lvHitTestInfo.iItem;
3289 lpLVItem->iSubItem = 0;
3291 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3294 static inline BOOL LISTVIEW_isHotTracking(const LISTVIEW_INFO *infoPtr)
3296 return ((infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) ||
3297 (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE) ||
3298 (infoPtr->dwLvExStyle & LVS_EX_TWOCLICKACTIVATE));
3301 /***
3302 * DESCRIPTION:
3303 * Called when the mouse is being actively tracked and has hovered for a specified
3304 * amount of time
3306 * PARAMETER(S):
3307 * [I] infoPtr : valid pointer to the listview structure
3308 * [I] fwKeys : key indicator
3309 * [I] x,y : mouse position
3311 * RETURN:
3312 * 0 if the message was processed, non-zero if there was an error
3314 * INFO:
3315 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3316 * over the item for a certain period of time.
3319 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3321 if (LISTVIEW_isHotTracking(infoPtr))
3323 LVITEMW item;
3324 POINT pt;
3326 pt.x = x;
3327 pt.y = y;
3329 if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3330 LISTVIEW_SetSelection(infoPtr, item.iItem);
3333 return 0;
3336 /***
3337 * DESCRIPTION:
3338 * Called whenever WM_MOUSEMOVE is received.
3340 * PARAMETER(S):
3341 * [I] infoPtr : valid pointer to the listview structure
3342 * [I] fwKeys : key indicator
3343 * [I] x,y : mouse position
3345 * RETURN:
3346 * 0 if the message is processed, non-zero if there was an error
3348 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3350 TRACKMOUSEEVENT trackinfo;
3352 if (!(fwKeys & MK_LBUTTON))
3353 infoPtr->bLButtonDown = FALSE;
3355 if (infoPtr->bLButtonDown)
3357 POINT tmp;
3358 RECT rect;
3359 WORD wDragWidth = GetSystemMetrics(SM_CXDRAG);
3360 WORD wDragHeight= GetSystemMetrics(SM_CYDRAG);
3362 rect.left = infoPtr->ptClickPos.x - wDragWidth;
3363 rect.right = infoPtr->ptClickPos.x + wDragWidth;
3364 rect.top = infoPtr->ptClickPos.y - wDragHeight;
3365 rect.bottom = infoPtr->ptClickPos.y + wDragHeight;
3367 tmp.x = x;
3368 tmp.y = y;
3370 if (!PtInRect(&rect, tmp))
3372 LVHITTESTINFO lvHitTestInfo;
3373 NMLISTVIEW nmlv;
3375 lvHitTestInfo.pt = infoPtr->ptClickPos;
3376 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3378 ZeroMemory(&nmlv, sizeof(nmlv));
3379 nmlv.iItem = lvHitTestInfo.iItem;
3380 nmlv.ptAction = infoPtr->ptClickPos;
3382 if (!infoPtr->bDragging)
3384 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3385 infoPtr->bDragging = TRUE;
3388 return 0;
3391 else
3392 infoPtr->bLButtonDown = FALSE;
3394 /* see if we are supposed to be tracking mouse hovering */
3395 if (LISTVIEW_isHotTracking(infoPtr)) {
3396 /* fill in the trackinfo struct */
3397 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3398 trackinfo.dwFlags = TME_QUERY;
3399 trackinfo.hwndTrack = infoPtr->hwndSelf;
3400 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3402 /* see if we are already tracking this hwnd */
3403 _TrackMouseEvent(&trackinfo);
3405 if(!(trackinfo.dwFlags & TME_HOVER)) {
3406 trackinfo.dwFlags = TME_HOVER;
3408 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3409 _TrackMouseEvent(&trackinfo);
3413 return 0;
3417 /***
3418 * Tests whether the item is assignable to a list with style lStyle
3420 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3422 if ( (lpLVItem->mask & LVIF_TEXT) &&
3423 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3424 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3426 return TRUE;
3430 /***
3431 * DESCRIPTION:
3432 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3434 * PARAMETER(S):
3435 * [I] infoPtr : valid pointer to the listview structure
3436 * [I] lpLVItem : valid pointer to new item attributes
3437 * [I] isNew : the item being set is being inserted
3438 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3439 * [O] bChanged : will be set to TRUE if the item really changed
3441 * RETURN:
3442 * SUCCESS : TRUE
3443 * FAILURE : FALSE
3445 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3447 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3448 ITEM_INFO *lpItem;
3449 NMLISTVIEW nmlv;
3450 UINT uChanged = 0;
3451 LVITEMW item;
3453 TRACE("()\n");
3455 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3457 if (lpLVItem->mask == 0) return TRUE;
3459 if (infoPtr->dwStyle & LVS_OWNERDATA)
3461 /* a virtual listview only stores selection and focus */
3462 if (lpLVItem->mask & ~LVIF_STATE)
3463 return FALSE;
3464 lpItem = NULL;
3466 else
3468 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3469 lpItem = DPA_GetPtr(hdpaSubItems, 0);
3470 assert (lpItem);
3473 /* we need to get the lParam and state of the item */
3474 item.iItem = lpLVItem->iItem;
3475 item.iSubItem = lpLVItem->iSubItem;
3476 item.mask = LVIF_STATE | LVIF_PARAM;
3477 item.stateMask = ~0;
3478 item.state = 0;
3479 item.lParam = 0;
3480 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3482 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3483 /* determine what fields will change */
3484 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3485 uChanged |= LVIF_STATE;
3487 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3488 uChanged |= LVIF_IMAGE;
3490 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3491 uChanged |= LVIF_PARAM;
3493 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3494 uChanged |= LVIF_INDENT;
3496 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3497 uChanged |= LVIF_TEXT;
3499 TRACE("uChanged=0x%x\n", uChanged);
3500 if (!uChanged) return TRUE;
3501 *bChanged = TRUE;
3503 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3504 nmlv.iItem = lpLVItem->iItem;
3505 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3506 nmlv.uOldState = item.state;
3507 nmlv.uChanged = uChanged;
3508 nmlv.lParam = item.lParam;
3510 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3511 /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
3512 /* are enabled */
3513 if(lpItem && !isNew && infoPtr->bDoChangeNotify)
3515 HWND hwndSelf = infoPtr->hwndSelf;
3517 if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3518 return FALSE;
3519 if (!IsWindow(hwndSelf))
3520 return FALSE;
3523 /* copy information */
3524 if (lpLVItem->mask & LVIF_TEXT)
3525 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3527 if (lpLVItem->mask & LVIF_IMAGE)
3528 lpItem->hdr.iImage = lpLVItem->iImage;
3530 if (lpLVItem->mask & LVIF_PARAM)
3531 lpItem->lParam = lpLVItem->lParam;
3533 if (lpLVItem->mask & LVIF_INDENT)
3534 lpItem->iIndent = lpLVItem->iIndent;
3536 if (uChanged & LVIF_STATE)
3538 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3540 lpItem->state &= ~lpLVItem->stateMask;
3541 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3543 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3545 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3546 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3548 else if (lpLVItem->stateMask & LVIS_SELECTED)
3549 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3551 /* if we are asked to change focus, and we manage it, do it */
3552 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3554 if (lpLVItem->state & LVIS_FOCUSED)
3556 LISTVIEW_SetItemFocus(infoPtr, -1);
3557 infoPtr->nFocusedItem = lpLVItem->iItem;
3558 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3560 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3561 infoPtr->nFocusedItem = -1;
3565 /* if we're inserting the item, we're done */
3566 if (isNew) return TRUE;
3568 /* send LVN_ITEMCHANGED notification */
3569 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3570 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3572 return TRUE;
3575 /***
3576 * DESCRIPTION:
3577 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3579 * PARAMETER(S):
3580 * [I] infoPtr : valid pointer to the listview structure
3581 * [I] lpLVItem : valid pointer to new subitem attributes
3582 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3583 * [O] bChanged : will be set to TRUE if the item really changed
3585 * RETURN:
3586 * SUCCESS : TRUE
3587 * FAILURE : FALSE
3589 static BOOL set_sub_item(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3591 HDPA hdpaSubItems;
3592 SUBITEM_INFO *lpSubItem;
3594 /* we do not support subitems for virtual listviews */
3595 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3597 /* set subitem only if column is present */
3598 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3600 /* First do some sanity checks */
3601 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
3602 particularly useful. We currently do not actually do anything with
3603 the flag on subitems.
3605 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)) return FALSE;
3606 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))) return TRUE;
3608 /* get the subitem structure, and create it if not there */
3609 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3610 assert (hdpaSubItems);
3612 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3613 if (!lpSubItem)
3615 SUBITEM_INFO *tmpSubItem;
3616 INT i;
3618 lpSubItem = Alloc(sizeof(SUBITEM_INFO));
3619 if (!lpSubItem) return FALSE;
3620 /* we could binary search here, if need be...*/
3621 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3623 tmpSubItem = DPA_GetPtr(hdpaSubItems, i);
3624 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3626 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3628 Free(lpSubItem);
3629 return FALSE;
3631 lpSubItem->iSubItem = lpLVItem->iSubItem;
3632 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3633 *bChanged = TRUE;
3636 if (lpLVItem->mask & LVIF_IMAGE)
3637 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3639 lpSubItem->hdr.iImage = lpLVItem->iImage;
3640 *bChanged = TRUE;
3643 if (lpLVItem->mask & LVIF_TEXT)
3644 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3646 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3647 *bChanged = TRUE;
3650 return TRUE;
3653 /***
3654 * DESCRIPTION:
3655 * Sets item attributes.
3657 * PARAMETER(S):
3658 * [I] infoPtr : valid pointer to the listview structure
3659 * [I] lpLVItem : new item attributes
3660 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3662 * RETURN:
3663 * SUCCESS : TRUE
3664 * FAILURE : FALSE
3666 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem, BOOL isW)
3668 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3669 HWND hwndSelf = infoPtr->hwndSelf;
3670 LPWSTR pszText = NULL;
3671 BOOL bResult, bChanged = FALSE;
3673 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3675 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3676 return FALSE;
3678 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3679 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3681 pszText = lpLVItem->pszText;
3682 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
3685 /* actually set the fields */
3686 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3688 if (lpLVItem->iSubItem)
3689 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3690 else
3691 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3692 if (!IsWindow(hwndSelf))
3693 return FALSE;
3695 /* redraw item, if necessary */
3696 if (bChanged && !infoPtr->bIsDrawing)
3698 /* this little optimization eliminates some nasty flicker */
3699 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3700 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
3701 lpLVItem->iSubItem > 0 && lpLVItem->iSubItem <= DPA_GetPtrCount(infoPtr->hdpaColumns) )
3702 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3703 else
3704 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3706 /* restore text */
3707 if (pszText)
3709 textfreeT(lpLVItem->pszText, isW);
3710 lpLVItem->pszText = pszText;
3713 return bResult;
3716 /***
3717 * DESCRIPTION:
3718 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3720 * PARAMETER(S):
3721 * [I] infoPtr : valid pointer to the listview structure
3723 * RETURN:
3724 * item index
3726 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *infoPtr)
3728 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3729 INT nItem = 0;
3730 SCROLLINFO scrollInfo;
3732 scrollInfo.cbSize = sizeof(SCROLLINFO);
3733 scrollInfo.fMask = SIF_POS;
3735 if (uView == LVS_LIST)
3737 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3738 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3740 else if (uView == LVS_REPORT)
3742 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3743 nItem = scrollInfo.nPos;
3745 else
3747 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3748 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3751 TRACE("nItem=%d\n", nItem);
3753 return nItem;
3757 /***
3758 * DESCRIPTION:
3759 * Erases the background of the given rectangle
3761 * PARAMETER(S):
3762 * [I] infoPtr : valid pointer to the listview structure
3763 * [I] hdc : device context handle
3764 * [I] lprcBox : clipping rectangle
3766 * RETURN:
3767 * Success: TRUE
3768 * Failure: FALSE
3770 static inline BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3772 if (!infoPtr->hBkBrush) return FALSE;
3774 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
3776 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3779 /***
3780 * DESCRIPTION:
3781 * Draws an item.
3783 * PARAMETER(S):
3784 * [I] infoPtr : valid pointer to the listview structure
3785 * [I] hdc : device context handle
3786 * [I] nItem : item index
3787 * [I] nSubItem : subitem index
3788 * [I] pos : item position in client coordinates
3789 * [I] cdmode : custom draw mode
3791 * RETURN:
3792 * Success: TRUE
3793 * Failure: FALSE
3795 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3797 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3798 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3799 static WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3800 DWORD cdsubitemmode = CDRF_DODEFAULT;
3801 LPRECT lprcFocus;
3802 RECT rcSelect, rcBox, rcIcon, rcLabel, rcStateIcon;
3803 NMLVCUSTOMDRAW nmlvcd;
3804 HIMAGELIST himl;
3805 LVITEMW lvItem;
3806 HFONT hOldFont;
3808 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
3810 /* get information needed for drawing the item */
3811 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
3812 if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
3813 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3814 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3815 lvItem.iItem = nItem;
3816 lvItem.iSubItem = nSubItem;
3817 lvItem.state = 0;
3818 lvItem.lParam = 0;
3819 lvItem.cchTextMax = DISP_TEXT_SIZE;
3820 lvItem.pszText = szDispText;
3821 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3822 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3823 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3824 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
3825 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3827 /* now check if we need to update the focus rectangle */
3828 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3830 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3831 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
3832 OffsetRect(&rcBox, pos.x, pos.y);
3833 OffsetRect(&rcSelect, pos.x, pos.y);
3834 OffsetRect(&rcIcon, pos.x, pos.y);
3835 OffsetRect(&rcStateIcon, pos.x, pos.y);
3836 OffsetRect(&rcLabel, pos.x, pos.y);
3837 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
3838 wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect),
3839 wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
3841 /* fill in the custom draw structure */
3842 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3844 hOldFont = GetCurrentObject(hdc, OBJ_FONT);
3845 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3846 if (cdmode & CDRF_SKIPDEFAULT) goto postpaint;
3847 if (cdmode & CDRF_NOTIFYITEMDRAW)
3848 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3849 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3850 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3851 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3852 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3854 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3855 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3857 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3858 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
3859 else if ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) == FALSE)
3860 prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE);
3862 /* in full row select, subitems, will just use main item's colors */
3863 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3864 nmlvcd.clrTextBk = CLR_NONE;
3866 /* state icons */
3867 if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && (nSubItem == 0))
3869 UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
3870 if (uStateImage)
3872 TRACE("uStateImage=%d\n", uStateImage);
3873 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
3874 rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
3878 /* small icons */
3879 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3880 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3882 TRACE("iImage=%d\n", lvItem.iImage);
3883 ImageList_DrawEx(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3884 rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk, CLR_DEFAULT,
3885 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3888 /* Don't bother painting item being edited */
3889 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3891 /* FIXME: temporary hack */
3892 rcSelect.left = rcLabel.left;
3894 /* draw the selection background, if we're drawing the main item */
3895 if (nSubItem == 0)
3897 /* in icon mode, the label rect is really what we want to draw the
3898 * background for */
3899 if (uView == LVS_ICON)
3900 rcSelect = rcLabel;
3902 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3903 rcSelect.right = rcBox.right;
3905 if (nmlvcd.clrTextBk != CLR_NONE)
3906 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3907 if(lprcFocus) *lprcFocus = rcSelect;
3910 /* figure out the text drawing flags */
3911 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3912 if (uView == LVS_ICON)
3913 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3914 else if (nSubItem)
3916 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3918 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3919 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3920 default: uFormat |= DT_LEFT;
3923 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3925 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3926 else rcLabel.left += LABEL_HOR_PADDING;
3928 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3930 /* for GRIDLINES reduce the bottom so the text formats correctly */
3931 if (uView == LVS_REPORT && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
3932 rcLabel.bottom--;
3934 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3936 postpaint:
3937 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3938 notify_postpaint(infoPtr, &nmlvcd);
3939 if (cdsubitemmode & CDRF_NEWFONT)
3940 SelectObject(hdc, hOldFont);
3941 return TRUE;
3944 /***
3945 * DESCRIPTION:
3946 * Draws listview items when in owner draw mode.
3948 * PARAMETER(S):
3949 * [I] infoPtr : valid pointer to the listview structure
3950 * [I] hdc : device context handle
3952 * RETURN:
3953 * None
3955 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3957 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3958 DWORD cditemmode = CDRF_DODEFAULT;
3959 NMLVCUSTOMDRAW nmlvcd;
3960 POINT Origin, Position;
3961 DRAWITEMSTRUCT dis;
3962 LVITEMW item;
3964 TRACE("()\n");
3966 ZeroMemory(&dis, sizeof(dis));
3968 /* Get scroll info once before loop */
3969 LISTVIEW_GetOrigin(infoPtr, &Origin);
3971 /* iterate through the invalidated rows */
3972 while(iterator_next(i))
3974 item.iItem = i->nItem;
3975 item.iSubItem = 0;
3976 item.mask = LVIF_PARAM | LVIF_STATE;
3977 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3978 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3980 dis.CtlType = ODT_LISTVIEW;
3981 dis.CtlID = uID;
3982 dis.itemID = item.iItem;
3983 dis.itemAction = ODA_DRAWENTIRE;
3984 dis.itemState = 0;
3985 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3986 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3987 dis.hwndItem = infoPtr->hwndSelf;
3988 dis.hDC = hdc;
3989 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3990 dis.rcItem.left = Position.x + Origin.x;
3991 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3992 dis.rcItem.top = Position.y + Origin.y;
3993 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3994 dis.itemData = item.lParam;
3996 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
3999 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
4000 * structure for the rest. of the paint cycle
4002 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
4003 if (cdmode & CDRF_NOTIFYITEMDRAW)
4004 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4006 if (!(cditemmode & CDRF_SKIPDEFAULT))
4008 prepaint_setup (infoPtr, hdc, &nmlvcd, FALSE);
4009 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
4012 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
4013 notify_postpaint(infoPtr, &nmlvcd);
4017 /***
4018 * DESCRIPTION:
4019 * Draws listview items when in report display mode.
4021 * PARAMETER(S):
4022 * [I] infoPtr : valid pointer to the listview structure
4023 * [I] hdc : device context handle
4024 * [I] cdmode : custom draw mode
4026 * RETURN:
4027 * None
4029 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4031 INT rgntype;
4032 RECT rcClip, rcItem;
4033 POINT Origin, Position;
4034 RANGE colRange;
4035 ITERATOR j;
4037 TRACE("()\n");
4039 /* figure out what to draw */
4040 rgntype = GetClipBox(hdc, &rcClip);
4041 if (rgntype == NULLREGION) return;
4043 /* Get scroll info once before loop */
4044 LISTVIEW_GetOrigin(infoPtr, &Origin);
4046 /* narrow down the columns we need to paint */
4047 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
4049 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
4050 if (rcItem.right + Origin.x >= rcClip.left) break;
4052 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
4054 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
4055 if (rcItem.left + Origin.x < rcClip.right) break;
4057 iterator_rangeitems(&j, colRange);
4059 /* in full row select, we _have_ to draw the main item */
4060 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
4061 j.nSpecial = 0;
4063 /* iterate through the invalidated rows */
4064 while(iterator_next(i))
4066 /* iterate through the invalidated columns */
4067 while(iterator_next(&j))
4069 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4070 Position.x += Origin.x;
4071 Position.y += Origin.y;
4073 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
4075 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4076 rcItem.top = 0;
4077 rcItem.bottom = infoPtr->nItemHeight;
4078 OffsetRect(&rcItem, Position.x, Position.y);
4079 if (!RectVisible(hdc, &rcItem)) continue;
4082 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
4085 iterator_destroy(&j);
4088 /***
4089 * DESCRIPTION:
4090 * Draws the gridlines if necessary when in report display mode.
4092 * PARAMETER(S):
4093 * [I] infoPtr : valid pointer to the listview structure
4094 * [I] hdc : device context handle
4096 * RETURN:
4097 * None
4099 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO *infoPtr, HDC hdc)
4101 INT rgntype;
4102 INT y, itemheight;
4103 HPEN hPen, hOldPen;
4104 RECT rcClip, rcItem;
4105 POINT Origin;
4106 RANGE colRange;
4107 ITERATOR j;
4109 TRACE("()\n");
4111 /* figure out what to draw */
4112 rgntype = GetClipBox(hdc, &rcClip);
4113 if (rgntype == NULLREGION) return;
4115 /* Get scroll info once before loop */
4116 LISTVIEW_GetOrigin(infoPtr, &Origin);
4118 /* narrow down the columns we need to paint */
4119 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
4121 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
4122 if (rcItem.right + Origin.x >= rcClip.left) break;
4124 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
4126 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
4127 if (rcItem.left + Origin.x < rcClip.right) break;
4129 iterator_rangeitems(&j, colRange);
4131 if ((hPen = CreatePen( PS_SOLID, 1, comctl32_color.clr3dFace )))
4133 hOldPen = SelectObject ( hdc, hPen );
4135 /* draw the vertical lines for the columns */
4136 iterator_rangeitems(&j, colRange);
4137 while(iterator_next(&j))
4139 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4140 if (rcItem.left == 0) continue; /* skip first column */
4141 rcItem.left += Origin.x;
4142 rcItem.right += Origin.x;
4143 rcItem.top = infoPtr->rcList.top;
4144 rcItem.bottom = infoPtr->rcList.bottom;
4145 TRACE("vert col=%d, rcItem=%s\n", j.nItem, wine_dbgstr_rect(&rcItem));
4146 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4147 LineTo (hdc, rcItem.left, rcItem.bottom);
4149 iterator_destroy(&j);
4151 /* draw the horizontial lines for the rows */
4152 itemheight = LISTVIEW_CalculateItemHeight(infoPtr);
4153 rcItem.left = infoPtr->rcList.left + Origin.x;
4154 rcItem.right = infoPtr->rcList.right + Origin.x;
4155 rcItem.bottom = rcItem.top = Origin.y - 1;
4156 MoveToEx(hdc, rcItem.left, rcItem.top, NULL);
4157 LineTo(hdc, rcItem.right, rcItem.top);
4158 for(y=itemheight-1+Origin.y; y<=infoPtr->rcList.bottom; y+=itemheight)
4160 rcItem.bottom = rcItem.top = y;
4161 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem));
4162 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4163 LineTo (hdc, rcItem.right, rcItem.top);
4166 SelectObject( hdc, hOldPen );
4167 DeleteObject( hPen );
4171 /***
4172 * DESCRIPTION:
4173 * Draws listview items when in list display mode.
4175 * PARAMETER(S):
4176 * [I] infoPtr : valid pointer to the listview structure
4177 * [I] hdc : device context handle
4178 * [I] cdmode : custom draw mode
4180 * RETURN:
4181 * None
4183 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4185 POINT Origin, Position;
4187 /* Get scroll info once before loop */
4188 LISTVIEW_GetOrigin(infoPtr, &Origin);
4190 while(iterator_prev(i))
4192 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4193 Position.x += Origin.x;
4194 Position.y += Origin.y;
4196 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
4201 /***
4202 * DESCRIPTION:
4203 * Draws listview items.
4205 * PARAMETER(S):
4206 * [I] infoPtr : valid pointer to the listview structure
4207 * [I] hdc : device context handle
4208 * [I] prcErase : rect to be erased before refresh (may be NULL)
4210 * RETURN:
4211 * NoneX
4213 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcErase)
4215 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4216 COLORREF oldTextColor = 0, oldBkColor = 0, oldClrTextBk, oldClrText;
4217 NMLVCUSTOMDRAW nmlvcd;
4218 HFONT hOldFont = 0;
4219 DWORD cdmode;
4220 INT oldBkMode = 0;
4221 RECT rcClient;
4222 ITERATOR i;
4223 HDC hdcOrig = hdc;
4224 HBITMAP hbmp = NULL;
4226 LISTVIEW_DUMP(infoPtr);
4228 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4229 TRACE("double buffering\n");
4231 hdc = CreateCompatibleDC(hdcOrig);
4232 if (!hdc) {
4233 ERR("Failed to create DC for backbuffer\n");
4234 return;
4236 hbmp = CreateCompatibleBitmap(hdcOrig, infoPtr->rcList.right,
4237 infoPtr->rcList.bottom);
4238 if (!hbmp) {
4239 ERR("Failed to create bitmap for backbuffer\n");
4240 DeleteDC(hdc);
4241 return;
4244 SelectObject(hdc, hbmp);
4245 SelectObject(hdc, infoPtr->hFont);
4246 } else {
4247 /* Save dc values we're gonna trash while drawing
4248 * FIXME: Should be done in LISTVIEW_DrawItem() */
4249 hOldFont = SelectObject(hdc, infoPtr->hFont);
4250 oldBkMode = GetBkMode(hdc);
4251 oldBkColor = GetBkColor(hdc);
4252 oldTextColor = GetTextColor(hdc);
4255 infoPtr->bIsDrawing = TRUE;
4257 if (prcErase) {
4258 LISTVIEW_FillBkgnd(infoPtr, hdc, prcErase);
4259 } else if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4260 /* If no erasing was done (usually because RedrawWindow was called
4261 * with RDW_INVALIDATE only) we need to copy the old contents into
4262 * the backbuffer before continuing. */
4263 BitBlt(hdc, infoPtr->rcList.left, infoPtr->rcList.top,
4264 infoPtr->rcList.right - infoPtr->rcList.left,
4265 infoPtr->rcList.bottom - infoPtr->rcList.top,
4266 hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4269 /* FIXME: Shouldn't need to do this */
4270 oldClrTextBk = infoPtr->clrTextBk;
4271 oldClrText = infoPtr->clrText;
4273 infoPtr->cditemmode = CDRF_DODEFAULT;
4275 GetClientRect(infoPtr->hwndSelf, &rcClient);
4276 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4277 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4278 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4279 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4281 /* Use these colors to draw the items */
4282 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4283 infoPtr->clrText = nmlvcd.clrText;
4285 /* nothing to draw */
4286 if(infoPtr->nItemCount == 0) goto enddraw;
4288 /* figure out what we need to draw */
4289 iterator_visibleitems(&i, infoPtr, hdc);
4291 /* send cache hint notification */
4292 if (infoPtr->dwStyle & LVS_OWNERDATA)
4294 RANGE range = iterator_range(&i);
4295 NMLVCACHEHINT nmlv;
4297 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4298 nmlv.iFrom = range.lower;
4299 nmlv.iTo = range.upper - 1;
4300 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4303 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4304 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4305 else
4307 if (uView == LVS_REPORT)
4308 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4309 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4310 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4312 /* if we have a focus rect, draw it */
4313 if (infoPtr->bFocus)
4314 DrawFocusRect(hdc, &infoPtr->rcFocus);
4316 iterator_destroy(&i);
4318 enddraw:
4319 /* For LVS_EX_GRIDLINES go and draw lines */
4320 /* This includes the case where there were *no* items */
4321 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
4322 infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
4323 LISTVIEW_RefreshReportGrid(infoPtr, hdc);
4325 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4326 notify_postpaint(infoPtr, &nmlvcd);
4328 infoPtr->clrTextBk = oldClrTextBk;
4329 infoPtr->clrText = oldClrText;
4331 if(hbmp) {
4332 BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top,
4333 infoPtr->rcList.right - infoPtr->rcList.left,
4334 infoPtr->rcList.bottom - infoPtr->rcList.top,
4335 hdc, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4337 DeleteObject(hbmp);
4338 DeleteDC(hdc);
4339 } else {
4340 SelectObject(hdc, hOldFont);
4341 SetBkMode(hdc, oldBkMode);
4342 SetBkColor(hdc, oldBkColor);
4343 SetTextColor(hdc, oldTextColor);
4346 infoPtr->bIsDrawing = FALSE;
4350 /***
4351 * DESCRIPTION:
4352 * Calculates the approximate width and height of a given number of items.
4354 * PARAMETER(S):
4355 * [I] infoPtr : valid pointer to the listview structure
4356 * [I] nItemCount : number of items
4357 * [I] wWidth : width
4358 * [I] wHeight : height
4360 * RETURN:
4361 * Returns a DWORD. The width in the low word and the height in high word.
4363 static DWORD LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO *infoPtr, INT nItemCount,
4364 WORD wWidth, WORD wHeight)
4366 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4367 INT nItemCountPerColumn = 1;
4368 INT nColumnCount = 0;
4369 DWORD dwViewRect = 0;
4371 if (nItemCount == -1)
4372 nItemCount = infoPtr->nItemCount;
4374 if (uView == LVS_LIST)
4376 if (wHeight == 0xFFFF)
4378 /* use current height */
4379 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4382 if (wHeight < infoPtr->nItemHeight)
4383 wHeight = infoPtr->nItemHeight;
4385 if (nItemCount > 0)
4387 if (infoPtr->nItemHeight > 0)
4389 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4390 if (nItemCountPerColumn == 0)
4391 nItemCountPerColumn = 1;
4393 if (nItemCount % nItemCountPerColumn != 0)
4394 nColumnCount = nItemCount / nItemCountPerColumn;
4395 else
4396 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4400 /* Microsoft padding magic */
4401 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4402 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4404 dwViewRect = MAKELONG(wWidth, wHeight);
4406 else if (uView == LVS_REPORT)
4408 RECT rcBox;
4410 if (infoPtr->nItemCount > 0)
4412 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4413 wWidth = rcBox.right - rcBox.left;
4414 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4416 else
4418 /* use current height and width */
4419 if (wHeight == 0xffff)
4420 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4421 if (wWidth == 0xffff)
4422 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4425 dwViewRect = MAKELONG(wWidth, wHeight);
4427 else if (uView == LVS_SMALLICON)
4428 FIXME("uView == LVS_SMALLICON: not implemented\n");
4429 else if (uView == LVS_ICON)
4430 FIXME("uView == LVS_ICON: not implemented\n");
4432 return dwViewRect;
4436 /***
4437 * DESCRIPTION:
4438 * Create a drag image list for the specified item.
4440 * PARAMETER(S):
4441 * [I] infoPtr : valid pointer to the listview structure
4442 * [I] iItem : index of item
4443 * [O] lppt : Upperr-left corner of the image
4445 * RETURN:
4446 * Returns a handle to the image list if successful, NULL otherwise.
4448 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4450 RECT rcItem;
4451 SIZE size;
4452 POINT pos;
4453 HDC hdc, hdcOrig;
4454 HBITMAP hbmp, hOldbmp;
4455 HIMAGELIST dragList = 0;
4456 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
4458 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4459 return 0;
4461 rcItem.left = LVIR_BOUNDS;
4462 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4463 return 0;
4465 lppt->x = rcItem.left;
4466 lppt->y = rcItem.top;
4468 size.cx = rcItem.right - rcItem.left;
4469 size.cy = rcItem.bottom - rcItem.top;
4471 hdcOrig = GetDC(infoPtr->hwndSelf);
4472 hdc = CreateCompatibleDC(hdcOrig);
4473 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4474 hOldbmp = SelectObject(hdc, hbmp);
4476 rcItem.left = rcItem.top = 0;
4477 rcItem.right = size.cx;
4478 rcItem.bottom = size.cy;
4479 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4481 pos.x = pos.y = 0;
4482 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4484 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4485 SelectObject(hdc, hOldbmp);
4486 ImageList_Add(dragList, hbmp, 0);
4488 else
4489 SelectObject(hdc, hOldbmp);
4491 DeleteObject(hbmp);
4492 DeleteDC(hdc);
4493 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4495 TRACE("ret=%p\n", dragList);
4497 return dragList;
4501 /***
4502 * DESCRIPTION:
4503 * Removes all listview items and subitems.
4505 * PARAMETER(S):
4506 * [I] infoPtr : valid pointer to the listview structure
4508 * RETURN:
4509 * SUCCESS : TRUE
4510 * FAILURE : FALSE
4512 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr, BOOL destroy)
4514 NMLISTVIEW nmlv;
4515 HDPA hdpaSubItems = NULL;
4516 BOOL bSuppress;
4517 ITEMHDR *hdrItem;
4518 INT i, j;
4520 TRACE("()\n");
4522 /* we do it directly, to avoid notifications */
4523 ranges_clear(infoPtr->selectionRanges);
4524 infoPtr->nSelectionMark = -1;
4525 infoPtr->nFocusedItem = -1;
4526 SetRectEmpty(&infoPtr->rcFocus);
4527 /* But we are supposed to leave nHotItem as is! */
4530 /* send LVN_DELETEALLITEMS notification */
4531 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4532 nmlv.iItem = -1;
4533 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4535 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4537 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4539 /* send LVN_DELETEITEM notification, if not suppressed
4540 and if it is not a virtual listview */
4541 if (!bSuppress) notify_deleteitem(infoPtr, i);
4542 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
4543 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4545 hdrItem = DPA_GetPtr(hdpaSubItems, j);
4546 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4547 Free(hdrItem);
4549 DPA_Destroy(hdpaSubItems);
4550 DPA_DeletePtr(infoPtr->hdpaItems, i);
4552 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4553 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4554 infoPtr->nItemCount --;
4557 if (!destroy)
4559 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4560 LISTVIEW_UpdateScroll(infoPtr);
4562 LISTVIEW_InvalidateList(infoPtr);
4564 return TRUE;
4567 /***
4568 * DESCRIPTION:
4569 * Scrolls, and updates the columns, when a column is changing width.
4571 * PARAMETER(S):
4572 * [I] infoPtr : valid pointer to the listview structure
4573 * [I] nColumn : column to scroll
4574 * [I] dx : amount of scroll, in pixels
4576 * RETURN:
4577 * None.
4579 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4581 COLUMN_INFO *lpColumnInfo;
4582 RECT rcOld, rcCol;
4583 POINT ptOrigin;
4584 INT nCol;
4586 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4587 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4588 rcCol = lpColumnInfo->rcHeader;
4589 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4590 rcCol.left = rcCol.right;
4592 /* adjust the other columns */
4593 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4595 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4596 lpColumnInfo->rcHeader.left += dx;
4597 lpColumnInfo->rcHeader.right += dx;
4600 /* do not update screen if not in report mode */
4601 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4603 /* if we have a focus, we must first erase the focus rect */
4604 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4606 /* Need to reset the item width when inserting a new column */
4607 infoPtr->nItemWidth += dx;
4609 LISTVIEW_UpdateScroll(infoPtr);
4610 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4612 /* scroll to cover the deleted column, and invalidate for redraw */
4613 rcOld = infoPtr->rcList;
4614 rcOld.left = ptOrigin.x + rcCol.left + dx;
4615 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4617 /* we can restore focus now */
4618 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4621 /***
4622 * DESCRIPTION:
4623 * Removes a column from the listview control.
4625 * PARAMETER(S):
4626 * [I] infoPtr : valid pointer to the listview structure
4627 * [I] nColumn : column index
4629 * RETURN:
4630 * SUCCESS : TRUE
4631 * FAILURE : FALSE
4633 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4635 RECT rcCol;
4637 TRACE("nColumn=%d\n", nColumn);
4639 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4640 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4642 /* While the MSDN specifically says that column zero should not be deleted,
4643 what actually happens is that the column itself is deleted but no items or subitems
4644 are removed.
4647 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4649 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4650 return FALSE;
4652 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4653 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4655 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4657 SUBITEM_INFO *lpSubItem, *lpDelItem;
4658 HDPA hdpaSubItems;
4659 INT nItem, nSubItem, i;
4661 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4663 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
4664 nSubItem = 0;
4665 lpDelItem = 0;
4666 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4668 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
4669 if (lpSubItem->iSubItem == nColumn)
4671 nSubItem = i;
4672 lpDelItem = lpSubItem;
4674 else if (lpSubItem->iSubItem > nColumn)
4676 lpSubItem->iSubItem--;
4680 /* if we found our subitem, zapp it */
4681 if (nSubItem > 0)
4683 /* free string */
4684 if (is_textW(lpDelItem->hdr.pszText))
4685 Free(lpDelItem->hdr.pszText);
4687 /* free item */
4688 Free(lpDelItem);
4690 /* free dpa memory */
4691 DPA_DeletePtr(hdpaSubItems, nSubItem);
4696 /* update the other column info */
4697 LISTVIEW_UpdateItemSize(infoPtr);
4698 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4699 LISTVIEW_InvalidateList(infoPtr);
4700 else
4701 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4703 return TRUE;
4706 /***
4707 * DESCRIPTION:
4708 * Invalidates the listview after an item's insertion or deletion.
4710 * PARAMETER(S):
4711 * [I] infoPtr : valid pointer to the listview structure
4712 * [I] nItem : item index
4713 * [I] dir : -1 if deleting, 1 if inserting
4715 * RETURN:
4716 * None
4718 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4720 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4721 INT nPerCol, nItemCol, nItemRow;
4722 RECT rcScroll;
4723 POINT Origin;
4725 /* if we don't refresh, what's the point of scrolling? */
4726 if (!is_redrawing(infoPtr)) return;
4728 assert (abs(dir) == 1);
4730 /* arrange icons if autoarrange is on */
4731 if (is_autoarrange(infoPtr))
4733 BOOL arrange = TRUE;
4734 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4735 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4736 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4739 /* scrollbars need updating */
4740 LISTVIEW_UpdateScroll(infoPtr);
4742 /* figure out the item's position */
4743 if (uView == LVS_REPORT)
4744 nPerCol = infoPtr->nItemCount + 1;
4745 else if (uView == LVS_LIST)
4746 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4747 else /* LVS_ICON, or LVS_SMALLICON */
4748 return;
4750 nItemCol = nItem / nPerCol;
4751 nItemRow = nItem % nPerCol;
4752 LISTVIEW_GetOrigin(infoPtr, &Origin);
4754 /* move the items below up a slot */
4755 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4756 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4757 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4758 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4759 OffsetRect(&rcScroll, Origin.x, Origin.y);
4760 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
4761 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4763 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
4764 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4765 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4768 /* report has only that column, so we're done */
4769 if (uView == LVS_REPORT) return;
4771 /* now for LISTs, we have to deal with the columns to the right */
4772 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4773 rcScroll.top = 0;
4774 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4775 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4776 OffsetRect(&rcScroll, Origin.x, Origin.y);
4777 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4778 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4779 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4782 /***
4783 * DESCRIPTION:
4784 * Removes an item from the listview control.
4786 * PARAMETER(S):
4787 * [I] infoPtr : valid pointer to the listview structure
4788 * [I] nItem : item index
4790 * RETURN:
4791 * SUCCESS : TRUE
4792 * FAILURE : FALSE
4794 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4796 LVITEMW item;
4797 const UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4798 const BOOL is_icon = (uView == LVS_SMALLICON || uView == LVS_ICON);
4800 TRACE("(nItem=%d)\n", nItem);
4802 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4804 /* remove selection, and focus */
4805 item.state = 0;
4806 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4807 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4809 /* send LVN_DELETEITEM notification. */
4810 if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
4812 /* we need to do this here, because we'll be deleting stuff */
4813 if (is_icon)
4814 LISTVIEW_InvalidateItem(infoPtr, nItem);
4816 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4818 HDPA hdpaSubItems;
4819 ITEMHDR *hdrItem;
4820 INT i;
4822 hdpaSubItems = DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4823 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4825 hdrItem = DPA_GetPtr(hdpaSubItems, i);
4826 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4827 Free(hdrItem);
4829 DPA_Destroy(hdpaSubItems);
4832 if (is_icon)
4834 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4835 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4838 infoPtr->nItemCount--;
4839 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4841 /* now is the invalidation fun */
4842 if (!is_icon)
4843 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4844 return TRUE;
4848 /***
4849 * DESCRIPTION:
4850 * Callback implementation for editlabel control
4852 * PARAMETER(S):
4853 * [I] infoPtr : valid pointer to the listview structure
4854 * [I] pszText : modified text
4855 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4857 * RETURN:
4858 * SUCCESS : TRUE
4859 * FAILURE : FALSE
4861 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4863 HWND hwndSelf = infoPtr->hwndSelf;
4864 NMLVDISPINFOW dispInfo;
4865 INT editedItem = infoPtr->nEditLabelItem;
4866 BOOL bSame;
4868 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4870 infoPtr->nEditLabelItem = -1;
4872 ZeroMemory(&dispInfo, sizeof(dispInfo));
4873 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4874 dispInfo.item.iItem = editedItem;
4875 dispInfo.item.iSubItem = 0;
4876 dispInfo.item.stateMask = ~0;
4877 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4879 if (isW)
4880 bSame = (lstrcmpW(dispInfo.item.pszText, pszText) == 0);
4881 else
4883 LPWSTR tmp = textdupTtoW(pszText, FALSE);
4884 bSame = (lstrcmpW(dispInfo.item.pszText, tmp) == 0);
4885 textfreeT(tmp, FALSE);
4887 if (bSame) return TRUE;
4889 /* add the text from the edit in */
4890 dispInfo.item.mask |= LVIF_TEXT;
4891 dispInfo.item.pszText = pszText;
4892 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4894 /* Do we need to update the Item Text */
4895 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4896 if (!IsWindow(hwndSelf))
4897 return FALSE;
4898 if (!pszText) return TRUE;
4900 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4902 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, editedItem);
4903 ITEM_INFO* lpItem = DPA_GetPtr(hdpaSubItems, 0);
4904 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4906 LISTVIEW_InvalidateItem(infoPtr, editedItem);
4907 return TRUE;
4911 ZeroMemory(&dispInfo, sizeof(dispInfo));
4912 dispInfo.item.mask = LVIF_TEXT;
4913 dispInfo.item.iItem = editedItem;
4914 dispInfo.item.iSubItem = 0;
4915 dispInfo.item.pszText = pszText;
4916 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4917 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4920 /***
4921 * DESCRIPTION:
4922 * Begin in place editing of specified list view item
4924 * PARAMETER(S):
4925 * [I] infoPtr : valid pointer to the listview structure
4926 * [I] nItem : item index
4927 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4929 * RETURN:
4930 * SUCCESS : TRUE
4931 * FAILURE : FALSE
4933 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4935 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4936 NMLVDISPINFOW dispInfo;
4937 RECT rect;
4938 HWND hwndSelf = infoPtr->hwndSelf;
4940 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4942 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4943 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4945 infoPtr->nEditLabelItem = nItem;
4947 /* Is the EditBox still there, if so remove it */
4948 if(infoPtr->hwndEdit != 0)
4950 SetFocus(infoPtr->hwndSelf);
4951 infoPtr->hwndEdit = 0;
4954 LISTVIEW_SetSelection(infoPtr, nItem);
4955 LISTVIEW_SetItemFocus(infoPtr, nItem);
4956 LISTVIEW_InvalidateItem(infoPtr, nItem);
4958 rect.left = LVIR_LABEL;
4959 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4961 ZeroMemory(&dispInfo, sizeof(dispInfo));
4962 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4963 dispInfo.item.iItem = nItem;
4964 dispInfo.item.iSubItem = 0;
4965 dispInfo.item.stateMask = ~0;
4966 dispInfo.item.pszText = szDispText;
4967 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4968 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4970 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4971 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4972 if (!infoPtr->hwndEdit) return 0;
4974 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4976 if (!IsWindow(hwndSelf))
4977 return 0;
4978 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4979 infoPtr->hwndEdit = 0;
4980 return 0;
4983 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4984 SetFocus(infoPtr->hwndEdit);
4985 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4986 return infoPtr->hwndEdit;
4990 /***
4991 * DESCRIPTION:
4992 * Ensures the specified item is visible, scrolling into view if necessary.
4994 * PARAMETER(S):
4995 * [I] infoPtr : valid pointer to the listview structure
4996 * [I] nItem : item index
4997 * [I] bPartial : partially or entirely visible
4999 * RETURN:
5000 * SUCCESS : TRUE
5001 * FAILURE : FALSE
5003 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
5005 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5006 INT nScrollPosHeight = 0;
5007 INT nScrollPosWidth = 0;
5008 INT nHorzAdjust = 0;
5009 INT nVertAdjust = 0;
5010 INT nHorzDiff = 0;
5011 INT nVertDiff = 0;
5012 RECT rcItem, rcTemp;
5014 rcItem.left = LVIR_BOUNDS;
5015 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
5017 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
5019 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
5021 /* scroll left/right, but in LVS_REPORT mode */
5022 if (uView == LVS_LIST)
5023 nScrollPosWidth = infoPtr->nItemWidth;
5024 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5025 nScrollPosWidth = 1;
5027 if (rcItem.left < infoPtr->rcList.left)
5029 nHorzAdjust = -1;
5030 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
5032 else
5034 nHorzAdjust = 1;
5035 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
5039 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
5041 /* scroll up/down, but not in LVS_LIST mode */
5042 if (uView == LVS_REPORT)
5043 nScrollPosHeight = infoPtr->nItemHeight;
5044 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
5045 nScrollPosHeight = 1;
5047 if (rcItem.top < infoPtr->rcList.top)
5049 nVertAdjust = -1;
5050 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
5052 else
5054 nVertAdjust = 1;
5055 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
5059 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
5061 if (nScrollPosWidth)
5063 INT diff = nHorzDiff / nScrollPosWidth;
5064 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
5065 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
5068 if (nScrollPosHeight)
5070 INT diff = nVertDiff / nScrollPosHeight;
5071 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
5072 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
5075 return TRUE;
5078 /***
5079 * DESCRIPTION:
5080 * Searches for an item with specific characteristics.
5082 * PARAMETER(S):
5083 * [I] hwnd : window handle
5084 * [I] nStart : base item index
5085 * [I] lpFindInfo : item information to look for
5087 * RETURN:
5088 * SUCCESS : index of item
5089 * FAILURE : -1
5091 static INT LISTVIEW_FindItemW(const LISTVIEW_INFO *infoPtr, INT nStart,
5092 const LVFINDINFOW *lpFindInfo)
5094 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5095 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5096 BOOL bWrap = FALSE, bNearest = FALSE;
5097 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
5098 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
5099 POINT Position, Destination;
5100 LVITEMW lvItem;
5102 /* Search in virtual listviews should be done by application, not by
5103 listview control, so we just send LVN_ODFINDITEMW and return the result */
5104 if (infoPtr->dwStyle & LVS_OWNERDATA)
5106 NMLVFINDITEMW nmlv;
5108 nmlv.iStart = nStart;
5109 nmlv.lvfi = *lpFindInfo;
5110 return notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
5113 if (!lpFindInfo || nItem < 0) return -1;
5115 lvItem.mask = 0;
5116 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
5118 lvItem.mask |= LVIF_TEXT;
5119 lvItem.pszText = szDispText;
5120 lvItem.cchTextMax = DISP_TEXT_SIZE;
5123 if (lpFindInfo->flags & LVFI_WRAP)
5124 bWrap = TRUE;
5126 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
5127 (uView == LVS_ICON || uView ==LVS_SMALLICON))
5129 POINT Origin;
5130 RECT rcArea;
5132 LISTVIEW_GetOrigin(infoPtr, &Origin);
5133 Destination.x = lpFindInfo->pt.x - Origin.x;
5134 Destination.y = lpFindInfo->pt.y - Origin.y;
5135 switch(lpFindInfo->vkDirection)
5137 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
5138 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
5139 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
5140 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
5141 case VK_HOME: Destination.x = Destination.y = 0; break;
5142 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5143 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5144 case VK_END:
5145 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
5146 Destination.x = rcArea.right;
5147 Destination.y = rcArea.bottom;
5148 break;
5149 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
5151 bNearest = TRUE;
5153 else Destination.x = Destination.y = 0;
5155 /* if LVFI_PARAM is specified, all other flags are ignored */
5156 if (lpFindInfo->flags & LVFI_PARAM)
5158 lvItem.mask |= LVIF_PARAM;
5159 bNearest = FALSE;
5160 lvItem.mask &= ~LVIF_TEXT;
5163 again:
5164 for (; nItem < nLast; nItem++)
5166 lvItem.iItem = nItem;
5167 lvItem.iSubItem = 0;
5168 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
5170 if (lvItem.mask & LVIF_PARAM)
5172 if (lpFindInfo->lParam == lvItem.lParam)
5173 return nItem;
5174 else
5175 continue;
5178 if (lvItem.mask & LVIF_TEXT)
5180 if (lpFindInfo->flags & LVFI_PARTIAL)
5182 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
5184 else
5186 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
5190 if (!bNearest) return nItem;
5192 /* This is very inefficient. To do a good job here,
5193 * we need a sorted array of (x,y) item positions */
5194 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5196 /* compute the distance^2 to the destination */
5197 xdist = Destination.x - Position.x;
5198 ydist = Destination.y - Position.y;
5199 dist = xdist * xdist + ydist * ydist;
5201 /* remember the distance, and item if it's closer */
5202 if (dist < mindist)
5204 mindist = dist;
5205 nNearestItem = nItem;
5209 if (bWrap)
5211 nItem = 0;
5212 nLast = min(nStart + 1, infoPtr->nItemCount);
5213 bWrap = FALSE;
5214 goto again;
5217 return nNearestItem;
5220 /***
5221 * DESCRIPTION:
5222 * Searches for an item with specific characteristics.
5224 * PARAMETER(S):
5225 * [I] hwnd : window handle
5226 * [I] nStart : base item index
5227 * [I] lpFindInfo : item information to look for
5229 * RETURN:
5230 * SUCCESS : index of item
5231 * FAILURE : -1
5233 static INT LISTVIEW_FindItemA(const LISTVIEW_INFO *infoPtr, INT nStart,
5234 const LVFINDINFOA *lpFindInfo)
5236 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
5237 LVFINDINFOW fiw;
5238 INT res;
5239 LPWSTR strW = NULL;
5241 memcpy(&fiw, lpFindInfo, sizeof(fiw));
5242 if (hasText) fiw.psz = strW = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
5243 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
5244 textfreeT(strW, FALSE);
5245 return res;
5248 /***
5249 * DESCRIPTION:
5250 * Retrieves the background image of the listview control.
5252 * PARAMETER(S):
5253 * [I] infoPtr : valid pointer to the listview structure
5254 * [O] lpBkImage : background image attributes
5256 * RETURN:
5257 * SUCCESS : TRUE
5258 * FAILURE : FALSE
5260 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
5261 /* { */
5262 /* FIXME (listview, "empty stub!\n"); */
5263 /* return FALSE; */
5264 /* } */
5266 /***
5267 * DESCRIPTION:
5268 * Retrieves column attributes.
5270 * PARAMETER(S):
5271 * [I] infoPtr : valid pointer to the listview structure
5272 * [I] nColumn : column index
5273 * [IO] lpColumn : column information
5274 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5275 * otherwise it is in fact a LPLVCOLUMNA
5277 * RETURN:
5278 * SUCCESS : TRUE
5279 * FAILURE : FALSE
5281 static BOOL LISTVIEW_GetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
5283 COLUMN_INFO *lpColumnInfo;
5284 HDITEMW hdi;
5286 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5287 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
5289 /* initialize memory */
5290 ZeroMemory(&hdi, sizeof(hdi));
5292 if (lpColumn->mask & LVCF_TEXT)
5294 hdi.mask |= HDI_TEXT;
5295 hdi.pszText = lpColumn->pszText;
5296 hdi.cchTextMax = lpColumn->cchTextMax;
5299 if (lpColumn->mask & LVCF_IMAGE)
5300 hdi.mask |= HDI_IMAGE;
5302 if (lpColumn->mask & LVCF_ORDER)
5303 hdi.mask |= HDI_ORDER;
5305 if (lpColumn->mask & LVCF_SUBITEM)
5306 hdi.mask |= HDI_LPARAM;
5308 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
5310 if (lpColumn->mask & LVCF_FMT)
5311 lpColumn->fmt = lpColumnInfo->fmt;
5313 if (lpColumn->mask & LVCF_WIDTH)
5314 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
5316 if (lpColumn->mask & LVCF_IMAGE)
5317 lpColumn->iImage = hdi.iImage;
5319 if (lpColumn->mask & LVCF_ORDER)
5320 lpColumn->iOrder = hdi.iOrder;
5322 if (lpColumn->mask & LVCF_SUBITEM)
5323 lpColumn->iSubItem = hdi.lParam;
5325 return TRUE;
5329 static BOOL LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5331 INT i;
5333 if (!lpiArray)
5334 return FALSE;
5336 /* FIXME: little hack */
5337 for (i = 0; i < iCount; i++)
5338 lpiArray[i] = i;
5340 return TRUE;
5343 /***
5344 * DESCRIPTION:
5345 * Retrieves the column width.
5347 * PARAMETER(S):
5348 * [I] infoPtr : valid pointer to the listview structure
5349 * [I] int : column index
5351 * RETURN:
5352 * SUCCESS : column width
5353 * FAILURE : zero
5355 static INT LISTVIEW_GetColumnWidth(const LISTVIEW_INFO *infoPtr, INT nColumn)
5357 INT nColumnWidth = 0;
5358 HDITEMW hdItem;
5360 TRACE("nColumn=%d\n", nColumn);
5362 /* we have a 'column' in LIST and REPORT mode only */
5363 switch(infoPtr->dwStyle & LVS_TYPEMASK)
5365 case LVS_LIST:
5366 nColumnWidth = infoPtr->nItemWidth;
5367 break;
5368 case LVS_REPORT:
5369 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDM_ITEMCHANGED.
5370 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
5371 * HDM_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
5373 * TODO: should we do the same in LVM_GETCOLUMN?
5375 hdItem.mask = HDI_WIDTH;
5376 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdItem))
5378 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr->hwndSelf, nColumn);
5379 return 0;
5381 nColumnWidth = hdItem.cxy;
5382 break;
5385 TRACE("nColumnWidth=%d\n", nColumnWidth);
5386 return nColumnWidth;
5389 /***
5390 * DESCRIPTION:
5391 * In list or report display mode, retrieves the number of items that can fit
5392 * vertically in the visible area. In icon or small icon display mode,
5393 * retrieves the total number of visible items.
5395 * PARAMETER(S):
5396 * [I] infoPtr : valid pointer to the listview structure
5398 * RETURN:
5399 * Number of fully visible items.
5401 static INT LISTVIEW_GetCountPerPage(const LISTVIEW_INFO *infoPtr)
5403 switch (infoPtr->dwStyle & LVS_TYPEMASK)
5405 case LVS_ICON:
5406 case LVS_SMALLICON:
5407 return infoPtr->nItemCount;
5408 case LVS_REPORT:
5409 return LISTVIEW_GetCountPerColumn(infoPtr);
5410 case LVS_LIST:
5411 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5413 assert(FALSE);
5414 return 0;
5417 /***
5418 * DESCRIPTION:
5419 * Retrieves an image list handle.
5421 * PARAMETER(S):
5422 * [I] infoPtr : valid pointer to the listview structure
5423 * [I] nImageList : image list identifier
5425 * RETURN:
5426 * SUCCESS : image list handle
5427 * FAILURE : NULL
5429 static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImageList)
5431 switch (nImageList)
5433 case LVSIL_NORMAL: return infoPtr->himlNormal;
5434 case LVSIL_SMALL: return infoPtr->himlSmall;
5435 case LVSIL_STATE: return infoPtr->himlState;
5437 return NULL;
5440 /* LISTVIEW_GetISearchString */
5442 /***
5443 * DESCRIPTION:
5444 * Retrieves item attributes.
5446 * PARAMETER(S):
5447 * [I] hwnd : window handle
5448 * [IO] lpLVItem : item info
5449 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5450 * if FALSE, then lpLVItem is a LPLVITEMA.
5452 * NOTE:
5453 * This is the internal 'GetItem' interface -- it tries to
5454 * be smart and avoid text copies, if possible, by modifying
5455 * lpLVItem->pszText to point to the text string. Please note
5456 * that this is not always possible (e.g. OWNERDATA), so on
5457 * entry you *must* supply valid values for pszText, and cchTextMax.
5458 * The only difference to the documented interface is that upon
5459 * return, you should use *only* the lpLVItem->pszText, rather than
5460 * the buffer pointer you provided on input. Most code already does
5461 * that, so it's not a problem.
5462 * For the two cases when the text must be copied (that is,
5463 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5465 * RETURN:
5466 * SUCCESS : TRUE
5467 * FAILURE : FALSE
5469 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5471 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5472 NMLVDISPINFOW dispInfo;
5473 ITEM_INFO *lpItem;
5474 ITEMHDR* pItemHdr;
5475 HDPA hdpaSubItems;
5476 INT isubitem;
5478 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5480 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5481 return FALSE;
5483 if (lpLVItem->mask == 0) return TRUE;
5485 /* make a local copy */
5486 isubitem = lpLVItem->iSubItem;
5488 /* a quick optimization if all we're asked is the focus state
5489 * these queries are worth optimising since they are common,
5490 * and can be answered in constant time, without the heavy accesses */
5491 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5492 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5494 lpLVItem->state = 0;
5495 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5496 lpLVItem->state |= LVIS_FOCUSED;
5497 return TRUE;
5500 ZeroMemory(&dispInfo, sizeof(dispInfo));
5502 /* if the app stores all the data, handle it separately */
5503 if (infoPtr->dwStyle & LVS_OWNERDATA)
5505 dispInfo.item.state = 0;
5507 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5508 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5510 /* NOTE: copy only fields which we _know_ are initialized, some apps
5511 * depend on the uninitialized fields being 0 */
5512 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5513 dispInfo.item.iItem = lpLVItem->iItem;
5514 dispInfo.item.iSubItem = isubitem;
5515 if (lpLVItem->mask & LVIF_TEXT)
5517 dispInfo.item.pszText = lpLVItem->pszText;
5518 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5520 if (lpLVItem->mask & LVIF_STATE)
5521 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5522 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5523 dispInfo.item.stateMask = lpLVItem->stateMask;
5524 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5526 /* full size structure expected - _WIN32IE >= 0x560 */
5527 *lpLVItem = dispInfo.item;
5529 else if (lpLVItem->mask & LVIF_INDENT)
5531 /* indent member expected - _WIN32IE >= 0x300 */
5532 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5534 else
5536 /* minimal structure expected */
5537 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5539 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5542 /* make sure lParam is zeroed out */
5543 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5545 /* we store only a little state, so if we're not asked, we're done */
5546 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5548 /* if focus is handled by us, report it */
5549 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5551 lpLVItem->state &= ~LVIS_FOCUSED;
5552 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5553 lpLVItem->state |= LVIS_FOCUSED;
5556 /* and do the same for selection, if we handle it */
5557 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5559 lpLVItem->state &= ~LVIS_SELECTED;
5560 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5561 lpLVItem->state |= LVIS_SELECTED;
5564 return TRUE;
5567 /* find the item and subitem structures before we proceed */
5568 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5569 lpItem = DPA_GetPtr(hdpaSubItems, 0);
5570 assert (lpItem);
5572 if (isubitem)
5574 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5575 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5576 if (!lpSubItem)
5578 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5579 isubitem = 0;
5582 else
5583 pItemHdr = &lpItem->hdr;
5585 /* Do we need to query the state from the app? */
5586 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5588 dispInfo.item.mask |= LVIF_STATE;
5589 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5592 /* Do we need to enquire about the image? */
5593 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5594 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5596 dispInfo.item.mask |= LVIF_IMAGE;
5597 dispInfo.item.iImage = I_IMAGECALLBACK;
5600 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5601 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5603 dispInfo.item.mask |= LVIF_TEXT;
5604 dispInfo.item.pszText = lpLVItem->pszText;
5605 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5606 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5607 *dispInfo.item.pszText = '\0';
5610 /* If we don't have all the requested info, query the application */
5611 if (dispInfo.item.mask != 0)
5613 dispInfo.item.iItem = lpLVItem->iItem;
5614 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5615 dispInfo.item.lParam = lpItem->lParam;
5616 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5617 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5620 /* we should not store values for subitems */
5621 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5623 /* Now, handle the iImage field */
5624 if (dispInfo.item.mask & LVIF_IMAGE)
5626 lpLVItem->iImage = dispInfo.item.iImage;
5627 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5628 pItemHdr->iImage = dispInfo.item.iImage;
5630 else if (lpLVItem->mask & LVIF_IMAGE)
5632 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5633 lpLVItem->iImage = pItemHdr->iImage;
5634 else
5635 lpLVItem->iImage = 0;
5638 /* The pszText field */
5639 if (dispInfo.item.mask & LVIF_TEXT)
5641 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5642 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5644 lpLVItem->pszText = dispInfo.item.pszText;
5646 else if (lpLVItem->mask & LVIF_TEXT)
5648 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5649 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5652 /* Next is the lParam field */
5653 if (dispInfo.item.mask & LVIF_PARAM)
5655 lpLVItem->lParam = dispInfo.item.lParam;
5656 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5657 lpItem->lParam = dispInfo.item.lParam;
5659 else if (lpLVItem->mask & LVIF_PARAM)
5660 lpLVItem->lParam = lpItem->lParam;
5662 /* if this is a subitem, we're done */
5663 if (isubitem) return TRUE;
5665 /* ... the state field (this one is different due to uCallbackmask) */
5666 if (lpLVItem->mask & LVIF_STATE)
5668 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5669 if (dispInfo.item.mask & LVIF_STATE)
5671 lpLVItem->state &= ~dispInfo.item.stateMask;
5672 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5674 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5676 lpLVItem->state &= ~LVIS_FOCUSED;
5677 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5678 lpLVItem->state |= LVIS_FOCUSED;
5680 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5682 lpLVItem->state &= ~LVIS_SELECTED;
5683 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5684 lpLVItem->state |= LVIS_SELECTED;
5688 /* and last, but not least, the indent field */
5689 if (lpLVItem->mask & LVIF_INDENT)
5690 lpLVItem->iIndent = lpItem->iIndent;
5692 return TRUE;
5695 /***
5696 * DESCRIPTION:
5697 * Retrieves item attributes.
5699 * PARAMETER(S):
5700 * [I] hwnd : window handle
5701 * [IO] lpLVItem : item info
5702 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5703 * if FALSE, then lpLVItem is a LPLVITEMA.
5705 * NOTE:
5706 * This is the external 'GetItem' interface -- it properly copies
5707 * the text in the provided buffer.
5709 * RETURN:
5710 * SUCCESS : TRUE
5711 * FAILURE : FALSE
5713 static BOOL LISTVIEW_GetItemExtT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5715 LPWSTR pszText;
5716 BOOL bResult;
5718 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5719 return FALSE;
5721 pszText = lpLVItem->pszText;
5722 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5723 if (bResult && lpLVItem->pszText != pszText)
5724 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5725 lpLVItem->pszText = pszText;
5727 return bResult;
5731 /***
5732 * DESCRIPTION:
5733 * Retrieves the position (upper-left) of the listview control item.
5734 * Note that for LVS_ICON style, the upper-left is that of the icon
5735 * and not the bounding box.
5737 * PARAMETER(S):
5738 * [I] infoPtr : valid pointer to the listview structure
5739 * [I] nItem : item index
5740 * [O] lpptPosition : coordinate information
5742 * RETURN:
5743 * SUCCESS : TRUE
5744 * FAILURE : FALSE
5746 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5748 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5749 POINT Origin;
5751 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5753 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5755 LISTVIEW_GetOrigin(infoPtr, &Origin);
5756 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5758 if (uView == LVS_ICON)
5760 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5761 lpptPosition->y += ICON_TOP_PADDING;
5763 lpptPosition->x += Origin.x;
5764 lpptPosition->y += Origin.y;
5766 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
5767 return TRUE;
5771 /***
5772 * DESCRIPTION:
5773 * Retrieves the bounding rectangle for a listview control item.
5775 * PARAMETER(S):
5776 * [I] infoPtr : valid pointer to the listview structure
5777 * [I] nItem : item index
5778 * [IO] lprc : bounding rectangle coordinates
5779 * lprc->left specifies the portion of the item for which the bounding
5780 * rectangle will be retrieved.
5782 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5783 * including the icon and label.
5785 * * For LVS_ICON
5786 * * Experiment shows that native control returns:
5787 * * width = min (48, length of text line)
5788 * * .left = position.x - (width - iconsize.cx)/2
5789 * * .right = .left + width
5790 * * height = #lines of text * ntmHeight + icon height + 8
5791 * * .top = position.y - 2
5792 * * .bottom = .top + height
5793 * * separation between items .y = itemSpacing.cy - height
5794 * * .x = itemSpacing.cx - width
5795 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5797 * * For LVS_ICON
5798 * * Experiment shows that native control returns:
5799 * * width = iconSize.cx + 16
5800 * * .left = position.x - (width - iconsize.cx)/2
5801 * * .right = .left + width
5802 * * height = iconSize.cy + 4
5803 * * .top = position.y - 2
5804 * * .bottom = .top + height
5805 * * separation between items .y = itemSpacing.cy - height
5806 * * .x = itemSpacing.cx - width
5807 * LVIR_LABEL Returns the bounding rectangle of the item text.
5809 * * For LVS_ICON
5810 * * Experiment shows that native control returns:
5811 * * width = text length
5812 * * .left = position.x - width/2
5813 * * .right = .left + width
5814 * * height = ntmH * linecount + 2
5815 * * .top = position.y + iconSize.cy + 6
5816 * * .bottom = .top + height
5817 * * separation between items .y = itemSpacing.cy - height
5818 * * .x = itemSpacing.cx - width
5819 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5820 * rectangles, but excludes columns in report view.
5822 * RETURN:
5823 * SUCCESS : TRUE
5824 * FAILURE : FALSE
5826 * NOTES
5827 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5828 * upon whether the window has the focus currently and on whether the item
5829 * is the one with the focus. Ensure that the control's record of which
5830 * item has the focus agrees with the items' records.
5832 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5834 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5835 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5836 BOOL doLabel = TRUE, oversizedBox = FALSE;
5837 POINT Position, Origin;
5838 LVITEMW lvItem;
5840 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5842 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5844 LISTVIEW_GetOrigin(infoPtr, &Origin);
5845 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5847 /* Be smart and try to figure out the minimum we have to do */
5848 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5849 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5850 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5851 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5852 oversizedBox = TRUE;
5854 /* get what we need from the item before hand, so we make
5855 * only one request. This can speed up things, if data
5856 * is stored on the app side */
5857 lvItem.mask = 0;
5858 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5859 if (doLabel) lvItem.mask |= LVIF_TEXT;
5860 lvItem.iItem = nItem;
5861 lvItem.iSubItem = 0;
5862 lvItem.pszText = szDispText;
5863 lvItem.cchTextMax = DISP_TEXT_SIZE;
5864 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5865 /* we got the state already up, simulate it here, to avoid a reget */
5866 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5868 lvItem.mask |= LVIF_STATE;
5869 lvItem.stateMask = LVIS_FOCUSED;
5870 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5873 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5874 lprc->left = LVIR_BOUNDS;
5875 switch(lprc->left)
5877 case LVIR_ICON:
5878 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
5879 break;
5881 case LVIR_LABEL:
5882 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc);
5883 break;
5885 case LVIR_BOUNDS:
5886 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
5887 break;
5889 case LVIR_SELECTBOUNDS:
5890 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL);
5891 break;
5893 default:
5894 WARN("Unknown value: %d\n", lprc->left);
5895 return FALSE;
5898 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5900 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
5902 return TRUE;
5905 /***
5906 * DESCRIPTION:
5907 * Retrieves the spacing between listview control items.
5909 * PARAMETER(S):
5910 * [I] infoPtr : valid pointer to the listview structure
5911 * [IO] lprc : rectangle to receive the output
5912 * on input, lprc->top = nSubItem
5913 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5915 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5916 * not only those of the first column.
5917 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5919 * RETURN:
5920 * TRUE: success
5921 * FALSE: failure
5923 static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5925 POINT Position;
5926 LVITEMW lvItem;
5927 INT nColumn;
5929 if (!lprc) return FALSE;
5931 nColumn = lprc->top;
5933 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
5934 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5935 if (lprc->top == 0)
5936 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5938 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5940 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5942 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5944 lvItem.mask = 0;
5945 lvItem.iItem = nItem;
5946 lvItem.iSubItem = nColumn;
5948 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5949 switch(lprc->left)
5951 case LVIR_ICON:
5952 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
5953 break;
5955 case LVIR_LABEL:
5956 case LVIR_BOUNDS:
5957 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
5958 break;
5960 default:
5961 ERR("Unknown bounds=%d\n", lprc->left);
5962 return FALSE;
5965 OffsetRect(lprc, Position.x, Position.y);
5966 return TRUE;
5970 /***
5971 * DESCRIPTION:
5972 * Retrieves the width of a label.
5974 * PARAMETER(S):
5975 * [I] infoPtr : valid pointer to the listview structure
5977 * RETURN:
5978 * SUCCESS : string width (in pixels)
5979 * FAILURE : zero
5981 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *infoPtr, INT nItem)
5983 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5984 LVITEMW lvItem;
5986 TRACE("(nItem=%d)\n", nItem);
5988 lvItem.mask = LVIF_TEXT;
5989 lvItem.iItem = nItem;
5990 lvItem.iSubItem = 0;
5991 lvItem.pszText = szDispText;
5992 lvItem.cchTextMax = DISP_TEXT_SIZE;
5993 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5995 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5998 /***
5999 * DESCRIPTION:
6000 * Retrieves the spacing between listview control items.
6002 * PARAMETER(S):
6003 * [I] infoPtr : valid pointer to the listview structure
6004 * [I] bSmall : flag for small or large icon
6006 * RETURN:
6007 * Horizontal + vertical spacing
6009 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall)
6011 LONG lResult;
6013 if (!bSmall)
6015 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6017 else
6019 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
6020 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6021 else
6022 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6024 return lResult;
6027 /***
6028 * DESCRIPTION:
6029 * Retrieves the state of a listview control item.
6031 * PARAMETER(S):
6032 * [I] infoPtr : valid pointer to the listview structure
6033 * [I] nItem : item index
6034 * [I] uMask : state mask
6036 * RETURN:
6037 * State specified by the mask.
6039 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
6041 LVITEMW lvItem;
6043 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6045 lvItem.iItem = nItem;
6046 lvItem.iSubItem = 0;
6047 lvItem.mask = LVIF_STATE;
6048 lvItem.stateMask = uMask;
6049 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6051 return lvItem.state & uMask;
6054 /***
6055 * DESCRIPTION:
6056 * Retrieves the text of a listview control item or subitem.
6058 * PARAMETER(S):
6059 * [I] hwnd : window handle
6060 * [I] nItem : item index
6061 * [IO] lpLVItem : item information
6062 * [I] isW : TRUE if lpLVItem is Unicode
6064 * RETURN:
6065 * SUCCESS : string length
6066 * FAILURE : 0
6068 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6070 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6072 lpLVItem->mask = LVIF_TEXT;
6073 lpLVItem->iItem = nItem;
6074 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
6076 return textlenT(lpLVItem->pszText, isW);
6079 /***
6080 * DESCRIPTION:
6081 * Searches for an item based on properties + relationships.
6083 * PARAMETER(S):
6084 * [I] infoPtr : valid pointer to the listview structure
6085 * [I] nItem : item index
6086 * [I] uFlags : relationship flag
6088 * RETURN:
6089 * SUCCESS : item index
6090 * FAILURE : -1
6092 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
6094 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6095 UINT uMask = 0;
6096 LVFINDINFOW lvFindInfo;
6097 INT nCountPerColumn;
6098 INT nCountPerRow;
6099 INT i;
6101 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
6102 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
6104 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6106 if (uFlags & LVNI_CUT)
6107 uMask |= LVIS_CUT;
6109 if (uFlags & LVNI_DROPHILITED)
6110 uMask |= LVIS_DROPHILITED;
6112 if (uFlags & LVNI_FOCUSED)
6113 uMask |= LVIS_FOCUSED;
6115 if (uFlags & LVNI_SELECTED)
6116 uMask |= LVIS_SELECTED;
6118 /* if we're asked for the focused item, that's only one,
6119 * so it's worth optimizing */
6120 if (uFlags & LVNI_FOCUSED)
6122 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
6123 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
6126 if (uFlags & LVNI_ABOVE)
6128 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6130 while (nItem >= 0)
6132 nItem--;
6133 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6134 return nItem;
6137 else
6139 /* Special case for autoarrange - move 'til the top of a list */
6140 if (is_autoarrange(infoPtr))
6142 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6143 while (nItem - nCountPerRow >= 0)
6145 nItem -= nCountPerRow;
6146 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6147 return nItem;
6149 return -1;
6151 lvFindInfo.flags = LVFI_NEARESTXY;
6152 lvFindInfo.vkDirection = VK_UP;
6153 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6154 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6156 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6157 return nItem;
6161 else if (uFlags & LVNI_BELOW)
6163 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6165 while (nItem < infoPtr->nItemCount)
6167 nItem++;
6168 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6169 return nItem;
6172 else
6174 /* Special case for autoarrange - move 'til the bottom of a list */
6175 if (is_autoarrange(infoPtr))
6177 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6178 while (nItem + nCountPerRow < infoPtr->nItemCount )
6180 nItem += nCountPerRow;
6181 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6182 return nItem;
6184 return -1;
6186 lvFindInfo.flags = LVFI_NEARESTXY;
6187 lvFindInfo.vkDirection = VK_DOWN;
6188 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6189 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6191 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6192 return nItem;
6196 else if (uFlags & LVNI_TOLEFT)
6198 if (uView == LVS_LIST)
6200 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6201 while (nItem - nCountPerColumn >= 0)
6203 nItem -= nCountPerColumn;
6204 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6205 return nItem;
6208 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6210 /* Special case for autoarrange - move 'ti the beginning of a row */
6211 if (is_autoarrange(infoPtr))
6213 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6214 while (nItem % nCountPerRow > 0)
6216 nItem --;
6217 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6218 return nItem;
6220 return -1;
6222 lvFindInfo.flags = LVFI_NEARESTXY;
6223 lvFindInfo.vkDirection = VK_LEFT;
6224 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6225 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6227 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6228 return nItem;
6232 else if (uFlags & LVNI_TORIGHT)
6234 if (uView == LVS_LIST)
6236 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6237 while (nItem + nCountPerColumn < infoPtr->nItemCount)
6239 nItem += nCountPerColumn;
6240 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6241 return nItem;
6244 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6246 /* Special case for autoarrange - move 'til the end of a row */
6247 if (is_autoarrange(infoPtr))
6249 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6250 while (nItem % nCountPerRow < nCountPerRow - 1 )
6252 nItem ++;
6253 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6254 return nItem;
6256 return -1;
6258 lvFindInfo.flags = LVFI_NEARESTXY;
6259 lvFindInfo.vkDirection = VK_RIGHT;
6260 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6261 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6263 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6264 return nItem;
6268 else
6270 nItem++;
6272 /* search by index */
6273 for (i = nItem; i < infoPtr->nItemCount; i++)
6275 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
6276 return i;
6280 return -1;
6283 /* LISTVIEW_GetNumberOfWorkAreas */
6285 /***
6286 * DESCRIPTION:
6287 * Retrieves the origin coordinates when in icon or small icon display mode.
6289 * PARAMETER(S):
6290 * [I] infoPtr : valid pointer to the listview structure
6291 * [O] lpptOrigin : coordinate information
6293 * RETURN:
6294 * None.
6296 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
6298 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6299 INT nHorzPos = 0, nVertPos = 0;
6300 SCROLLINFO scrollInfo;
6302 scrollInfo.cbSize = sizeof(SCROLLINFO);
6303 scrollInfo.fMask = SIF_POS;
6305 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6306 nHorzPos = scrollInfo.nPos;
6307 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6308 nVertPos = scrollInfo.nPos;
6310 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6312 lpptOrigin->x = infoPtr->rcList.left;
6313 lpptOrigin->y = infoPtr->rcList.top;
6314 if (uView == LVS_LIST)
6315 nHorzPos *= infoPtr->nItemWidth;
6316 else if (uView == LVS_REPORT)
6317 nVertPos *= infoPtr->nItemHeight;
6319 lpptOrigin->x -= nHorzPos;
6320 lpptOrigin->y -= nVertPos;
6322 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
6325 /***
6326 * DESCRIPTION:
6327 * Retrieves the width of a string.
6329 * PARAMETER(S):
6330 * [I] hwnd : window handle
6331 * [I] lpszText : text string to process
6332 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6334 * RETURN:
6335 * SUCCESS : string width (in pixels)
6336 * FAILURE : zero
6338 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6340 SIZE stringSize;
6342 stringSize.cx = 0;
6343 if (is_textT(lpszText, isW))
6345 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6346 HDC hdc = GetDC(infoPtr->hwndSelf);
6347 HFONT hOldFont = SelectObject(hdc, hFont);
6349 if (isW)
6350 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6351 else
6352 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6353 SelectObject(hdc, hOldFont);
6354 ReleaseDC(infoPtr->hwndSelf, hdc);
6356 return stringSize.cx;
6359 /***
6360 * DESCRIPTION:
6361 * Determines which listview item is located at the specified position.
6363 * PARAMETER(S):
6364 * [I] infoPtr : valid pointer to the listview structure
6365 * [IO] lpht : hit test information
6366 * [I] subitem : fill out iSubItem.
6367 * [I] select : return the index only if the hit selects the item
6369 * NOTE:
6370 * (mm 20001022): We must not allow iSubItem to be touched, for
6371 * an app might pass only a structure with space up to iItem!
6372 * (MS Office 97 does that for instance in the file open dialog)
6374 * RETURN:
6375 * SUCCESS : item index
6376 * FAILURE : -1
6378 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6380 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6381 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6382 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6383 POINT Origin, Position, opt;
6384 LVITEMW lvItem;
6385 ITERATOR i;
6386 INT iItem;
6388 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6390 lpht->flags = 0;
6391 lpht->iItem = -1;
6392 if (subitem) lpht->iSubItem = 0;
6394 if (infoPtr->rcList.left > lpht->pt.x)
6395 lpht->flags |= LVHT_TOLEFT;
6396 else if (infoPtr->rcList.right < lpht->pt.x)
6397 lpht->flags |= LVHT_TORIGHT;
6399 if (infoPtr->rcList.top > lpht->pt.y)
6400 lpht->flags |= LVHT_ABOVE;
6401 else if (infoPtr->rcList.bottom < lpht->pt.y)
6402 lpht->flags |= LVHT_BELOW;
6404 TRACE("lpht->flags=0x%x\n", lpht->flags);
6405 if (lpht->flags) return -1;
6407 lpht->flags |= LVHT_NOWHERE;
6409 LISTVIEW_GetOrigin(infoPtr, &Origin);
6411 /* first deal with the large items */
6412 rcSearch.left = lpht->pt.x;
6413 rcSearch.top = lpht->pt.y;
6414 rcSearch.right = rcSearch.left + 1;
6415 rcSearch.bottom = rcSearch.top + 1;
6417 iterator_frameditems(&i, infoPtr, &rcSearch);
6418 iterator_next(&i); /* go to first item in the sequence */
6419 iItem = i.nItem;
6420 iterator_destroy(&i);
6422 TRACE("lpht->iItem=%d\n", iItem);
6423 if (iItem == -1) return -1;
6425 lvItem.mask = LVIF_STATE | LVIF_TEXT;
6426 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6427 lvItem.stateMask = LVIS_STATEIMAGEMASK;
6428 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6429 lvItem.iItem = iItem;
6430 lvItem.iSubItem = 0;
6431 lvItem.pszText = szDispText;
6432 lvItem.cchTextMax = DISP_TEXT_SIZE;
6433 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6434 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6436 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
6437 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6438 opt.x = lpht->pt.x - Position.x - Origin.x;
6439 opt.y = lpht->pt.y - Position.y - Origin.y;
6441 if (uView == LVS_REPORT)
6442 rcBounds = rcBox;
6443 else
6445 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6446 UnionRect(&rcBounds, &rcBounds, &rcState);
6448 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6449 if (!PtInRect(&rcBounds, opt)) return -1;
6451 if (PtInRect(&rcIcon, opt))
6452 lpht->flags |= LVHT_ONITEMICON;
6453 else if (PtInRect(&rcLabel, opt))
6454 lpht->flags |= LVHT_ONITEMLABEL;
6455 else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6456 lpht->flags |= LVHT_ONITEMSTATEICON;
6457 if (lpht->flags & LVHT_ONITEM)
6458 lpht->flags &= ~LVHT_NOWHERE;
6460 TRACE("lpht->flags=0x%x\n", lpht->flags);
6461 if (uView == LVS_REPORT && subitem)
6463 INT j;
6465 rcBounds.right = rcBounds.left;
6466 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6468 rcBounds.left = rcBounds.right;
6469 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6470 if (PtInRect(&rcBounds, opt))
6472 lpht->iSubItem = j;
6473 break;
6478 if (select && !(uView == LVS_REPORT &&
6479 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6480 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6482 if (uView == LVS_REPORT)
6484 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6485 UnionRect(&rcBounds, &rcBounds, &rcState);
6487 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6489 return lpht->iItem = iItem;
6493 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6494 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6495 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6496 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6497 their own sort proc. when sending LVM_SORTITEMS.
6499 /* Platform SDK:
6500 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6502 LVS_SORTXXX must be specified,
6503 LVS_OWNERDRAW is not set,
6504 <item>.pszText is not LPSTR_TEXTCALLBACK.
6506 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6507 are sorted based on item text..."
6509 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6511 ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
6512 ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
6513 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6515 /* if we're sorting descending, negate the return value */
6516 return (((const LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6519 /***
6520 * DESCRIPTION:
6521 * Inserts a new item in the listview control.
6523 * PARAMETER(S):
6524 * [I] infoPtr : valid pointer to the listview structure
6525 * [I] lpLVItem : item information
6526 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6528 * RETURN:
6529 * SUCCESS : new item index
6530 * FAILURE : -1
6532 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6534 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6535 INT nItem;
6536 HDPA hdpaSubItems;
6537 NMLISTVIEW nmlv;
6538 ITEM_INFO *lpItem;
6539 BOOL is_sorted, has_changed;
6540 LVITEMW item;
6541 HWND hwndSelf = infoPtr->hwndSelf;
6543 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6545 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6547 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6548 if (!lpLVItem || lpLVItem->iSubItem) return -1;
6550 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6552 if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
6554 /* insert item in listview control data structure */
6555 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6556 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6558 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6559 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6561 if (lpLVItem->iItem < 0 && !is_sorted) return -1;
6563 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6564 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6565 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6566 if (nItem == -1) goto fail;
6567 infoPtr->nItemCount++;
6569 /* shift indices first so they don't get tangled */
6570 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6572 /* set the item attributes */
6573 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6575 /* full size structure expected - _WIN32IE >= 0x560 */
6576 item = *lpLVItem;
6578 else if (lpLVItem->mask & LVIF_INDENT)
6580 /* indent member expected - _WIN32IE >= 0x300 */
6581 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6583 else
6585 /* minimal structure expected */
6586 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6588 item.iItem = nItem;
6589 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6591 item.mask |= LVIF_STATE;
6592 item.stateMask |= LVIS_STATEIMAGEMASK;
6593 item.state &= ~LVIS_STATEIMAGEMASK;
6594 item.state |= INDEXTOSTATEIMAGEMASK(1);
6596 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6598 /* if we're sorted, sort the list, and update the index */
6599 if (is_sorted)
6601 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6602 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6603 assert(nItem != -1);
6606 /* make room for the position, if we are in the right mode */
6607 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6609 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6610 goto undo;
6611 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6613 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6614 goto undo;
6618 /* send LVN_INSERTITEM notification */
6619 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6620 nmlv.iItem = nItem;
6621 nmlv.lParam = lpItem->lParam;
6622 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6623 if (!IsWindow(hwndSelf))
6624 return -1;
6626 /* align items (set position of each item) */
6627 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6629 POINT pt;
6631 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6632 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6633 else
6634 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6636 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6639 /* now is the invalidation fun */
6640 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6641 return nItem;
6643 undo:
6644 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6645 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6646 infoPtr->nItemCount--;
6647 fail:
6648 DPA_DeletePtr(hdpaSubItems, 0);
6649 DPA_Destroy (hdpaSubItems);
6650 Free (lpItem);
6651 return -1;
6654 /***
6655 * DESCRIPTION:
6656 * Redraws a range of items.
6658 * PARAMETER(S):
6659 * [I] infoPtr : valid pointer to the listview structure
6660 * [I] nFirst : first item
6661 * [I] nLast : last item
6663 * RETURN:
6664 * SUCCESS : TRUE
6665 * FAILURE : FALSE
6667 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6669 INT i;
6671 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6672 max(nFirst, nLast) >= infoPtr->nItemCount)
6673 return FALSE;
6675 for (i = nFirst; i <= nLast; i++)
6676 LISTVIEW_InvalidateItem(infoPtr, i);
6678 return TRUE;
6681 /***
6682 * DESCRIPTION:
6683 * Scroll the content of a listview.
6685 * PARAMETER(S):
6686 * [I] infoPtr : valid pointer to the listview structure
6687 * [I] dx : horizontal scroll amount in pixels
6688 * [I] dy : vertical scroll amount in pixels
6690 * RETURN:
6691 * SUCCESS : TRUE
6692 * FAILURE : FALSE
6694 * COMMENTS:
6695 * If the control is in report mode (LVS_REPORT) the control can
6696 * be scrolled only in line increments. "dy" will be rounded to the
6697 * nearest number of pixels that are a whole line. Ex: if line height
6698 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6699 * is passed, then the scroll will be 0. (per MSDN 7/2002)
6701 * For: (per experimentation with native control and CSpy ListView)
6702 * LVS_ICON dy=1 = 1 pixel (vertical only)
6703 * dx ignored
6704 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6705 * dx ignored
6706 * LVS_LIST dx=1 = 1 column (horizontal only)
6707 * but will only scroll 1 column per message
6708 * no matter what the value.
6709 * dy must be 0 or FALSE returned.
6710 * LVS_REPORT dx=1 = 1 pixel
6711 * dy= see above
6714 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6716 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6717 case LVS_REPORT:
6718 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6719 dy /= infoPtr->nItemHeight;
6720 break;
6721 case LVS_LIST:
6722 if (dy != 0) return FALSE;
6723 break;
6724 default: /* icon */
6725 dx = 0;
6726 break;
6729 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6730 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6732 return TRUE;
6735 /***
6736 * DESCRIPTION:
6737 * Sets the background color.
6739 * PARAMETER(S):
6740 * [I] infoPtr : valid pointer to the listview structure
6741 * [I] clrBk : background color
6743 * RETURN:
6744 * SUCCESS : TRUE
6745 * FAILURE : FALSE
6747 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6749 TRACE("(clrBk=%x)\n", clrBk);
6751 if(infoPtr->clrBk != clrBk) {
6752 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6753 infoPtr->clrBk = clrBk;
6754 if (clrBk == CLR_NONE)
6755 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6756 else
6757 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6758 LISTVIEW_InvalidateList(infoPtr);
6761 return TRUE;
6764 /* LISTVIEW_SetBkImage */
6766 /*** Helper for {Insert,Set}ColumnT *only* */
6767 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
6768 const LVCOLUMNW *lpColumn, BOOL isW)
6770 if (lpColumn->mask & LVCF_FMT)
6772 /* format member is valid */
6773 lphdi->mask |= HDI_FORMAT;
6775 /* set text alignment (leftmost column must be left-aligned) */
6776 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6777 lphdi->fmt |= HDF_LEFT;
6778 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6779 lphdi->fmt |= HDF_RIGHT;
6780 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6781 lphdi->fmt |= HDF_CENTER;
6783 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6784 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6786 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6788 lphdi->fmt |= HDF_IMAGE;
6789 lphdi->iImage = I_IMAGECALLBACK;
6793 if (lpColumn->mask & LVCF_WIDTH)
6795 lphdi->mask |= HDI_WIDTH;
6796 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6798 /* make it fill the remainder of the controls width */
6799 RECT rcHeader;
6800 INT item_index;
6802 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6804 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6805 lphdi->cxy += rcHeader.right - rcHeader.left;
6808 /* retrieve the layout of the header */
6809 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6810 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
6812 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6814 else
6815 lphdi->cxy = lpColumn->cx;
6818 if (lpColumn->mask & LVCF_TEXT)
6820 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6821 lphdi->fmt |= HDF_STRING;
6822 lphdi->pszText = lpColumn->pszText;
6823 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6826 if (lpColumn->mask & LVCF_IMAGE)
6828 lphdi->mask |= HDI_IMAGE;
6829 lphdi->iImage = lpColumn->iImage;
6832 if (lpColumn->mask & LVCF_ORDER)
6834 lphdi->mask |= HDI_ORDER;
6835 lphdi->iOrder = lpColumn->iOrder;
6840 /***
6841 * DESCRIPTION:
6842 * Inserts a new column.
6844 * PARAMETER(S):
6845 * [I] infoPtr : valid pointer to the listview structure
6846 * [I] nColumn : column index
6847 * [I] lpColumn : column information
6848 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6850 * RETURN:
6851 * SUCCESS : new column index
6852 * FAILURE : -1
6854 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6855 const LVCOLUMNW *lpColumn, BOOL isW)
6857 COLUMN_INFO *lpColumnInfo;
6858 INT nNewColumn;
6859 HDITEMW hdi;
6861 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6863 if (!lpColumn || nColumn < 0) return -1;
6864 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6866 ZeroMemory(&hdi, sizeof(HDITEMW));
6867 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6870 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
6871 * (can be seen in SPY) otherwise column never gets added.
6873 if (!(lpColumn->mask & LVCF_WIDTH)) {
6874 hdi.mask |= HDI_WIDTH;
6875 hdi.cxy = 10;
6879 * when the iSubItem is available Windows copies it to the header lParam. It seems
6880 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
6882 if (lpColumn->mask & LVCF_SUBITEM)
6884 hdi.mask |= HDI_LPARAM;
6885 hdi.lParam = lpColumn->iSubItem;
6888 /* insert item in header control */
6889 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6890 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6891 (WPARAM)nColumn, (LPARAM)&hdi);
6892 if (nNewColumn == -1) return -1;
6893 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6895 /* create our own column info */
6896 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6897 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6899 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6900 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6902 /* now we have to actually adjust the data */
6903 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6905 SUBITEM_INFO *lpSubItem;
6906 HDPA hdpaSubItems;
6907 INT nItem, i;
6909 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6911 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
6912 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6914 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
6915 if (lpSubItem->iSubItem >= nNewColumn)
6916 lpSubItem->iSubItem++;
6921 /* make space for the new column */
6922 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6923 LISTVIEW_UpdateItemSize(infoPtr);
6925 return nNewColumn;
6927 fail:
6928 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6929 if (lpColumnInfo)
6931 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6932 Free(lpColumnInfo);
6934 return -1;
6937 /***
6938 * DESCRIPTION:
6939 * Sets the attributes of a header item.
6941 * PARAMETER(S):
6942 * [I] infoPtr : valid pointer to the listview structure
6943 * [I] nColumn : column index
6944 * [I] lpColumn : column attributes
6945 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6947 * RETURN:
6948 * SUCCESS : TRUE
6949 * FAILURE : FALSE
6951 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
6952 const LVCOLUMNW *lpColumn, BOOL isW)
6954 HDITEMW hdi, hdiget;
6955 BOOL bResult;
6957 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6959 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6961 ZeroMemory(&hdi, sizeof(HDITEMW));
6962 if (lpColumn->mask & LVCF_FMT)
6964 hdi.mask |= HDI_FORMAT;
6965 hdiget.mask = HDI_FORMAT;
6966 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6967 hdi.fmt = hdiget.fmt & HDF_STRING;
6969 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6971 /* set header item attributes */
6972 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6973 if (!bResult) return FALSE;
6975 if (lpColumn->mask & LVCF_FMT)
6977 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6978 int oldFmt = lpColumnInfo->fmt;
6980 lpColumnInfo->fmt = lpColumn->fmt;
6981 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6983 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6984 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6988 return TRUE;
6991 /***
6992 * DESCRIPTION:
6993 * Sets the column order array
6995 * PARAMETERS:
6996 * [I] infoPtr : valid pointer to the listview structure
6997 * [I] iCount : number of elements in column order array
6998 * [I] lpiArray : pointer to column order array
7000 * RETURN:
7001 * SUCCESS : TRUE
7002 * FAILURE : FALSE
7004 static BOOL LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
7006 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7008 if (!lpiArray)
7009 return FALSE;
7011 return TRUE;
7015 /***
7016 * DESCRIPTION:
7017 * Sets the width of a column
7019 * PARAMETERS:
7020 * [I] infoPtr : valid pointer to the listview structure
7021 * [I] nColumn : column index
7022 * [I] cx : column width
7024 * RETURN:
7025 * SUCCESS : TRUE
7026 * FAILURE : FALSE
7028 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
7030 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7031 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
7032 INT max_cx = 0;
7033 HDITEMW hdi;
7035 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
7037 /* set column width only if in report or list mode */
7038 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
7040 /* take care of invalid cx values */
7041 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
7042 else if (uView == LVS_LIST && cx < 1) return FALSE;
7044 /* resize all columns if in LVS_LIST mode */
7045 if(uView == LVS_LIST)
7047 infoPtr->nItemWidth = cx;
7048 LISTVIEW_InvalidateList(infoPtr);
7049 return TRUE;
7052 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7054 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
7056 INT nLabelWidth;
7057 LVITEMW lvItem;
7059 lvItem.mask = LVIF_TEXT;
7060 lvItem.iItem = 0;
7061 lvItem.iSubItem = nColumn;
7062 lvItem.pszText = szDispText;
7063 lvItem.cchTextMax = DISP_TEXT_SIZE;
7064 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7066 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
7067 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
7068 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
7070 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
7071 max_cx += infoPtr->iconSize.cx;
7072 max_cx += TRAILING_LABEL_PADDING;
7075 /* autosize based on listview items width */
7076 if(cx == LVSCW_AUTOSIZE)
7077 cx = max_cx;
7078 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7080 /* if iCol is the last column make it fill the remainder of the controls width */
7081 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
7083 RECT rcHeader;
7084 POINT Origin;
7086 LISTVIEW_GetOrigin(infoPtr, &Origin);
7087 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
7089 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
7091 else
7093 /* Despite what the MS docs say, if this is not the last
7094 column, then MS resizes the column to the width of the
7095 largest text string in the column, including headers
7096 and items. This is different from LVSCW_AUTOSIZE in that
7097 LVSCW_AUTOSIZE ignores the header string length. */
7098 cx = 0;
7100 /* retrieve header text */
7101 hdi.mask = HDI_TEXT;
7102 hdi.cchTextMax = DISP_TEXT_SIZE;
7103 hdi.pszText = szDispText;
7104 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
7106 HDC hdc = GetDC(infoPtr->hwndSelf);
7107 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
7108 SIZE size;
7110 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
7111 cx = size.cx + TRAILING_HEADER_PADDING;
7112 /* FIXME: Take into account the header image, if one is present */
7113 SelectObject(hdc, old_font);
7114 ReleaseDC(infoPtr->hwndSelf, hdc);
7116 cx = max (cx, max_cx);
7120 if (cx < 0) return FALSE;
7122 /* call header to update the column change */
7123 hdi.mask = HDI_WIDTH;
7124 hdi.cxy = cx;
7125 TRACE("hdi.cxy=%d\n", hdi.cxy);
7126 return Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7129 /***
7130 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
7133 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
7135 HDC hdc_wnd, hdc;
7136 HBITMAP hbm_im, hbm_mask, hbm_orig;
7137 RECT rc;
7138 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
7139 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
7140 HIMAGELIST himl;
7142 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
7143 ILC_COLOR | ILC_MASK, 2, 2);
7144 hdc_wnd = GetDC(infoPtr->hwndSelf);
7145 hdc = CreateCompatibleDC(hdc_wnd);
7146 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
7147 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
7148 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
7150 rc.left = rc.top = 0;
7151 rc.right = GetSystemMetrics(SM_CXSMICON);
7152 rc.bottom = GetSystemMetrics(SM_CYSMICON);
7154 hbm_orig = SelectObject(hdc, hbm_mask);
7155 FillRect(hdc, &rc, hbr_white);
7156 InflateRect(&rc, -3, -3);
7157 FillRect(hdc, &rc, hbr_black);
7159 SelectObject(hdc, hbm_im);
7160 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
7161 SelectObject(hdc, hbm_orig);
7162 ImageList_Add(himl, hbm_im, hbm_mask);
7164 SelectObject(hdc, hbm_im);
7165 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
7166 SelectObject(hdc, hbm_orig);
7167 ImageList_Add(himl, hbm_im, hbm_mask);
7169 DeleteObject(hbm_mask);
7170 DeleteObject(hbm_im);
7171 DeleteDC(hdc);
7173 return himl;
7176 /***
7177 * DESCRIPTION:
7178 * Sets the extended listview style.
7180 * PARAMETERS:
7181 * [I] infoPtr : valid pointer to the listview structure
7182 * [I] dwMask : mask
7183 * [I] dwStyle : style
7185 * RETURN:
7186 * SUCCESS : previous style
7187 * FAILURE : 0
7189 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwExStyle)
7191 DWORD dwOldExStyle = infoPtr->dwLvExStyle;
7193 /* set new style */
7194 if (dwMask)
7195 infoPtr->dwLvExStyle = (dwOldExStyle & ~dwMask) | (dwExStyle & dwMask);
7196 else
7197 infoPtr->dwLvExStyle = dwExStyle;
7199 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_CHECKBOXES)
7201 HIMAGELIST himl = 0;
7202 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7204 LVITEMW item;
7205 item.mask = LVIF_STATE;
7206 item.stateMask = LVIS_STATEIMAGEMASK;
7207 item.state = INDEXTOSTATEIMAGEMASK(1);
7208 LISTVIEW_SetItemState(infoPtr, -1, &item);
7210 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
7212 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
7215 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_HEADERDRAGDROP)
7217 DWORD dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
7218 if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
7219 dwStyle |= HDS_DRAGDROP;
7220 else
7221 dwStyle &= ~HDS_DRAGDROP;
7222 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
7225 /* GRIDLINES adds decoration at top so changes sizes */
7226 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_GRIDLINES)
7228 LISTVIEW_UpdateSize(infoPtr);
7232 LISTVIEW_InvalidateList(infoPtr);
7233 return dwOldExStyle;
7236 /***
7237 * DESCRIPTION:
7238 * Sets the new hot cursor used during hot tracking and hover selection.
7240 * PARAMETER(S):
7241 * [I] infoPtr : valid pointer to the listview structure
7242 * [I] hCursor : the new hot cursor handle
7244 * RETURN:
7245 * Returns the previous hot cursor
7247 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
7249 HCURSOR oldCursor = infoPtr->hHotCursor;
7251 infoPtr->hHotCursor = hCursor;
7253 return oldCursor;
7257 /***
7258 * DESCRIPTION:
7259 * Sets the hot item index.
7261 * PARAMETERS:
7262 * [I] infoPtr : valid pointer to the listview structure
7263 * [I] iIndex : index
7265 * RETURN:
7266 * SUCCESS : previous hot item index
7267 * FAILURE : -1 (no hot item)
7269 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
7271 INT iOldIndex = infoPtr->nHotItem;
7273 infoPtr->nHotItem = iIndex;
7275 return iOldIndex;
7279 /***
7280 * DESCRIPTION:
7281 * Sets the amount of time the cursor must hover over an item before it is selected.
7283 * PARAMETER(S):
7284 * [I] infoPtr : valid pointer to the listview structure
7285 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7287 * RETURN:
7288 * Returns the previous hover time
7290 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
7292 DWORD oldHoverTime = infoPtr->dwHoverTime;
7294 infoPtr->dwHoverTime = dwHoverTime;
7296 return oldHoverTime;
7299 /***
7300 * DESCRIPTION:
7301 * Sets spacing for icons of LVS_ICON style.
7303 * PARAMETER(S):
7304 * [I] infoPtr : valid pointer to the listview structure
7305 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7306 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7308 * RETURN:
7309 * MAKELONG(oldcx, oldcy)
7311 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
7313 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7314 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7316 TRACE("requested=(%d,%d)\n", cx, cy);
7318 /* this is supported only for LVS_ICON style */
7319 if (uView != LVS_ICON) return oldspacing;
7321 /* set to defaults, if instructed to */
7322 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
7323 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
7325 /* if 0 then compute width
7326 * FIXME: Should scan each item and determine max width of
7327 * icon or label, then make that the width */
7328 if (cx == 0)
7329 cx = infoPtr->iconSpacing.cx;
7331 /* if 0 then compute height */
7332 if (cy == 0)
7333 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
7334 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
7337 infoPtr->iconSpacing.cx = cx;
7338 infoPtr->iconSpacing.cy = cy;
7340 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7341 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
7342 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7343 infoPtr->ntmHeight);
7345 /* these depend on the iconSpacing */
7346 LISTVIEW_UpdateItemSize(infoPtr);
7348 return oldspacing;
7351 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
7353 INT cx, cy;
7355 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
7357 size->cx = cx;
7358 size->cy = cy;
7360 else
7362 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
7363 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7367 /***
7368 * DESCRIPTION:
7369 * Sets image lists.
7371 * PARAMETER(S):
7372 * [I] infoPtr : valid pointer to the listview structure
7373 * [I] nType : image list type
7374 * [I] himl : image list handle
7376 * RETURN:
7377 * SUCCESS : old image list
7378 * FAILURE : NULL
7380 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7382 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7383 INT oldHeight = infoPtr->nItemHeight;
7384 HIMAGELIST himlOld = 0;
7386 TRACE("(nType=%d, himl=%p\n", nType, himl);
7388 switch (nType)
7390 case LVSIL_NORMAL:
7391 himlOld = infoPtr->himlNormal;
7392 infoPtr->himlNormal = himl;
7393 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7394 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7395 break;
7397 case LVSIL_SMALL:
7398 himlOld = infoPtr->himlSmall;
7399 infoPtr->himlSmall = himl;
7400 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7401 break;
7403 case LVSIL_STATE:
7404 himlOld = infoPtr->himlState;
7405 infoPtr->himlState = himl;
7406 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7407 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7408 break;
7410 default:
7411 ERR("Unknown icon type=%d\n", nType);
7412 return NULL;
7415 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7416 if (infoPtr->nItemHeight != oldHeight)
7417 LISTVIEW_UpdateScroll(infoPtr);
7419 return himlOld;
7422 /***
7423 * DESCRIPTION:
7424 * Preallocates memory (does *not* set the actual count of items !)
7426 * PARAMETER(S):
7427 * [I] infoPtr : valid pointer to the listview structure
7428 * [I] nItems : item count (projected number of items to allocate)
7429 * [I] dwFlags : update flags
7431 * RETURN:
7432 * SUCCESS : TRUE
7433 * FAILURE : FALSE
7435 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7437 TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
7439 if (infoPtr->dwStyle & LVS_OWNERDATA)
7441 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7442 INT nOldCount = infoPtr->nItemCount;
7444 if (nItems < nOldCount)
7446 RANGE range = { nItems, nOldCount };
7447 ranges_del(infoPtr->selectionRanges, range);
7448 if (infoPtr->nFocusedItem >= nItems)
7450 infoPtr->nFocusedItem = -1;
7451 SetRectEmpty(&infoPtr->rcFocus);
7455 infoPtr->nItemCount = nItems;
7456 LISTVIEW_UpdateScroll(infoPtr);
7458 /* the flags are valid only in ownerdata report and list modes */
7459 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7461 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7462 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7464 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7465 LISTVIEW_InvalidateList(infoPtr);
7466 else
7468 INT nFrom, nTo;
7469 POINT Origin;
7470 RECT rcErase;
7472 LISTVIEW_GetOrigin(infoPtr, &Origin);
7473 nFrom = min(nOldCount, nItems);
7474 nTo = max(nOldCount, nItems);
7476 if (uView == LVS_REPORT)
7478 rcErase.left = 0;
7479 rcErase.top = nFrom * infoPtr->nItemHeight;
7480 rcErase.right = infoPtr->nItemWidth;
7481 rcErase.bottom = nTo * infoPtr->nItemHeight;
7482 OffsetRect(&rcErase, Origin.x, Origin.y);
7483 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7484 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7486 else /* LVS_LIST */
7488 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7490 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7491 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7492 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7493 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7494 OffsetRect(&rcErase, Origin.x, Origin.y);
7495 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7496 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7498 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7499 rcErase.top = 0;
7500 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7501 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7502 OffsetRect(&rcErase, Origin.x, Origin.y);
7503 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7504 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7508 else
7510 /* According to MSDN for non-LVS_OWNERDATA this is just
7511 * a performance issue. The control allocates its internal
7512 * data structures for the number of items specified. It
7513 * cuts down on the number of memory allocations. Therefore
7514 * we will just issue a WARN here
7516 WARN("for non-ownerdata performance option not implemented.\n");
7519 return TRUE;
7522 /***
7523 * DESCRIPTION:
7524 * Sets the position of an item.
7526 * PARAMETER(S):
7527 * [I] infoPtr : valid pointer to the listview structure
7528 * [I] nItem : item index
7529 * [I] pt : coordinate
7531 * RETURN:
7532 * SUCCESS : TRUE
7533 * FAILURE : FALSE
7535 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7537 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7538 POINT Origin;
7540 TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7542 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7543 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7545 LISTVIEW_GetOrigin(infoPtr, &Origin);
7547 /* This point value seems to be an undocumented feature.
7548 * The best guess is that it means either at the origin,
7549 * or at true beginning of the list. I will assume the origin. */
7550 if ((pt.x == -1) && (pt.y == -1))
7551 pt = Origin;
7553 if (uView == LVS_ICON)
7555 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7556 pt.y -= ICON_TOP_PADDING;
7558 pt.x -= Origin.x;
7559 pt.y -= Origin.y;
7561 infoPtr->bAutoarrange = FALSE;
7563 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7566 /***
7567 * DESCRIPTION:
7568 * Sets the state of one or many items.
7570 * PARAMETER(S):
7571 * [I] infoPtr : valid pointer to the listview structure
7572 * [I] nItem : item index
7573 * [I] lpLVItem : item or subitem info
7575 * RETURN:
7576 * SUCCESS : TRUE
7577 * FAILURE : FALSE
7579 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7581 BOOL bResult = TRUE;
7582 LVITEMW lvItem;
7584 lvItem.iItem = nItem;
7585 lvItem.iSubItem = 0;
7586 lvItem.mask = LVIF_STATE;
7587 lvItem.state = lpLVItem->state;
7588 lvItem.stateMask = lpLVItem->stateMask;
7589 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7591 if (nItem == -1)
7593 /* apply to all items */
7594 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7595 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7597 else
7598 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7601 * Update selection mark
7603 * Investigation on windows 2k showed that selection mark was updated
7604 * whenever a new selection was made, but if the selected item was
7605 * unselected it was not updated.
7607 * we are probably still not 100% accurate, but this at least sets the
7608 * proper selection mark when it is needed
7611 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7612 (infoPtr->nSelectionMark == -1))
7614 int i;
7615 for (i = 0; i < infoPtr->nItemCount; i++)
7617 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7619 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7621 infoPtr->nSelectionMark = i;
7622 break;
7625 else if (ranges_contain(infoPtr->selectionRanges, i))
7627 infoPtr->nSelectionMark = i;
7628 break;
7633 return bResult;
7636 /***
7637 * DESCRIPTION:
7638 * Sets the text of an item or subitem.
7640 * PARAMETER(S):
7641 * [I] hwnd : window handle
7642 * [I] nItem : item index
7643 * [I] lpLVItem : item or subitem info
7644 * [I] isW : TRUE if input is Unicode
7646 * RETURN:
7647 * SUCCESS : TRUE
7648 * FAILURE : FALSE
7650 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7652 LVITEMW lvItem;
7654 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7656 lvItem.iItem = nItem;
7657 lvItem.iSubItem = lpLVItem->iSubItem;
7658 lvItem.mask = LVIF_TEXT;
7659 lvItem.pszText = lpLVItem->pszText;
7660 lvItem.cchTextMax = lpLVItem->cchTextMax;
7662 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7664 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7667 /***
7668 * DESCRIPTION:
7669 * Set item index that marks the start of a multiple selection.
7671 * PARAMETER(S):
7672 * [I] infoPtr : valid pointer to the listview structure
7673 * [I] nIndex : index
7675 * RETURN:
7676 * Index number or -1 if there is no selection mark.
7678 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7680 INT nOldIndex = infoPtr->nSelectionMark;
7682 TRACE("(nIndex=%d)\n", nIndex);
7684 infoPtr->nSelectionMark = nIndex;
7686 return nOldIndex;
7689 /***
7690 * DESCRIPTION:
7691 * Sets the text background color.
7693 * PARAMETER(S):
7694 * [I] infoPtr : valid pointer to the listview structure
7695 * [I] clrTextBk : text background color
7697 * RETURN:
7698 * SUCCESS : TRUE
7699 * FAILURE : FALSE
7701 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7703 TRACE("(clrTextBk=%x)\n", clrTextBk);
7705 if (infoPtr->clrTextBk != clrTextBk)
7707 infoPtr->clrTextBk = clrTextBk;
7708 LISTVIEW_InvalidateList(infoPtr);
7711 return TRUE;
7714 /***
7715 * DESCRIPTION:
7716 * Sets the text foreground color.
7718 * PARAMETER(S):
7719 * [I] infoPtr : valid pointer to the listview structure
7720 * [I] clrText : text color
7722 * RETURN:
7723 * SUCCESS : TRUE
7724 * FAILURE : FALSE
7726 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7728 TRACE("(clrText=%x)\n", clrText);
7730 if (infoPtr->clrText != clrText)
7732 infoPtr->clrText = clrText;
7733 LISTVIEW_InvalidateList(infoPtr);
7736 return TRUE;
7739 /***
7740 * DESCRIPTION:
7741 * Determines which listview item is located at the specified position.
7743 * PARAMETER(S):
7744 * [I] infoPtr : valid pointer to the listview structure
7745 * [I] hwndNewToolTip : handle to new ToolTip
7747 * RETURN:
7748 * old tool tip
7750 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7752 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7753 infoPtr->hwndToolTip = hwndNewToolTip;
7754 return hwndOldToolTip;
7758 * DESCRIPTION:
7759 * sets the Unicode character format flag for the control
7760 * PARAMETER(S):
7761 * [I] infoPtr :valid pointer to the listview structure
7762 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
7764 * RETURN:
7765 * Old Unicode Format
7767 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL fUnicode)
7769 BOOL rc = infoPtr->notifyFormat;
7770 infoPtr->notifyFormat = (fUnicode)?NFR_UNICODE:NFR_ANSI;
7771 return rc;
7774 /* LISTVIEW_SetWorkAreas */
7776 /***
7777 * DESCRIPTION:
7778 * Callback internally used by LISTVIEW_SortItems()
7780 * PARAMETER(S):
7781 * [I] first : pointer to first ITEM_INFO to compare
7782 * [I] second : pointer to second ITEM_INFO to compare
7783 * [I] lParam : HWND of control
7785 * RETURN:
7786 * if first comes before second : negative
7787 * if first comes after second : positive
7788 * if first and second are equivalent : zero
7790 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7792 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7793 ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
7794 ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
7796 /* Forward the call to the client defined callback */
7797 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7800 /***
7801 * DESCRIPTION:
7802 * Sorts the listview items.
7804 * PARAMETER(S):
7805 * [I] infoPtr : valid pointer to the listview structure
7806 * [I] pfnCompare : application-defined value
7807 * [I] lParamSort : pointer to comparison callback
7809 * RETURN:
7810 * SUCCESS : TRUE
7811 * FAILURE : FALSE
7813 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7815 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7816 HDPA hdpaSubItems;
7817 ITEM_INFO *lpItem;
7818 LPVOID selectionMarkItem;
7819 LVITEMW item;
7820 int i;
7822 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7824 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7826 if (!pfnCompare) return FALSE;
7827 if (!infoPtr->hdpaItems) return FALSE;
7829 /* if there are 0 or 1 items, there is no need to sort */
7830 if (infoPtr->nItemCount < 2) return TRUE;
7832 if (infoPtr->nFocusedItem >= 0)
7834 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7835 lpItem = DPA_GetPtr(hdpaSubItems, 0);
7836 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7838 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7839 /* clear the lpItem->state for non-selected ones */
7840 /* remove the selection ranges */
7842 infoPtr->pfnCompare = pfnCompare;
7843 infoPtr->lParamSort = lParamSort;
7844 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7846 /* Adjust selections and indices so that they are the way they should
7847 * be after the sort (otherwise, the list items move around, but
7848 * whatever is at the item's previous original position will be
7849 * selected instead)
7851 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7852 for (i=0; i < infoPtr->nItemCount; i++)
7854 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
7855 lpItem = DPA_GetPtr(hdpaSubItems, 0);
7857 if (lpItem->state & LVIS_SELECTED)
7859 item.state = LVIS_SELECTED;
7860 item.stateMask = LVIS_SELECTED;
7861 LISTVIEW_SetItemState(infoPtr, i, &item);
7863 if (lpItem->state & LVIS_FOCUSED)
7865 infoPtr->nFocusedItem = i;
7866 lpItem->state &= ~LVIS_FOCUSED;
7869 if (selectionMarkItem != NULL)
7870 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7871 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7873 /* refresh the display */
7874 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7875 LISTVIEW_InvalidateList(infoPtr);
7877 return TRUE;
7880 /***
7881 * DESCRIPTION:
7882 * Update theme handle after a theme change.
7884 * PARAMETER(S):
7885 * [I] infoPtr : valid pointer to the listview structure
7887 * RETURN:
7888 * SUCCESS : 0
7889 * FAILURE : something else
7891 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr)
7893 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7894 CloseThemeData(theme);
7895 OpenThemeData(infoPtr->hwndSelf, themeClass);
7896 return 0;
7899 /***
7900 * DESCRIPTION:
7901 * Updates an items or rearranges the listview control.
7903 * PARAMETER(S):
7904 * [I] infoPtr : valid pointer to the listview structure
7905 * [I] nItem : item index
7907 * RETURN:
7908 * SUCCESS : TRUE
7909 * FAILURE : FALSE
7911 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7913 TRACE("(nItem=%d)\n", nItem);
7915 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7917 /* rearrange with default alignment style */
7918 if (is_autoarrange(infoPtr))
7919 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7920 else
7921 LISTVIEW_InvalidateItem(infoPtr, nItem);
7923 return TRUE;
7926 /***
7927 * DESCRIPTION:
7928 * Draw the track line at the place defined in the infoPtr structure.
7929 * The line is drawn with a XOR pen so drawing the line for the second time
7930 * in the same place erases the line.
7932 * PARAMETER(S):
7933 * [I] infoPtr : valid pointer to the listview structure
7935 * RETURN:
7936 * SUCCESS : TRUE
7937 * FAILURE : FALSE
7939 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
7941 HPEN hOldPen;
7942 HDC hdc;
7943 INT oldROP;
7945 if (infoPtr->xTrackLine == -1)
7946 return FALSE;
7948 if (!(hdc = GetDC(infoPtr->hwndSelf)))
7949 return FALSE;
7950 hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
7951 oldROP = SetROP2(hdc, R2_XORPEN);
7952 MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
7953 LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
7954 SetROP2(hdc, oldROP);
7955 SelectObject(hdc, hOldPen);
7956 ReleaseDC(infoPtr->hwndSelf, hdc);
7957 return TRUE;
7960 /***
7961 * DESCRIPTION:
7962 * Called when an edit control should be displayed. This function is called after
7963 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
7965 * PARAMETER(S):
7966 * [I] hwnd : Handle to the listview
7967 * [I] uMsg : WM_TIMER (ignored)
7968 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
7969 * [I] dwTimer : The elapsed time (ignored)
7971 * RETURN:
7972 * None.
7974 static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
7976 DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
7977 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
7979 KillTimer(hwnd, idEvent);
7980 editItem->fEnabled = FALSE;
7981 /* check if the item is still selected */
7982 if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
7983 LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
7986 /***
7987 * DESCRIPTION:
7988 * Creates the listview control - the WM_NCCREATE phase.
7990 * PARAMETER(S):
7991 * [I] hwnd : window handle
7992 * [I] lpcs : the create parameters
7994 * RETURN:
7995 * Success: TRUE
7996 * Failure: FALSE
7998 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
8000 LISTVIEW_INFO *infoPtr;
8001 LOGFONTW logFont;
8003 TRACE("(lpcs=%p)\n", lpcs);
8005 /* initialize info pointer */
8006 infoPtr = Alloc(sizeof(LISTVIEW_INFO));
8007 if (!infoPtr) return FALSE;
8009 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
8011 infoPtr->hwndSelf = hwnd;
8012 infoPtr->dwStyle = lpcs->style; /* Note: may be changed in WM_CREATE */
8013 /* determine the type of structures to use */
8014 infoPtr->hwndNotify = lpcs->hwndParent;
8015 /* infoPtr->notifyFormat will be filled in WM_CREATE */
8017 /* initialize color information */
8018 infoPtr->clrBk = CLR_NONE;
8019 infoPtr->clrText = CLR_DEFAULT;
8020 infoPtr->clrTextBk = CLR_DEFAULT;
8021 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
8023 /* set default values */
8024 infoPtr->nFocusedItem = -1;
8025 infoPtr->nSelectionMark = -1;
8026 infoPtr->nHotItem = -1;
8027 infoPtr->bRedraw = TRUE;
8028 infoPtr->bNoItemMetrics = TRUE;
8029 infoPtr->bDoChangeNotify = TRUE;
8030 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
8031 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
8032 infoPtr->nEditLabelItem = -1;
8033 infoPtr->dwHoverTime = -1; /* default system hover time */
8034 infoPtr->nMeasureItemHeight = 0;
8035 infoPtr->xTrackLine = -1; /* no track line */
8036 infoPtr->itemEdit.fEnabled = FALSE;
8038 /* get default font (icon title) */
8039 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
8040 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
8041 infoPtr->hFont = infoPtr->hDefaultFont;
8042 LISTVIEW_SaveTextMetrics(infoPtr);
8044 /* allocate memory for the data structure */
8045 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
8046 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
8047 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
8048 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
8049 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
8050 return TRUE;
8052 fail:
8053 DestroyWindow(infoPtr->hwndHeader);
8054 ranges_destroy(infoPtr->selectionRanges);
8055 DPA_Destroy(infoPtr->hdpaItems);
8056 DPA_Destroy(infoPtr->hdpaPosX);
8057 DPA_Destroy(infoPtr->hdpaPosY);
8058 DPA_Destroy(infoPtr->hdpaColumns);
8059 Free(infoPtr);
8060 return FALSE;
8063 /***
8064 * DESCRIPTION:
8065 * Creates the listview control - the WM_CREATE phase. Most of the data is
8066 * already set up in LISTVIEW_NCCreate
8068 * PARAMETER(S):
8069 * [I] hwnd : window handle
8070 * [I] lpcs : the create parameters
8072 * RETURN:
8073 * Success: 0
8074 * Failure: -1
8076 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
8078 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8079 UINT uView = lpcs->style & LVS_TYPEMASK;
8080 DWORD dFlags = WS_CHILD | HDS_HORZ | HDS_FULLDRAG | HDS_DRAGDROP;
8082 TRACE("(lpcs=%p)\n", lpcs);
8084 infoPtr->dwStyle = lpcs->style;
8085 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
8086 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
8088 /* setup creation flags */
8089 dFlags |= (LVS_NOSORTHEADER & lpcs->style) ? 0 : HDS_BUTTONS;
8090 dFlags |= (LVS_NOCOLUMNHEADER & lpcs->style) ? HDS_HIDDEN : 0;
8092 /* create header */
8093 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL, dFlags,
8094 0, 0, 0, 0, hwnd, NULL,
8095 lpcs->hInstance, NULL);
8096 if (!infoPtr->hwndHeader) return -1;
8098 /* set header unicode format */
8099 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, TRUE, 0);
8101 /* set header font */
8102 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
8104 /* init item size to avoid division by 0 */
8105 LISTVIEW_UpdateItemSize (infoPtr);
8107 if (uView == LVS_REPORT)
8109 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
8111 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8113 LISTVIEW_UpdateSize(infoPtr);
8114 LISTVIEW_UpdateScroll(infoPtr);
8117 OpenThemeData(hwnd, themeClass);
8119 /* initialize the icon sizes */
8120 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
8121 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
8122 return 0;
8125 /***
8126 * DESCRIPTION:
8127 * Destroys the listview control.
8129 * PARAMETER(S):
8130 * [I] infoPtr : valid pointer to the listview structure
8132 * RETURN:
8133 * Success: 0
8134 * Failure: -1
8136 static LRESULT LISTVIEW_Destroy(const LISTVIEW_INFO *infoPtr)
8138 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8139 CloseThemeData(theme);
8140 return 0;
8143 /***
8144 * DESCRIPTION:
8145 * Enables the listview control.
8147 * PARAMETER(S):
8148 * [I] infoPtr : valid pointer to the listview structure
8149 * [I] bEnable : specifies whether to enable or disable the window
8151 * RETURN:
8152 * SUCCESS : TRUE
8153 * FAILURE : FALSE
8155 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr, BOOL bEnable)
8157 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
8158 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8159 return TRUE;
8162 /***
8163 * DESCRIPTION:
8164 * Erases the background of the listview control.
8166 * PARAMETER(S):
8167 * [I] infoPtr : valid pointer to the listview structure
8168 * [I] hdc : device context handle
8170 * RETURN:
8171 * SUCCESS : TRUE
8172 * FAILURE : FALSE
8174 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
8176 RECT rc;
8178 TRACE("(hdc=%p)\n", hdc);
8180 if (!GetClipBox(hdc, &rc)) return FALSE;
8182 /* for double buffered controls we need to do this during refresh */
8183 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
8185 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
8189 /***
8190 * DESCRIPTION:
8191 * Helper function for LISTVIEW_[HV]Scroll *only*.
8192 * Performs vertical/horizontal scrolling by a give amount.
8194 * PARAMETER(S):
8195 * [I] infoPtr : valid pointer to the listview structure
8196 * [I] dx : amount of horizontal scroll
8197 * [I] dy : amount of vertical scroll
8199 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
8201 /* now we can scroll the list */
8202 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
8203 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
8204 /* if we have focus, adjust rect */
8205 OffsetRect(&infoPtr->rcFocus, dx, dy);
8206 UpdateWindow(infoPtr->hwndSelf);
8209 /***
8210 * DESCRIPTION:
8211 * Performs vertical scrolling.
8213 * PARAMETER(S):
8214 * [I] infoPtr : valid pointer to the listview structure
8215 * [I] nScrollCode : scroll code
8216 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8217 * [I] hScrollWnd : scrollbar control window handle
8219 * RETURN:
8220 * Zero
8222 * NOTES:
8223 * SB_LINEUP/SB_LINEDOWN:
8224 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8225 * for LVS_REPORT is 1 line
8226 * for LVS_LIST cannot occur
8229 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8230 INT nScrollDiff, HWND hScrollWnd)
8232 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8233 INT nOldScrollPos, nNewScrollPos;
8234 SCROLLINFO scrollInfo;
8235 BOOL is_an_icon;
8237 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8238 debugscrollcode(nScrollCode), nScrollDiff);
8240 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8242 scrollInfo.cbSize = sizeof(SCROLLINFO);
8243 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8245 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
8247 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
8249 nOldScrollPos = scrollInfo.nPos;
8250 switch (nScrollCode)
8252 case SB_INTERNAL:
8253 break;
8255 case SB_LINEUP:
8256 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
8257 break;
8259 case SB_LINEDOWN:
8260 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
8261 break;
8263 case SB_PAGEUP:
8264 nScrollDiff = -scrollInfo.nPage;
8265 break;
8267 case SB_PAGEDOWN:
8268 nScrollDiff = scrollInfo.nPage;
8269 break;
8271 case SB_THUMBPOSITION:
8272 case SB_THUMBTRACK:
8273 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8274 break;
8276 default:
8277 nScrollDiff = 0;
8280 /* quit right away if pos isn't changing */
8281 if (nScrollDiff == 0) return 0;
8283 /* calculate new position, and handle overflows */
8284 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8285 if (nScrollDiff > 0) {
8286 if (nNewScrollPos < nOldScrollPos ||
8287 nNewScrollPos > scrollInfo.nMax)
8288 nNewScrollPos = scrollInfo.nMax;
8289 } else {
8290 if (nNewScrollPos > nOldScrollPos ||
8291 nNewScrollPos < scrollInfo.nMin)
8292 nNewScrollPos = scrollInfo.nMin;
8295 /* set the new position, and reread in case it changed */
8296 scrollInfo.fMask = SIF_POS;
8297 scrollInfo.nPos = nNewScrollPos;
8298 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
8300 /* carry on only if it really changed */
8301 if (nNewScrollPos == nOldScrollPos) return 0;
8303 /* now adjust to client coordinates */
8304 nScrollDiff = nOldScrollPos - nNewScrollPos;
8305 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
8307 /* and scroll the window */
8308 scroll_list(infoPtr, 0, nScrollDiff);
8310 return 0;
8313 /***
8314 * DESCRIPTION:
8315 * Performs horizontal scrolling.
8317 * PARAMETER(S):
8318 * [I] infoPtr : valid pointer to the listview structure
8319 * [I] nScrollCode : scroll code
8320 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8321 * [I] hScrollWnd : scrollbar control window handle
8323 * RETURN:
8324 * Zero
8326 * NOTES:
8327 * SB_LINELEFT/SB_LINERIGHT:
8328 * for LVS_ICON, LVS_SMALLICON 1 pixel
8329 * for LVS_REPORT is 1 pixel
8330 * for LVS_LIST is 1 column --> which is a 1 because the
8331 * scroll is based on columns not pixels
8334 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8335 INT nScrollDiff, HWND hScrollWnd)
8337 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8338 INT nOldScrollPos, nNewScrollPos;
8339 SCROLLINFO scrollInfo;
8341 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8342 debugscrollcode(nScrollCode), nScrollDiff);
8344 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8346 scrollInfo.cbSize = sizeof(SCROLLINFO);
8347 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8349 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
8351 nOldScrollPos = scrollInfo.nPos;
8353 switch (nScrollCode)
8355 case SB_INTERNAL:
8356 break;
8358 case SB_LINELEFT:
8359 nScrollDiff = -1;
8360 break;
8362 case SB_LINERIGHT:
8363 nScrollDiff = 1;
8364 break;
8366 case SB_PAGELEFT:
8367 nScrollDiff = -scrollInfo.nPage;
8368 break;
8370 case SB_PAGERIGHT:
8371 nScrollDiff = scrollInfo.nPage;
8372 break;
8374 case SB_THUMBPOSITION:
8375 case SB_THUMBTRACK:
8376 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8377 break;
8379 default:
8380 nScrollDiff = 0;
8383 /* quit right away if pos isn't changing */
8384 if (nScrollDiff == 0) return 0;
8386 /* calculate new position, and handle overflows */
8387 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8388 if (nScrollDiff > 0) {
8389 if (nNewScrollPos < nOldScrollPos ||
8390 nNewScrollPos > scrollInfo.nMax)
8391 nNewScrollPos = scrollInfo.nMax;
8392 } else {
8393 if (nNewScrollPos > nOldScrollPos ||
8394 nNewScrollPos < scrollInfo.nMin)
8395 nNewScrollPos = scrollInfo.nMin;
8398 /* set the new position, and reread in case it changed */
8399 scrollInfo.fMask = SIF_POS;
8400 scrollInfo.nPos = nNewScrollPos;
8401 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
8403 /* carry on only if it really changed */
8404 if (nNewScrollPos == nOldScrollPos) return 0;
8406 if(uView == LVS_REPORT)
8407 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
8409 /* now adjust to client coordinates */
8410 nScrollDiff = nOldScrollPos - nNewScrollPos;
8411 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
8413 /* and scroll the window */
8414 scroll_list(infoPtr, nScrollDiff, 0);
8416 return 0;
8419 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
8421 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8422 INT gcWheelDelta = 0;
8423 INT pulScrollLines = 3;
8424 SCROLLINFO scrollInfo;
8426 TRACE("(wheelDelta=%d)\n", wheelDelta);
8428 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8429 gcWheelDelta -= wheelDelta;
8431 scrollInfo.cbSize = sizeof(SCROLLINFO);
8432 scrollInfo.fMask = SIF_POS;
8434 switch(uView)
8436 case LVS_ICON:
8437 case LVS_SMALLICON:
8439 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8440 * should be fixed in the future.
8442 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
8443 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8444 break;
8446 case LVS_REPORT:
8447 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8449 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
8450 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8451 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
8453 break;
8455 case LVS_LIST:
8456 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8457 break;
8459 return 0;
8462 /***
8463 * DESCRIPTION:
8464 * ???
8466 * PARAMETER(S):
8467 * [I] infoPtr : valid pointer to the listview structure
8468 * [I] nVirtualKey : virtual key
8469 * [I] lKeyData : key data
8471 * RETURN:
8472 * Zero
8474 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8476 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8477 HWND hwndSelf = infoPtr->hwndSelf;
8478 INT nItem = -1;
8479 NMLVKEYDOWN nmKeyDown;
8481 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
8483 /* send LVN_KEYDOWN notification */
8484 nmKeyDown.wVKey = nVirtualKey;
8485 nmKeyDown.flags = 0;
8486 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8487 if (!IsWindow(hwndSelf))
8488 return 0;
8490 switch (nVirtualKey)
8492 case VK_SPACE:
8493 nItem = infoPtr->nFocusedItem;
8494 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
8495 toggle_checkbox_state(infoPtr, infoPtr->nFocusedItem);
8496 break;
8498 case VK_RETURN:
8499 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8501 if (!notify(infoPtr, NM_RETURN)) return 0;
8502 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8504 break;
8506 case VK_HOME:
8507 if (infoPtr->nItemCount > 0)
8508 nItem = 0;
8509 break;
8511 case VK_END:
8512 if (infoPtr->nItemCount > 0)
8513 nItem = infoPtr->nItemCount - 1;
8514 break;
8516 case VK_LEFT:
8517 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8518 break;
8520 case VK_UP:
8521 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8522 break;
8524 case VK_RIGHT:
8525 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8526 break;
8528 case VK_DOWN:
8529 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8530 break;
8532 case VK_PRIOR:
8533 if (uView == LVS_REPORT)
8535 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8536 if (infoPtr->nFocusedItem == topidx)
8537 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8538 else
8539 nItem = topidx;
8541 else
8542 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8543 * LISTVIEW_GetCountPerRow(infoPtr);
8544 if(nItem < 0) nItem = 0;
8545 break;
8547 case VK_NEXT:
8548 if (uView == LVS_REPORT)
8550 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8551 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8552 if (infoPtr->nFocusedItem == topidx + cnt - 1)
8553 nItem = infoPtr->nFocusedItem + cnt - 1;
8554 else
8555 nItem = topidx + cnt - 1;
8557 else
8558 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8559 * LISTVIEW_GetCountPerRow(infoPtr);
8560 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8561 break;
8564 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8565 LISTVIEW_KeySelection(infoPtr, nItem, nVirtualKey == VK_SPACE);
8567 return 0;
8570 /***
8571 * DESCRIPTION:
8572 * Kills the focus.
8574 * PARAMETER(S):
8575 * [I] infoPtr : valid pointer to the listview structure
8577 * RETURN:
8578 * Zero
8580 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8582 TRACE("()\n");
8584 /* if we did not have the focus, there's nothing to do */
8585 if (!infoPtr->bFocus) return 0;
8587 /* send NM_KILLFOCUS notification */
8588 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8590 /* if we have a focus rectagle, get rid of it */
8591 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8593 /* set window focus flag */
8594 infoPtr->bFocus = FALSE;
8596 /* invalidate the selected items before resetting focus flag */
8597 LISTVIEW_InvalidateSelectedItems(infoPtr);
8599 return 0;
8602 /***
8603 * DESCRIPTION:
8604 * Processes double click messages (left mouse button).
8606 * PARAMETER(S):
8607 * [I] infoPtr : valid pointer to the listview structure
8608 * [I] wKey : key flag
8609 * [I] x,y : mouse coordinate
8611 * RETURN:
8612 * Zero
8614 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8616 LVHITTESTINFO htInfo;
8618 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8620 /* Cancel the item edition if any */
8621 if (infoPtr->itemEdit.fEnabled)
8623 KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
8624 infoPtr->itemEdit.fEnabled = FALSE;
8627 /* send NM_RELEASEDCAPTURE notification */
8628 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8630 htInfo.pt.x = x;
8631 htInfo.pt.y = y;
8633 /* send NM_DBLCLK notification */
8634 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8635 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8637 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8638 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8640 return 0;
8643 /***
8644 * DESCRIPTION:
8645 * Processes mouse down messages (left mouse button).
8647 * PARAMETERS:
8648 * infoPtr [I ] valid pointer to the listview structure
8649 * wKey [I ] key flag
8650 * x,y [I ] mouse coordinate
8652 * RETURN:
8653 * Zero
8655 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8657 LVHITTESTINFO lvHitTestInfo;
8658 static BOOL bGroupSelect = TRUE;
8659 POINT pt = { x, y };
8660 INT nItem;
8662 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8664 /* send NM_RELEASEDCAPTURE notification */
8665 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8667 /* set left button down flag and record the click position */
8668 infoPtr->bLButtonDown = TRUE;
8669 infoPtr->ptClickPos = pt;
8670 infoPtr->bDragging = FALSE;
8672 lvHitTestInfo.pt.x = x;
8673 lvHitTestInfo.pt.y = y;
8675 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8676 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
8677 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8679 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8681 toggle_checkbox_state(infoPtr, nItem);
8682 return 0;
8685 if (infoPtr->dwStyle & LVS_SINGLESEL)
8687 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8688 infoPtr->nEditLabelItem = nItem;
8689 else
8690 LISTVIEW_SetSelection(infoPtr, nItem);
8692 else
8694 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8696 if (bGroupSelect)
8698 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8699 LISTVIEW_SetItemFocus(infoPtr, nItem);
8700 infoPtr->nSelectionMark = nItem;
8702 else
8704 LVITEMW item;
8706 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8707 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8709 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8710 infoPtr->nSelectionMark = nItem;
8713 else if (wKey & MK_CONTROL)
8715 LVITEMW item;
8717 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8719 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8720 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8721 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8722 infoPtr->nSelectionMark = nItem;
8724 else if (wKey & MK_SHIFT)
8726 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8728 else
8730 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8731 infoPtr->nEditLabelItem = nItem;
8733 /* set selection (clears other pre-existing selections) */
8734 LISTVIEW_SetSelection(infoPtr, nItem);
8738 if (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE)
8739 if(lvHitTestInfo.iItem != -1) notify_itemactivate(infoPtr,&lvHitTestInfo);
8741 else
8743 /* remove all selections */
8744 LISTVIEW_DeselectAll(infoPtr);
8745 ReleaseCapture();
8748 return 0;
8751 /***
8752 * DESCRIPTION:
8753 * Processes mouse up messages (left mouse button).
8755 * PARAMETERS:
8756 * infoPtr [I ] valid pointer to the listview structure
8757 * wKey [I ] key flag
8758 * x,y [I ] mouse coordinate
8760 * RETURN:
8761 * Zero
8763 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8765 LVHITTESTINFO lvHitTestInfo;
8767 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8769 if (!infoPtr->bLButtonDown) return 0;
8771 lvHitTestInfo.pt.x = x;
8772 lvHitTestInfo.pt.y = y;
8774 /* send NM_CLICK notification */
8775 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8776 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8778 /* set left button flag */
8779 infoPtr->bLButtonDown = FALSE;
8781 if (infoPtr->bDragging)
8783 infoPtr->bDragging = FALSE;
8784 return 0;
8787 /* if we clicked on a selected item, edit the label */
8788 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8790 /* we want to make sure the user doesn't want to do a double click. So we will
8791 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
8793 infoPtr->itemEdit.fEnabled = TRUE;
8794 infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
8795 SetTimer(infoPtr->hwndSelf,
8796 (UINT_PTR)&infoPtr->itemEdit,
8797 GetDoubleClickTime(),
8798 LISTVIEW_DelayedEditItem);
8801 if (!infoPtr->bFocus)
8802 SetFocus(infoPtr->hwndSelf);
8804 return 0;
8807 /***
8808 * DESCRIPTION:
8809 * Destroys the listview control (called after WM_DESTROY).
8811 * PARAMETER(S):
8812 * [I] infoPtr : valid pointer to the listview structure
8814 * RETURN:
8815 * Zero
8817 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8819 TRACE("()\n");
8821 /* delete all items */
8822 LISTVIEW_DeleteAllItems(infoPtr, TRUE);
8824 /* destroy data structure */
8825 DPA_Destroy(infoPtr->hdpaItems);
8826 DPA_Destroy(infoPtr->hdpaPosX);
8827 DPA_Destroy(infoPtr->hdpaPosY);
8828 DPA_Destroy(infoPtr->hdpaColumns);
8829 ranges_destroy(infoPtr->selectionRanges);
8831 /* destroy image lists */
8832 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8834 if (infoPtr->himlNormal)
8835 ImageList_Destroy(infoPtr->himlNormal);
8836 if (infoPtr->himlSmall)
8837 ImageList_Destroy(infoPtr->himlSmall);
8838 if (infoPtr->himlState)
8839 ImageList_Destroy(infoPtr->himlState);
8842 /* destroy font, bkgnd brush */
8843 infoPtr->hFont = 0;
8844 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8845 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8847 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8849 /* free listview info pointer*/
8850 Free(infoPtr);
8852 return 0;
8855 /***
8856 * DESCRIPTION:
8857 * Handles notifications from header.
8859 * PARAMETER(S):
8860 * [I] infoPtr : valid pointer to the listview structure
8861 * [I] nCtrlId : control identifier
8862 * [I] lpnmh : notification information
8864 * RETURN:
8865 * Zero
8867 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8869 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8870 HWND hwndSelf = infoPtr->hwndSelf;
8872 TRACE("(lpnmh=%p)\n", lpnmh);
8874 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8876 switch (lpnmh->hdr.code)
8878 case HDN_TRACKW:
8879 case HDN_TRACKA:
8881 COLUMN_INFO *lpColumnInfo;
8882 POINT ptOrigin;
8883 INT x;
8885 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8886 break;
8888 /* remove the old line (if any) */
8889 LISTVIEW_DrawTrackLine(infoPtr);
8891 /* compute & draw the new line */
8892 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8893 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
8894 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8895 infoPtr->xTrackLine = x + ptOrigin.x;
8896 LISTVIEW_DrawTrackLine(infoPtr);
8897 break;
8900 case HDN_ENDTRACKA:
8901 case HDN_ENDTRACKW:
8902 /* remove the track line (if any) */
8903 LISTVIEW_DrawTrackLine(infoPtr);
8904 infoPtr->xTrackLine = -1;
8905 break;
8907 case HDN_ENDDRAG:
8908 FIXME("Changing column order not implemented\n");
8909 return TRUE;
8911 case HDN_ITEMCHANGINGW:
8912 case HDN_ITEMCHANGINGA:
8913 return notify_forward_header(infoPtr, lpnmh);
8915 case HDN_ITEMCHANGEDW:
8916 case HDN_ITEMCHANGEDA:
8918 COLUMN_INFO *lpColumnInfo;
8919 INT dx, cxy;
8921 notify_forward_header(infoPtr, lpnmh);
8922 if (!IsWindow(hwndSelf))
8923 break;
8925 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8927 HDITEMW hdi;
8929 hdi.mask = HDI_WIDTH;
8930 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, &hdi)) return 0;
8931 cxy = hdi.cxy;
8933 else
8934 cxy = lpnmh->pitem->cxy;
8936 /* determine how much we change since the last know position */
8937 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8938 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8939 if (dx != 0)
8941 lpColumnInfo->rcHeader.right += dx;
8942 if (lpnmh->iItem + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
8943 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8944 else
8946 /* only needs to update the scrolls */
8947 infoPtr->nItemWidth += dx;
8948 LISTVIEW_UpdateScroll(infoPtr);
8950 LISTVIEW_UpdateItemSize(infoPtr);
8951 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8953 POINT ptOrigin;
8954 RECT rcCol = lpColumnInfo->rcHeader;
8956 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8957 OffsetRect(&rcCol, ptOrigin.x, 0);
8959 rcCol.top = infoPtr->rcList.top;
8960 rcCol.bottom = infoPtr->rcList.bottom;
8962 /* resizing left-aligned columns leaves most of the left side untouched */
8963 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8965 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
8966 if (dx > 0)
8967 nMaxDirty += dx;
8968 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
8971 /* when shrinking the last column clear the now unused field */
8972 if (lpnmh->iItem == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1 && dx < 0)
8973 rcCol.right -= dx;
8975 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8979 break;
8981 case HDN_ITEMCLICKW:
8982 case HDN_ITEMCLICKA:
8984 /* Handle sorting by Header Column */
8985 NMLISTVIEW nmlv;
8987 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8988 nmlv.iItem = -1;
8989 nmlv.iSubItem = lpnmh->iItem;
8990 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8991 notify_forward_header(infoPtr, lpnmh);
8993 break;
8995 case HDN_DIVIDERDBLCLICKW:
8996 case HDN_DIVIDERDBLCLICKA:
8997 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
8998 break;
9001 return 0;
9004 /***
9005 * DESCRIPTION:
9006 * Paint non-client area of control.
9008 * PARAMETER(S):
9009 * [I] infoPtr : valid pointer to the listview structureof the sender
9010 * [I] region : update region
9012 * RETURN:
9013 * TRUE - frame was painted
9014 * FALSE - call default window proc
9016 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
9018 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
9019 HDC dc;
9020 RECT r;
9021 HRGN cliprgn;
9022 int cxEdge = GetSystemMetrics (SM_CXEDGE),
9023 cyEdge = GetSystemMetrics (SM_CYEDGE);
9025 if (!theme) return FALSE;
9027 GetWindowRect(infoPtr->hwndSelf, &r);
9029 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
9030 r.right - cxEdge, r.bottom - cyEdge);
9031 if (region != (HRGN)1)
9032 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
9033 OffsetRect(&r, -r.left, -r.top);
9035 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
9036 OffsetRect(&r, -r.left, -r.top);
9038 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
9039 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
9040 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
9041 ReleaseDC(infoPtr->hwndSelf, dc);
9043 /* Call default proc to get the scrollbars etc. painted */
9044 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
9046 return TRUE;
9049 /***
9050 * DESCRIPTION:
9051 * Determines the type of structure to use.
9053 * PARAMETER(S):
9054 * [I] infoPtr : valid pointer to the listview structureof the sender
9055 * [I] hwndFrom : listview window handle
9056 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
9058 * RETURN:
9059 * Zero
9061 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
9063 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
9065 if (nCommand == NF_REQUERY)
9066 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
9068 return infoPtr->notifyFormat;
9071 /***
9072 * DESCRIPTION:
9073 * Paints/Repaints the listview control.
9075 * PARAMETER(S):
9076 * [I] infoPtr : valid pointer to the listview structure
9077 * [I] hdc : device context handle
9079 * RETURN:
9080 * Zero
9082 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
9084 TRACE("(hdc=%p)\n", hdc);
9086 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
9088 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9090 infoPtr->bNoItemMetrics = FALSE;
9091 LISTVIEW_UpdateItemSize(infoPtr);
9092 if (uView == LVS_ICON || uView == LVS_SMALLICON)
9093 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9094 LISTVIEW_UpdateScroll(infoPtr);
9097 UpdateWindow(infoPtr->hwndHeader);
9099 if (hdc)
9100 LISTVIEW_Refresh(infoPtr, hdc, NULL);
9101 else
9103 PAINTSTRUCT ps;
9105 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
9106 if (!hdc) return 1;
9107 LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
9108 EndPaint(infoPtr->hwndSelf, &ps);
9111 return 0;
9115 /***
9116 * DESCRIPTION:
9117 * Paints/Repaints the listview control.
9119 * PARAMETER(S):
9120 * [I] infoPtr : valid pointer to the listview structure
9121 * [I] hdc : device context handle
9122 * [I] options : drawing options
9124 * RETURN:
9125 * Zero
9127 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
9129 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
9131 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
9132 return 0;
9134 if (options & PRF_ERASEBKGND)
9135 LISTVIEW_EraseBkgnd(infoPtr, hdc);
9137 if (options & PRF_CLIENT)
9138 LISTVIEW_Paint(infoPtr, hdc);
9140 return 0;
9144 /***
9145 * DESCRIPTION:
9146 * Processes double click messages (right mouse button).
9148 * PARAMETER(S):
9149 * [I] infoPtr : valid pointer to the listview structure
9150 * [I] wKey : key flag
9151 * [I] x,y : mouse coordinate
9153 * RETURN:
9154 * Zero
9156 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9158 LVHITTESTINFO lvHitTestInfo;
9160 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9162 /* send NM_RELEASEDCAPTURE notification */
9163 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9165 /* send NM_RDBLCLK notification */
9166 lvHitTestInfo.pt.x = x;
9167 lvHitTestInfo.pt.y = y;
9168 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9169 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
9171 return 0;
9174 /***
9175 * DESCRIPTION:
9176 * Processes mouse down messages (right mouse button).
9178 * PARAMETER(S):
9179 * [I] infoPtr : valid pointer to the listview structure
9180 * [I] wKey : key flag
9181 * [I] x,y : mouse coordinate
9183 * RETURN:
9184 * Zero
9186 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9188 LVHITTESTINFO lvHitTestInfo;
9189 INT nItem;
9191 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9193 /* send NM_RELEASEDCAPTURE notification */
9194 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9196 /* make sure the listview control window has the focus */
9197 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
9199 /* set right button down flag */
9200 infoPtr->bRButtonDown = TRUE;
9202 /* determine the index of the selected item */
9203 lvHitTestInfo.pt.x = x;
9204 lvHitTestInfo.pt.y = y;
9205 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
9207 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9209 LISTVIEW_SetItemFocus(infoPtr, nItem);
9210 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9211 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9212 LISTVIEW_SetSelection(infoPtr, nItem);
9214 else
9216 LISTVIEW_DeselectAll(infoPtr);
9219 return 0;
9222 /***
9223 * DESCRIPTION:
9224 * Processes mouse up messages (right mouse button).
9226 * PARAMETER(S):
9227 * [I] infoPtr : valid pointer to the listview structure
9228 * [I] wKey : key flag
9229 * [I] x,y : mouse coordinate
9231 * RETURN:
9232 * Zero
9234 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9236 LVHITTESTINFO lvHitTestInfo;
9237 POINT pt;
9239 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9241 if (!infoPtr->bRButtonDown) return 0;
9243 /* set button flag */
9244 infoPtr->bRButtonDown = FALSE;
9246 /* Send NM_RClICK notification */
9247 lvHitTestInfo.pt.x = x;
9248 lvHitTestInfo.pt.y = y;
9249 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9250 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
9252 /* Change to screen coordinate for WM_CONTEXTMENU */
9253 pt = lvHitTestInfo.pt;
9254 ClientToScreen(infoPtr->hwndSelf, &pt);
9256 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9257 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
9258 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
9260 return 0;
9264 /***
9265 * DESCRIPTION:
9266 * Sets the cursor.
9268 * PARAMETER(S):
9269 * [I] infoPtr : valid pointer to the listview structure
9270 * [I] hwnd : window handle of window containing the cursor
9271 * [I] nHittest : hit-test code
9272 * [I] wMouseMsg : ideintifier of the mouse message
9274 * RETURN:
9275 * TRUE if cursor is set
9276 * FALSE otherwise
9278 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
9280 LVHITTESTINFO lvHitTestInfo;
9282 if(!(LISTVIEW_isHotTracking(infoPtr))) return FALSE;
9284 if(!infoPtr->hHotCursor) return FALSE;
9286 GetCursorPos(&lvHitTestInfo.pt);
9287 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
9289 SetCursor(infoPtr->hHotCursor);
9291 return TRUE;
9294 /***
9295 * DESCRIPTION:
9296 * Sets the focus.
9298 * PARAMETER(S):
9299 * [I] infoPtr : valid pointer to the listview structure
9300 * [I] hwndLoseFocus : handle of previously focused window
9302 * RETURN:
9303 * Zero
9305 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
9307 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
9309 /* if we have the focus already, there's nothing to do */
9310 if (infoPtr->bFocus) return 0;
9312 /* send NM_SETFOCUS notification */
9313 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
9315 /* set window focus flag */
9316 infoPtr->bFocus = TRUE;
9318 /* put the focus rect back on */
9319 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
9321 /* redraw all visible selected items */
9322 LISTVIEW_InvalidateSelectedItems(infoPtr);
9324 return 0;
9327 /***
9328 * DESCRIPTION:
9329 * Sets the font.
9331 * PARAMETER(S):
9332 * [I] infoPtr : valid pointer to the listview structure
9333 * [I] fRedraw : font handle
9334 * [I] fRedraw : redraw flag
9336 * RETURN:
9337 * Zero
9339 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
9341 HFONT oldFont = infoPtr->hFont;
9343 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
9345 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9346 if (infoPtr->hFont == oldFont) return 0;
9348 LISTVIEW_SaveTextMetrics(infoPtr);
9350 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
9352 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
9353 LISTVIEW_UpdateSize(infoPtr);
9354 LISTVIEW_UpdateScroll(infoPtr);
9357 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
9359 return 0;
9362 /***
9363 * DESCRIPTION:
9364 * Message handling for WM_SETREDRAW.
9365 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9367 * PARAMETER(S):
9368 * [I] infoPtr : valid pointer to the listview structure
9369 * [I] bRedraw: state of redraw flag
9371 * RETURN:
9372 * DefWinProc return value
9374 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
9376 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
9378 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9379 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
9381 infoPtr->bRedraw = bRedraw;
9383 if(!bRedraw) return 0;
9385 if (is_autoarrange(infoPtr))
9386 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9387 LISTVIEW_UpdateScroll(infoPtr);
9389 /* despite what the WM_SETREDRAW docs says, apps expect us
9390 * to invalidate the listview here... stupid! */
9391 LISTVIEW_InvalidateList(infoPtr);
9393 return 0;
9396 /***
9397 * DESCRIPTION:
9398 * Resizes the listview control. This function processes WM_SIZE
9399 * messages. At this time, the width and height are not used.
9401 * PARAMETER(S):
9402 * [I] infoPtr : valid pointer to the listview structure
9403 * [I] Width : new width
9404 * [I] Height : new height
9406 * RETURN:
9407 * Zero
9409 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
9411 RECT rcOld = infoPtr->rcList;
9413 TRACE("(width=%d, height=%d)\n", Width, Height);
9415 LISTVIEW_UpdateSize(infoPtr);
9416 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
9418 /* do not bother with display related stuff if we're not redrawing */
9419 if (!is_redrawing(infoPtr)) return 0;
9421 if (is_autoarrange(infoPtr))
9422 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9424 LISTVIEW_UpdateScroll(infoPtr);
9426 /* refresh all only for lists whose height changed significantly */
9427 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
9428 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
9429 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
9430 LISTVIEW_InvalidateList(infoPtr);
9432 return 0;
9435 /***
9436 * DESCRIPTION:
9437 * Sets the size information.
9439 * PARAMETER(S):
9440 * [I] infoPtr : valid pointer to the listview structure
9442 * RETURN:
9443 * None
9445 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
9447 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9449 TRACE("uView=%d, rcList(old)=%s\n", uView, wine_dbgstr_rect(&infoPtr->rcList));
9451 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
9453 if (uView == LVS_LIST)
9455 /* Apparently the "LIST" style is supposed to have the same
9456 * number of items in a column even if there is no scroll bar.
9457 * Since if a scroll bar already exists then the bottom is already
9458 * reduced, only reduce if the scroll bar does not currently exist.
9459 * The "2" is there to mimic the native control. I think it may be
9460 * related to either padding or edges. (GLA 7/2002)
9462 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
9463 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
9464 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
9466 else if (uView == LVS_REPORT)
9468 HDLAYOUT hl;
9469 WINDOWPOS wp;
9471 hl.prc = &infoPtr->rcList;
9472 hl.pwpos = &wp;
9473 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9474 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp.flags, wp.x, wp.y, wp.cx, wp.cy);
9475 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy,
9476 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9477 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9478 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy);
9480 infoPtr->rcList.top = max(wp.cy, 0);
9481 infoPtr->rcList.top += (infoPtr->dwLvExStyle & LVS_EX_GRIDLINES) ? 2 : 0;
9484 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
9487 /***
9488 * DESCRIPTION:
9489 * Processes WM_STYLECHANGED messages.
9491 * PARAMETER(S):
9492 * [I] infoPtr : valid pointer to the listview structure
9493 * [I] wStyleType : window style type (normal or extended)
9494 * [I] lpss : window style information
9496 * RETURN:
9497 * Zero
9499 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9500 const STYLESTRUCT *lpss)
9502 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9503 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9504 UINT style;
9506 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9507 wStyleType, lpss->styleOld, lpss->styleNew);
9509 if (wStyleType != GWL_STYLE) return 0;
9511 /* FIXME: if LVS_NOSORTHEADER changed, update header */
9512 /* what if LVS_OWNERDATA changed? */
9513 /* or LVS_SINGLESEL */
9514 /* or LVS_SORT{AS,DES}CENDING */
9516 infoPtr->dwStyle = lpss->styleNew;
9518 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9519 ((lpss->styleNew & WS_HSCROLL) == 0))
9520 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9522 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9523 ((lpss->styleNew & WS_VSCROLL) == 0))
9524 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9526 if (uNewView != uOldView)
9528 SIZE oldIconSize = infoPtr->iconSize;
9529 HIMAGELIST himl;
9531 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9532 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9534 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9535 SetRectEmpty(&infoPtr->rcFocus);
9537 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9538 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
9540 if (uNewView == LVS_ICON)
9542 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
9544 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9545 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9546 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
9549 else if (uNewView == LVS_REPORT)
9551 HDLAYOUT hl;
9552 WINDOWPOS wp;
9554 hl.prc = &infoPtr->rcList;
9555 hl.pwpos = &wp;
9556 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9557 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
9558 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9559 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9562 LISTVIEW_UpdateItemSize(infoPtr);
9565 if (uNewView == LVS_REPORT)
9567 if ((lpss->styleOld ^ lpss->styleNew) & LVS_NOCOLUMNHEADER)
9569 if (lpss->styleNew & LVS_NOCOLUMNHEADER)
9571 /* Turn off the header control */
9572 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
9573 TRACE("Hide header control, was 0x%08x\n", style);
9574 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style | HDS_HIDDEN);
9575 } else {
9576 /* Turn on the header control */
9577 if ((style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE)) & HDS_HIDDEN)
9579 TRACE("Show header control, was 0x%08x\n", style);
9580 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, (style & ~HDS_HIDDEN) | WS_VISIBLE);
9586 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9587 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9588 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9590 /* update the size of the client area */
9591 LISTVIEW_UpdateSize(infoPtr);
9593 /* add scrollbars if needed */
9594 LISTVIEW_UpdateScroll(infoPtr);
9596 /* invalidate client area + erase background */
9597 LISTVIEW_InvalidateList(infoPtr);
9599 return 0;
9602 /***
9603 * DESCRIPTION:
9604 * Window procedure of the listview control.
9607 static LRESULT WINAPI
9608 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9610 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9612 TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg, wParam, lParam);
9614 if (!infoPtr && (uMsg != WM_NCCREATE))
9615 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9617 switch (uMsg)
9619 case LVM_APPROXIMATEVIEWRECT:
9620 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9621 LOWORD(lParam), HIWORD(lParam));
9622 case LVM_ARRANGE:
9623 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9625 /* case LVM_CANCELEDITLABEL: */
9627 case LVM_CREATEDRAGIMAGE:
9628 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9630 case LVM_DELETEALLITEMS:
9631 return LISTVIEW_DeleteAllItems(infoPtr, FALSE);
9633 case LVM_DELETECOLUMN:
9634 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9636 case LVM_DELETEITEM:
9637 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9639 case LVM_EDITLABELW:
9640 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9642 case LVM_EDITLABELA:
9643 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9645 /* case LVM_ENABLEGROUPVIEW: */
9647 case LVM_ENSUREVISIBLE:
9648 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9650 case LVM_FINDITEMW:
9651 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9653 case LVM_FINDITEMA:
9654 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9656 case LVM_GETBKCOLOR:
9657 return infoPtr->clrBk;
9659 /* case LVM_GETBKIMAGE: */
9661 case LVM_GETCALLBACKMASK:
9662 return infoPtr->uCallbackMask;
9664 case LVM_GETCOLUMNA:
9665 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9667 case LVM_GETCOLUMNW:
9668 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9670 case LVM_GETCOLUMNORDERARRAY:
9671 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9673 case LVM_GETCOLUMNWIDTH:
9674 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9676 case LVM_GETCOUNTPERPAGE:
9677 return LISTVIEW_GetCountPerPage(infoPtr);
9679 case LVM_GETEDITCONTROL:
9680 return (LRESULT)infoPtr->hwndEdit;
9682 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9683 return infoPtr->dwLvExStyle;
9685 /* case LVM_GETGROUPINFO: */
9687 /* case LVM_GETGROUPMETRICS: */
9689 case LVM_GETHEADER:
9690 return (LRESULT)infoPtr->hwndHeader;
9692 case LVM_GETHOTCURSOR:
9693 return (LRESULT)infoPtr->hHotCursor;
9695 case LVM_GETHOTITEM:
9696 return infoPtr->nHotItem;
9698 case LVM_GETHOVERTIME:
9699 return infoPtr->dwHoverTime;
9701 case LVM_GETIMAGELIST:
9702 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9704 /* case LVM_GETINSERTMARK: */
9706 /* case LVM_GETINSERTMARKCOLOR: */
9708 /* case LVM_GETINSERTMARKRECT: */
9710 case LVM_GETISEARCHSTRINGA:
9711 case LVM_GETISEARCHSTRINGW:
9712 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9713 return FALSE;
9715 case LVM_GETITEMA:
9716 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9718 case LVM_GETITEMW:
9719 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9721 case LVM_GETITEMCOUNT:
9722 return infoPtr->nItemCount;
9724 case LVM_GETITEMPOSITION:
9725 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9727 case LVM_GETITEMRECT:
9728 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9730 case LVM_GETITEMSPACING:
9731 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9733 case LVM_GETITEMSTATE:
9734 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9736 case LVM_GETITEMTEXTA:
9737 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9739 case LVM_GETITEMTEXTW:
9740 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9742 case LVM_GETNEXTITEM:
9743 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9745 case LVM_GETNUMBEROFWORKAREAS:
9746 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9747 return 1;
9749 case LVM_GETORIGIN:
9750 if (!lParam) return FALSE;
9751 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT ||
9752 (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST) return FALSE;
9753 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9754 return TRUE;
9756 /* case LVM_GETOUTLINECOLOR: */
9758 /* case LVM_GETSELECTEDCOLUMN: */
9760 case LVM_GETSELECTEDCOUNT:
9761 return LISTVIEW_GetSelectedCount(infoPtr);
9763 case LVM_GETSELECTIONMARK:
9764 return infoPtr->nSelectionMark;
9766 case LVM_GETSTRINGWIDTHA:
9767 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9769 case LVM_GETSTRINGWIDTHW:
9770 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9772 case LVM_GETSUBITEMRECT:
9773 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
9775 case LVM_GETTEXTBKCOLOR:
9776 return infoPtr->clrTextBk;
9778 case LVM_GETTEXTCOLOR:
9779 return infoPtr->clrText;
9781 /* case LVM_GETTILEINFO: */
9783 /* case LVM_GETTILEVIEWINFO: */
9785 case LVM_GETTOOLTIPS:
9786 if( !infoPtr->hwndToolTip )
9787 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9788 return (LRESULT)infoPtr->hwndToolTip;
9790 case LVM_GETTOPINDEX:
9791 return LISTVIEW_GetTopIndex(infoPtr);
9793 case LVM_GETUNICODEFORMAT:
9794 return (infoPtr->notifyFormat == NFR_UNICODE);
9796 /* case LVM_GETVIEW: */
9798 case LVM_GETVIEWRECT:
9799 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9801 case LVM_GETWORKAREAS:
9802 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9803 return FALSE;
9805 /* case LVM_HASGROUP: */
9807 case LVM_HITTEST:
9808 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9810 case LVM_INSERTCOLUMNA:
9811 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9813 case LVM_INSERTCOLUMNW:
9814 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9816 /* case LVM_INSERTGROUP: */
9818 /* case LVM_INSERTGROUPSORTED: */
9820 case LVM_INSERTITEMA:
9821 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9823 case LVM_INSERTITEMW:
9824 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9826 /* case LVM_INSERTMARKHITTEST: */
9828 /* case LVM_ISGROUPVIEWENABLED: */
9830 /* case LVM_MAPIDTOINDEX: */
9832 /* case LVM_MAPINDEXTOID: */
9834 /* case LVM_MOVEGROUP: */
9836 /* case LVM_MOVEITEMTOGROUP: */
9838 case LVM_REDRAWITEMS:
9839 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9841 /* case LVM_REMOVEALLGROUPS: */
9843 /* case LVM_REMOVEGROUP: */
9845 case LVM_SCROLL:
9846 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9848 case LVM_SETBKCOLOR:
9849 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9851 /* case LVM_SETBKIMAGE: */
9853 case LVM_SETCALLBACKMASK:
9854 infoPtr->uCallbackMask = (UINT)wParam;
9855 return TRUE;
9857 case LVM_SETCOLUMNA:
9858 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9860 case LVM_SETCOLUMNW:
9861 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9863 case LVM_SETCOLUMNORDERARRAY:
9864 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9866 case LVM_SETCOLUMNWIDTH:
9867 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9869 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9870 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9872 /* case LVM_SETGROUPINFO: */
9874 /* case LVM_SETGROUPMETRICS: */
9876 case LVM_SETHOTCURSOR:
9877 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9879 case LVM_SETHOTITEM:
9880 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9882 case LVM_SETHOVERTIME:
9883 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9885 case LVM_SETICONSPACING:
9886 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9888 case LVM_SETIMAGELIST:
9889 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9891 /* case LVM_SETINFOTIP: */
9893 /* case LVM_SETINSERTMARK: */
9895 /* case LVM_SETINSERTMARKCOLOR: */
9897 case LVM_SETITEMA:
9898 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9900 case LVM_SETITEMW:
9901 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9903 case LVM_SETITEMCOUNT:
9904 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9906 case LVM_SETITEMPOSITION:
9908 POINT pt;
9909 pt.x = (short)LOWORD(lParam);
9910 pt.y = (short)HIWORD(lParam);
9911 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9914 case LVM_SETITEMPOSITION32:
9915 if (lParam == 0) return FALSE;
9916 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9918 case LVM_SETITEMSTATE:
9919 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9921 case LVM_SETITEMTEXTA:
9922 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9924 case LVM_SETITEMTEXTW:
9925 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9927 /* case LVM_SETOUTLINECOLOR: */
9929 /* case LVM_SETSELECTEDCOLUMN: */
9931 case LVM_SETSELECTIONMARK:
9932 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9934 case LVM_SETTEXTBKCOLOR:
9935 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9937 case LVM_SETTEXTCOLOR:
9938 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9940 /* case LVM_SETTILEINFO: */
9942 /* case LVM_SETTILEVIEWINFO: */
9944 /* case LVM_SETTILEWIDTH: */
9946 case LVM_SETTOOLTIPS:
9947 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9949 case LVM_SETUNICODEFORMAT:
9950 return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
9952 /* case LVM_SETVIEW: */
9954 /* case LVM_SETWORKAREAS: */
9956 /* case LVM_SORTGROUPS: */
9958 case LVM_SORTITEMS:
9959 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9961 /* LVM_SORTITEMSEX: */
9963 case LVM_SUBITEMHITTEST:
9964 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9966 case LVM_UPDATE:
9967 return LISTVIEW_Update(infoPtr, (INT)wParam);
9969 case WM_CHAR:
9970 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9972 case WM_COMMAND:
9973 return LISTVIEW_Command(infoPtr, wParam, lParam);
9975 case WM_NCCREATE:
9976 return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
9978 case WM_CREATE:
9979 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9981 case WM_DESTROY:
9982 return LISTVIEW_Destroy(infoPtr);
9984 case WM_ENABLE:
9985 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
9987 case WM_ERASEBKGND:
9988 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9990 case WM_GETDLGCODE:
9991 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9993 case WM_GETFONT:
9994 return (LRESULT)infoPtr->hFont;
9996 case WM_HSCROLL:
9997 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9999 case WM_KEYDOWN:
10000 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
10002 case WM_KILLFOCUS:
10003 return LISTVIEW_KillFocus(infoPtr);
10005 case WM_LBUTTONDBLCLK:
10006 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10008 case WM_LBUTTONDOWN:
10009 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10011 case WM_LBUTTONUP:
10012 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10014 case WM_MOUSEMOVE:
10015 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10017 case WM_MOUSEHOVER:
10018 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10020 case WM_NCDESTROY:
10021 return LISTVIEW_NCDestroy(infoPtr);
10023 case WM_NCPAINT:
10024 if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
10025 return 0;
10026 goto fwd_msg;
10028 case WM_NOTIFY:
10029 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
10030 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
10031 else return 0;
10033 case WM_NOTIFYFORMAT:
10034 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
10036 case WM_PRINTCLIENT:
10037 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
10039 case WM_PAINT:
10040 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
10042 case WM_RBUTTONDBLCLK:
10043 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10045 case WM_RBUTTONDOWN:
10046 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10048 case WM_RBUTTONUP:
10049 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10051 case WM_SETCURSOR:
10052 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
10053 return TRUE;
10054 goto fwd_msg;
10056 case WM_SETFOCUS:
10057 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
10059 case WM_SETFONT:
10060 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
10062 case WM_SETREDRAW:
10063 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
10065 case WM_SIZE:
10066 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10068 case WM_STYLECHANGED:
10069 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10071 case WM_SYSCOLORCHANGE:
10072 COMCTL32_RefreshSysColors();
10073 return 0;
10075 /* case WM_TIMER: */
10076 case WM_THEMECHANGED:
10077 return LISTVIEW_ThemeChanged(infoPtr);
10079 case WM_VSCROLL:
10080 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10082 case WM_MOUSEWHEEL:
10083 if (wParam & (MK_SHIFT | MK_CONTROL))
10084 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10085 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
10087 case WM_WINDOWPOSCHANGED:
10088 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
10090 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
10091 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
10092 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
10094 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
10096 MEASUREITEMSTRUCT mis;
10097 mis.CtlType = ODT_LISTVIEW;
10098 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
10099 mis.itemID = -1;
10100 mis.itemWidth = 0;
10101 mis.itemData = 0;
10102 mis.itemHeight= infoPtr->nItemHeight;
10103 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
10104 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
10105 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
10108 LISTVIEW_UpdateSize(infoPtr);
10109 LISTVIEW_UpdateScroll(infoPtr);
10111 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10113 /* case WM_WININICHANGE: */
10115 default:
10116 if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
10117 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
10119 fwd_msg:
10120 /* call default window procedure */
10121 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10126 /***
10127 * DESCRIPTION:
10128 * Registers the window class.
10130 * PARAMETER(S):
10131 * None
10133 * RETURN:
10134 * None
10136 void LISTVIEW_Register(void)
10138 WNDCLASSW wndClass;
10140 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
10141 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
10142 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
10143 wndClass.cbClsExtra = 0;
10144 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
10145 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
10146 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
10147 wndClass.lpszClassName = WC_LISTVIEWW;
10148 RegisterClassW(&wndClass);
10151 /***
10152 * DESCRIPTION:
10153 * Unregisters the window class.
10155 * PARAMETER(S):
10156 * None
10158 * RETURN:
10159 * None
10161 void LISTVIEW_Unregister(void)
10163 UnregisterClassW(WC_LISTVIEWW, NULL);
10166 /***
10167 * DESCRIPTION:
10168 * Handle any WM_COMMAND messages
10170 * PARAMETER(S):
10171 * [I] infoPtr : valid pointer to the listview structure
10172 * [I] wParam : the first message parameter
10173 * [I] lParam : the second message parameter
10175 * RETURN:
10176 * Zero.
10178 static LRESULT LISTVIEW_Command(const LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
10180 switch (HIWORD(wParam))
10182 case EN_UPDATE:
10185 * Adjust the edit window size
10187 WCHAR buffer[1024];
10188 HDC hdc = GetDC(infoPtr->hwndEdit);
10189 HFONT hFont, hOldFont = 0;
10190 RECT rect;
10191 SIZE sz;
10193 if (!infoPtr->hwndEdit || !hdc) return 0;
10194 GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
10195 GetWindowRect(infoPtr->hwndEdit, &rect);
10197 /* Select font to get the right dimension of the string */
10198 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
10199 if(hFont != 0)
10201 hOldFont = SelectObject(hdc, hFont);
10204 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
10206 TEXTMETRICW textMetric;
10208 /* Add Extra spacing for the next character */
10209 GetTextMetricsW(hdc, &textMetric);
10210 sz.cx += (textMetric.tmMaxCharWidth * 2);
10212 SetWindowPos (
10213 infoPtr->hwndEdit,
10214 HWND_TOP,
10217 sz.cx,
10218 rect.bottom - rect.top,
10219 SWP_DRAWFRAME|SWP_NOMOVE);
10221 if(hFont != 0)
10222 SelectObject(hdc, hOldFont);
10224 ReleaseDC(infoPtr->hwndEdit, hdc);
10226 break;
10229 default:
10230 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
10233 return 0;
10237 /***
10238 * DESCRIPTION:
10239 * Subclassed edit control windproc function
10241 * PARAMETER(S):
10242 * [I] hwnd : the edit window handle
10243 * [I] uMsg : the message that is to be processed
10244 * [I] wParam : first message parameter
10245 * [I] lParam : second message parameter
10246 * [I] isW : TRUE if input is Unicode
10248 * RETURN:
10249 * Zero.
10251 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
10253 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
10254 BOOL cancel = FALSE;
10256 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10257 hwnd, uMsg, wParam, lParam, isW);
10259 switch (uMsg)
10261 case WM_GETDLGCODE:
10262 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
10264 case WM_KILLFOCUS:
10265 break;
10267 case WM_DESTROY:
10269 WNDPROC editProc = infoPtr->EditWndProc;
10270 infoPtr->EditWndProc = 0;
10271 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
10272 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
10275 case WM_KEYDOWN:
10276 if (VK_ESCAPE == (INT)wParam)
10278 cancel = TRUE;
10279 break;
10281 else if (VK_RETURN == (INT)wParam)
10282 break;
10284 default:
10285 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
10288 /* kill the edit */
10289 if (infoPtr->hwndEdit)
10291 LPWSTR buffer = NULL;
10293 infoPtr->hwndEdit = 0;
10294 if (!cancel)
10296 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
10298 if (len)
10300 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
10302 if (isW) GetWindowTextW(hwnd, buffer, len+1);
10303 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
10307 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
10309 Free(buffer);
10312 SendMessageW(hwnd, WM_CLOSE, 0, 0);
10313 return 0;
10316 /***
10317 * DESCRIPTION:
10318 * Subclassed edit control Unicode windproc function
10320 * PARAMETER(S):
10321 * [I] hwnd : the edit window handle
10322 * [I] uMsg : the message that is to be processed
10323 * [I] wParam : first message parameter
10324 * [I] lParam : second message parameter
10326 * RETURN:
10328 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10330 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
10333 /***
10334 * DESCRIPTION:
10335 * Subclassed edit control ANSI windproc function
10337 * PARAMETER(S):
10338 * [I] hwnd : the edit window handle
10339 * [I] uMsg : the message that is to be processed
10340 * [I] wParam : first message parameter
10341 * [I] lParam : second message parameter
10343 * RETURN:
10345 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10347 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
10350 /***
10351 * DESCRIPTION:
10352 * Creates a subclassed edit control
10354 * PARAMETER(S):
10355 * [I] infoPtr : valid pointer to the listview structure
10356 * [I] text : initial text for the edit
10357 * [I] style : the window style
10358 * [I] isW : TRUE if input is Unicode
10360 * RETURN:
10362 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
10363 INT x, INT y, INT width, INT height, BOOL isW)
10365 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
10366 HWND hedit;
10367 SIZE sz;
10368 HDC hdc;
10369 HDC hOldFont=0;
10370 TEXTMETRICW textMetric;
10371 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
10373 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
10375 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
10376 hdc = GetDC(infoPtr->hwndSelf);
10378 /* Select the font to get appropriate metric dimensions */
10379 if(infoPtr->hFont != 0)
10380 hOldFont = SelectObject(hdc, infoPtr->hFont);
10382 /*Get String Length in pixels */
10383 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
10385 /*Add Extra spacing for the next character */
10386 GetTextMetricsW(hdc, &textMetric);
10387 sz.cx += (textMetric.tmMaxCharWidth * 2);
10389 if(infoPtr->hFont != 0)
10390 SelectObject(hdc, hOldFont);
10392 ReleaseDC(infoPtr->hwndSelf, hdc);
10393 if (isW)
10394 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10395 else
10396 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10398 if (!hedit) return 0;
10400 infoPtr->EditWndProc = (WNDPROC)
10401 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
10402 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
10404 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
10406 return hedit;