comctl32: Do not send LVN_DELETEITEM on LVM_DELETEALLITEMS for virtual listviews.
[wine/hacks.git] / dlls / comctl32 / listview.c
blob6724139f7df472d61d11402c0bda3da674533fdf
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);
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);
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);
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
3224 * RETURN:
3225 * SUCCESS : TRUE (needs to be repainted)
3226 * FAILURE : FALSE (nothing has changed)
3228 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3230 /* FIXME: pass in the state */
3231 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3232 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3233 BOOL bResult = FALSE;
3235 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3236 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3238 if (infoPtr->dwStyle & LVS_SINGLESEL)
3240 bResult = TRUE;
3241 LISTVIEW_SetSelection(infoPtr, nItem);
3243 else
3245 if (wShift)
3247 bResult = TRUE;
3248 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3250 else if (wCtrl)
3252 LVITEMW lvItem;
3253 lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3254 lvItem.stateMask = LVIS_SELECTED;
3255 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3257 if (lvItem.state & LVIS_SELECTED)
3258 infoPtr->nSelectionMark = nItem;
3260 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3262 else
3264 bResult = TRUE;
3265 LISTVIEW_SetSelection(infoPtr, nItem);
3268 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3271 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3272 return bResult;
3275 static BOOL LISTVIEW_GetItemAtPt(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3277 LVHITTESTINFO lvHitTestInfo;
3279 ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3280 lvHitTestInfo.pt.x = pt.x;
3281 lvHitTestInfo.pt.y = pt.y;
3283 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3285 lpLVItem->mask = LVIF_PARAM;
3286 lpLVItem->iItem = lvHitTestInfo.iItem;
3287 lpLVItem->iSubItem = 0;
3289 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3292 static inline BOOL LISTVIEW_isHotTracking(const LISTVIEW_INFO *infoPtr)
3294 return ((infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) ||
3295 (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE) ||
3296 (infoPtr->dwLvExStyle & LVS_EX_TWOCLICKACTIVATE));
3299 /***
3300 * DESCRIPTION:
3301 * Called when the mouse is being actively tracked and has hovered for a specified
3302 * amount of time
3304 * PARAMETER(S):
3305 * [I] infoPtr : valid pointer to the listview structure
3306 * [I] fwKeys : key indicator
3307 * [I] x,y : mouse position
3309 * RETURN:
3310 * 0 if the message was processed, non-zero if there was an error
3312 * INFO:
3313 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3314 * over the item for a certain period of time.
3317 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3319 if (LISTVIEW_isHotTracking(infoPtr))
3321 LVITEMW item;
3322 POINT pt;
3324 pt.x = x;
3325 pt.y = y;
3327 if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3328 LISTVIEW_SetSelection(infoPtr, item.iItem);
3331 return 0;
3334 /***
3335 * DESCRIPTION:
3336 * Called whenever WM_MOUSEMOVE is received.
3338 * PARAMETER(S):
3339 * [I] infoPtr : valid pointer to the listview structure
3340 * [I] fwKeys : key indicator
3341 * [I] x,y : mouse position
3343 * RETURN:
3344 * 0 if the message is processed, non-zero if there was an error
3346 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3348 TRACKMOUSEEVENT trackinfo;
3350 if (!(fwKeys & MK_LBUTTON))
3351 infoPtr->bLButtonDown = FALSE;
3353 if (infoPtr->bLButtonDown)
3355 POINT tmp;
3356 RECT rect;
3357 WORD wDragWidth = GetSystemMetrics(SM_CXDRAG);
3358 WORD wDragHeight= GetSystemMetrics(SM_CYDRAG);
3360 rect.left = infoPtr->ptClickPos.x - wDragWidth;
3361 rect.right = infoPtr->ptClickPos.x + wDragWidth;
3362 rect.top = infoPtr->ptClickPos.y - wDragHeight;
3363 rect.bottom = infoPtr->ptClickPos.y + wDragHeight;
3365 tmp.x = x;
3366 tmp.y = y;
3368 if (!PtInRect(&rect, tmp))
3370 LVHITTESTINFO lvHitTestInfo;
3371 NMLISTVIEW nmlv;
3373 lvHitTestInfo.pt = infoPtr->ptClickPos;
3374 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3376 ZeroMemory(&nmlv, sizeof(nmlv));
3377 nmlv.iItem = lvHitTestInfo.iItem;
3378 nmlv.ptAction = infoPtr->ptClickPos;
3380 if (!infoPtr->bDragging)
3382 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3383 infoPtr->bDragging = TRUE;
3386 return 0;
3389 else
3390 infoPtr->bLButtonDown = FALSE;
3392 /* see if we are supposed to be tracking mouse hovering */
3393 if (LISTVIEW_isHotTracking(infoPtr)) {
3394 /* fill in the trackinfo struct */
3395 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3396 trackinfo.dwFlags = TME_QUERY;
3397 trackinfo.hwndTrack = infoPtr->hwndSelf;
3398 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3400 /* see if we are already tracking this hwnd */
3401 _TrackMouseEvent(&trackinfo);
3403 if(!(trackinfo.dwFlags & TME_HOVER)) {
3404 trackinfo.dwFlags = TME_HOVER;
3406 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3407 _TrackMouseEvent(&trackinfo);
3411 return 0;
3415 /***
3416 * Tests whether the item is assignable to a list with style lStyle
3418 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3420 if ( (lpLVItem->mask & LVIF_TEXT) &&
3421 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3422 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3424 return TRUE;
3428 /***
3429 * DESCRIPTION:
3430 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3432 * PARAMETER(S):
3433 * [I] infoPtr : valid pointer to the listview structure
3434 * [I] lpLVItem : valid pointer to new item attributes
3435 * [I] isNew : the item being set is being inserted
3436 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3437 * [O] bChanged : will be set to TRUE if the item really changed
3439 * RETURN:
3440 * SUCCESS : TRUE
3441 * FAILURE : FALSE
3443 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3445 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3446 ITEM_INFO *lpItem;
3447 NMLISTVIEW nmlv;
3448 UINT uChanged = 0;
3449 LVITEMW item;
3451 TRACE("()\n");
3453 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3455 if (lpLVItem->mask == 0) return TRUE;
3457 if (infoPtr->dwStyle & LVS_OWNERDATA)
3459 /* a virtual listview only stores selection and focus */
3460 if (lpLVItem->mask & ~LVIF_STATE)
3461 return FALSE;
3462 lpItem = NULL;
3464 else
3466 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3467 lpItem = DPA_GetPtr(hdpaSubItems, 0);
3468 assert (lpItem);
3471 /* we need to get the lParam and state of the item */
3472 item.iItem = lpLVItem->iItem;
3473 item.iSubItem = lpLVItem->iSubItem;
3474 item.mask = LVIF_STATE | LVIF_PARAM;
3475 item.stateMask = ~0;
3476 item.state = 0;
3477 item.lParam = 0;
3478 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3480 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3481 /* determine what fields will change */
3482 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3483 uChanged |= LVIF_STATE;
3485 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3486 uChanged |= LVIF_IMAGE;
3488 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3489 uChanged |= LVIF_PARAM;
3491 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3492 uChanged |= LVIF_INDENT;
3494 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3495 uChanged |= LVIF_TEXT;
3497 TRACE("uChanged=0x%x\n", uChanged);
3498 if (!uChanged) return TRUE;
3499 *bChanged = TRUE;
3501 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3502 nmlv.iItem = lpLVItem->iItem;
3503 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3504 nmlv.uOldState = item.state;
3505 nmlv.uChanged = uChanged;
3506 nmlv.lParam = item.lParam;
3508 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3509 /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
3510 /* are enabled */
3511 if(lpItem && !isNew && infoPtr->bDoChangeNotify)
3513 HWND hwndSelf = infoPtr->hwndSelf;
3515 if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3516 return FALSE;
3517 if (!IsWindow(hwndSelf))
3518 return FALSE;
3521 /* copy information */
3522 if (lpLVItem->mask & LVIF_TEXT)
3523 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3525 if (lpLVItem->mask & LVIF_IMAGE)
3526 lpItem->hdr.iImage = lpLVItem->iImage;
3528 if (lpLVItem->mask & LVIF_PARAM)
3529 lpItem->lParam = lpLVItem->lParam;
3531 if (lpLVItem->mask & LVIF_INDENT)
3532 lpItem->iIndent = lpLVItem->iIndent;
3534 if (uChanged & LVIF_STATE)
3536 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3538 lpItem->state &= ~lpLVItem->stateMask;
3539 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3541 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3543 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3544 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3546 else if (lpLVItem->stateMask & LVIS_SELECTED)
3547 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3549 /* if we are asked to change focus, and we manage it, do it */
3550 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3552 if (lpLVItem->state & LVIS_FOCUSED)
3554 LISTVIEW_SetItemFocus(infoPtr, -1);
3555 infoPtr->nFocusedItem = lpLVItem->iItem;
3556 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3558 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3559 infoPtr->nFocusedItem = -1;
3563 /* if we're inserting the item, we're done */
3564 if (isNew) return TRUE;
3566 /* send LVN_ITEMCHANGED notification */
3567 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3568 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3570 return TRUE;
3573 /***
3574 * DESCRIPTION:
3575 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3577 * PARAMETER(S):
3578 * [I] infoPtr : valid pointer to the listview structure
3579 * [I] lpLVItem : valid pointer to new subitem attributes
3580 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3581 * [O] bChanged : will be set to TRUE if the item really changed
3583 * RETURN:
3584 * SUCCESS : TRUE
3585 * FAILURE : FALSE
3587 static BOOL set_sub_item(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3589 HDPA hdpaSubItems;
3590 SUBITEM_INFO *lpSubItem;
3592 /* we do not support subitems for virtual listviews */
3593 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3595 /* set subitem only if column is present */
3596 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3598 /* First do some sanity checks */
3599 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
3600 particularly useful. We currently do not actually do anything with
3601 the flag on subitems.
3603 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)) return FALSE;
3604 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))) return TRUE;
3606 /* get the subitem structure, and create it if not there */
3607 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3608 assert (hdpaSubItems);
3610 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3611 if (!lpSubItem)
3613 SUBITEM_INFO *tmpSubItem;
3614 INT i;
3616 lpSubItem = Alloc(sizeof(SUBITEM_INFO));
3617 if (!lpSubItem) return FALSE;
3618 /* we could binary search here, if need be...*/
3619 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3621 tmpSubItem = DPA_GetPtr(hdpaSubItems, i);
3622 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3624 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3626 Free(lpSubItem);
3627 return FALSE;
3629 lpSubItem->iSubItem = lpLVItem->iSubItem;
3630 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3631 *bChanged = TRUE;
3634 if (lpLVItem->mask & LVIF_IMAGE)
3635 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3637 lpSubItem->hdr.iImage = lpLVItem->iImage;
3638 *bChanged = TRUE;
3641 if (lpLVItem->mask & LVIF_TEXT)
3642 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3644 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3645 *bChanged = TRUE;
3648 return TRUE;
3651 /***
3652 * DESCRIPTION:
3653 * Sets item attributes.
3655 * PARAMETER(S):
3656 * [I] infoPtr : valid pointer to the listview structure
3657 * [I] lpLVItem : new item attributes
3658 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3660 * RETURN:
3661 * SUCCESS : TRUE
3662 * FAILURE : FALSE
3664 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem, BOOL isW)
3666 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3667 HWND hwndSelf = infoPtr->hwndSelf;
3668 LPWSTR pszText = NULL;
3669 BOOL bResult, bChanged = FALSE;
3671 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3673 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3674 return FALSE;
3676 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3677 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3679 pszText = lpLVItem->pszText;
3680 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
3683 /* actually set the fields */
3684 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3686 if (lpLVItem->iSubItem)
3687 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3688 else
3689 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3690 if (!IsWindow(hwndSelf))
3691 return FALSE;
3693 /* redraw item, if necessary */
3694 if (bChanged && !infoPtr->bIsDrawing)
3696 /* this little optimization eliminates some nasty flicker */
3697 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3698 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
3699 lpLVItem->iSubItem > 0 && lpLVItem->iSubItem <= DPA_GetPtrCount(infoPtr->hdpaColumns) )
3700 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3701 else
3702 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3704 /* restore text */
3705 if (pszText)
3707 textfreeT(lpLVItem->pszText, isW);
3708 lpLVItem->pszText = pszText;
3711 return bResult;
3714 /***
3715 * DESCRIPTION:
3716 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3718 * PARAMETER(S):
3719 * [I] infoPtr : valid pointer to the listview structure
3721 * RETURN:
3722 * item index
3724 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *infoPtr)
3726 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3727 INT nItem = 0;
3728 SCROLLINFO scrollInfo;
3730 scrollInfo.cbSize = sizeof(SCROLLINFO);
3731 scrollInfo.fMask = SIF_POS;
3733 if (uView == LVS_LIST)
3735 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3736 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3738 else if (uView == LVS_REPORT)
3740 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3741 nItem = scrollInfo.nPos;
3743 else
3745 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3746 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3749 TRACE("nItem=%d\n", nItem);
3751 return nItem;
3755 /***
3756 * DESCRIPTION:
3757 * Erases the background of the given rectangle
3759 * PARAMETER(S):
3760 * [I] infoPtr : valid pointer to the listview structure
3761 * [I] hdc : device context handle
3762 * [I] lprcBox : clipping rectangle
3764 * RETURN:
3765 * Success: TRUE
3766 * Failure: FALSE
3768 static inline BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3770 if (!infoPtr->hBkBrush) return FALSE;
3772 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
3774 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3777 /***
3778 * DESCRIPTION:
3779 * Draws an item.
3781 * PARAMETER(S):
3782 * [I] infoPtr : valid pointer to the listview structure
3783 * [I] hdc : device context handle
3784 * [I] nItem : item index
3785 * [I] nSubItem : subitem index
3786 * [I] pos : item position in client coordinates
3787 * [I] cdmode : custom draw mode
3789 * RETURN:
3790 * Success: TRUE
3791 * Failure: FALSE
3793 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3795 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3796 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3797 static WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3798 DWORD cdsubitemmode = CDRF_DODEFAULT;
3799 LPRECT lprcFocus;
3800 RECT rcSelect, rcBox, rcIcon, rcLabel, rcStateIcon;
3801 NMLVCUSTOMDRAW nmlvcd;
3802 HIMAGELIST himl;
3803 LVITEMW lvItem;
3804 HFONT hOldFont;
3806 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
3808 /* get information needed for drawing the item */
3809 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
3810 if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
3811 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3812 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3813 lvItem.iItem = nItem;
3814 lvItem.iSubItem = nSubItem;
3815 lvItem.state = 0;
3816 lvItem.lParam = 0;
3817 lvItem.cchTextMax = DISP_TEXT_SIZE;
3818 lvItem.pszText = szDispText;
3819 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3820 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3821 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3822 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
3823 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3825 /* now check if we need to update the focus rectangle */
3826 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3828 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3829 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
3830 OffsetRect(&rcBox, pos.x, pos.y);
3831 OffsetRect(&rcSelect, pos.x, pos.y);
3832 OffsetRect(&rcIcon, pos.x, pos.y);
3833 OffsetRect(&rcStateIcon, pos.x, pos.y);
3834 OffsetRect(&rcLabel, pos.x, pos.y);
3835 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
3836 wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect),
3837 wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
3839 /* fill in the custom draw structure */
3840 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3842 hOldFont = GetCurrentObject(hdc, OBJ_FONT);
3843 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3844 if (cdmode & CDRF_SKIPDEFAULT) goto postpaint;
3845 if (cdmode & CDRF_NOTIFYITEMDRAW)
3846 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3847 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3848 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3849 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3850 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3852 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3853 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3855 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3856 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
3857 else if ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) == FALSE)
3858 prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE);
3860 /* in full row select, subitems, will just use main item's colors */
3861 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3862 nmlvcd.clrTextBk = CLR_NONE;
3864 /* state icons */
3865 if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && (nSubItem == 0))
3867 UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
3868 if (uStateImage)
3870 TRACE("uStateImage=%d\n", uStateImage);
3871 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
3872 rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
3876 /* small icons */
3877 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3878 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3880 TRACE("iImage=%d\n", lvItem.iImage);
3881 ImageList_DrawEx(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3882 rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk, CLR_DEFAULT,
3883 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3886 /* Don't bother painting item being edited */
3887 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3889 /* FIXME: temporary hack */
3890 rcSelect.left = rcLabel.left;
3892 /* draw the selection background, if we're drawing the main item */
3893 if (nSubItem == 0)
3895 /* in icon mode, the label rect is really what we want to draw the
3896 * background for */
3897 if (uView == LVS_ICON)
3898 rcSelect = rcLabel;
3900 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3901 rcSelect.right = rcBox.right;
3903 if (nmlvcd.clrTextBk != CLR_NONE)
3904 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3905 if(lprcFocus) *lprcFocus = rcSelect;
3908 /* figure out the text drawing flags */
3909 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3910 if (uView == LVS_ICON)
3911 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3912 else if (nSubItem)
3914 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3916 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3917 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3918 default: uFormat |= DT_LEFT;
3921 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3923 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3924 else rcLabel.left += LABEL_HOR_PADDING;
3926 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3928 /* for GRIDLINES reduce the bottom so the text formats correctly */
3929 if (uView == LVS_REPORT && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
3930 rcLabel.bottom--;
3932 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3934 postpaint:
3935 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3936 notify_postpaint(infoPtr, &nmlvcd);
3937 if (cdsubitemmode & CDRF_NEWFONT)
3938 SelectObject(hdc, hOldFont);
3939 return TRUE;
3942 /***
3943 * DESCRIPTION:
3944 * Draws listview items when in owner draw mode.
3946 * PARAMETER(S):
3947 * [I] infoPtr : valid pointer to the listview structure
3948 * [I] hdc : device context handle
3950 * RETURN:
3951 * None
3953 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3955 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3956 DWORD cditemmode = CDRF_DODEFAULT;
3957 NMLVCUSTOMDRAW nmlvcd;
3958 POINT Origin, Position;
3959 DRAWITEMSTRUCT dis;
3960 LVITEMW item;
3962 TRACE("()\n");
3964 ZeroMemory(&dis, sizeof(dis));
3966 /* Get scroll info once before loop */
3967 LISTVIEW_GetOrigin(infoPtr, &Origin);
3969 /* iterate through the invalidated rows */
3970 while(iterator_next(i))
3972 item.iItem = i->nItem;
3973 item.iSubItem = 0;
3974 item.mask = LVIF_PARAM | LVIF_STATE;
3975 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3976 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3978 dis.CtlType = ODT_LISTVIEW;
3979 dis.CtlID = uID;
3980 dis.itemID = item.iItem;
3981 dis.itemAction = ODA_DRAWENTIRE;
3982 dis.itemState = 0;
3983 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3984 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3985 dis.hwndItem = infoPtr->hwndSelf;
3986 dis.hDC = hdc;
3987 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3988 dis.rcItem.left = Position.x + Origin.x;
3989 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3990 dis.rcItem.top = Position.y + Origin.y;
3991 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3992 dis.itemData = item.lParam;
3994 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
3997 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3998 * structure for the rest. of the paint cycle
4000 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
4001 if (cdmode & CDRF_NOTIFYITEMDRAW)
4002 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4004 if (!(cditemmode & CDRF_SKIPDEFAULT))
4006 prepaint_setup (infoPtr, hdc, &nmlvcd, FALSE);
4007 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
4010 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
4011 notify_postpaint(infoPtr, &nmlvcd);
4015 /***
4016 * DESCRIPTION:
4017 * Draws listview items when in report display mode.
4019 * PARAMETER(S):
4020 * [I] infoPtr : valid pointer to the listview structure
4021 * [I] hdc : device context handle
4022 * [I] cdmode : custom draw mode
4024 * RETURN:
4025 * None
4027 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4029 INT rgntype;
4030 RECT rcClip, rcItem;
4031 POINT Origin, Position;
4032 RANGE colRange;
4033 ITERATOR j;
4035 TRACE("()\n");
4037 /* figure out what to draw */
4038 rgntype = GetClipBox(hdc, &rcClip);
4039 if (rgntype == NULLREGION) return;
4041 /* Get scroll info once before loop */
4042 LISTVIEW_GetOrigin(infoPtr, &Origin);
4044 /* narrow down the columns we need to paint */
4045 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
4047 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
4048 if (rcItem.right + Origin.x >= rcClip.left) break;
4050 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
4052 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
4053 if (rcItem.left + Origin.x < rcClip.right) break;
4055 iterator_rangeitems(&j, colRange);
4057 /* in full row select, we _have_ to draw the main item */
4058 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
4059 j.nSpecial = 0;
4061 /* iterate through the invalidated rows */
4062 while(iterator_next(i))
4064 /* iterate through the invalidated columns */
4065 while(iterator_next(&j))
4067 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4068 Position.x += Origin.x;
4069 Position.y += Origin.y;
4071 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
4073 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4074 rcItem.top = 0;
4075 rcItem.bottom = infoPtr->nItemHeight;
4076 OffsetRect(&rcItem, Position.x, Position.y);
4077 if (!RectVisible(hdc, &rcItem)) continue;
4080 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
4083 iterator_destroy(&j);
4086 /***
4087 * DESCRIPTION:
4088 * Draws the gridlines if necessary when in report display mode.
4090 * PARAMETER(S):
4091 * [I] infoPtr : valid pointer to the listview structure
4092 * [I] hdc : device context handle
4094 * RETURN:
4095 * None
4097 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO *infoPtr, HDC hdc)
4099 INT rgntype;
4100 INT y, itemheight;
4101 HPEN hPen, hOldPen;
4102 RECT rcClip, rcItem;
4103 POINT Origin;
4104 RANGE colRange;
4105 ITERATOR j;
4107 TRACE("()\n");
4109 /* figure out what to draw */
4110 rgntype = GetClipBox(hdc, &rcClip);
4111 if (rgntype == NULLREGION) return;
4113 /* Get scroll info once before loop */
4114 LISTVIEW_GetOrigin(infoPtr, &Origin);
4116 /* narrow down the columns we need to paint */
4117 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
4119 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
4120 if (rcItem.right + Origin.x >= rcClip.left) break;
4122 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
4124 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
4125 if (rcItem.left + Origin.x < rcClip.right) break;
4127 iterator_rangeitems(&j, colRange);
4129 if ((hPen = CreatePen( PS_SOLID, 1, comctl32_color.clr3dFace )))
4131 hOldPen = SelectObject ( hdc, hPen );
4133 /* draw the vertical lines for the columns */
4134 iterator_rangeitems(&j, colRange);
4135 while(iterator_next(&j))
4137 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4138 if (rcItem.left == 0) continue; /* skip first column */
4139 rcItem.left += Origin.x;
4140 rcItem.right += Origin.x;
4141 rcItem.top = infoPtr->rcList.top;
4142 rcItem.bottom = infoPtr->rcList.bottom;
4143 TRACE("vert col=%d, rcItem=%s\n", j.nItem, wine_dbgstr_rect(&rcItem));
4144 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4145 LineTo (hdc, rcItem.left, rcItem.bottom);
4147 iterator_destroy(&j);
4149 /* draw the horizontial lines for the rows */
4150 itemheight = LISTVIEW_CalculateItemHeight(infoPtr);
4151 rcItem.left = infoPtr->rcList.left + Origin.x;
4152 rcItem.right = infoPtr->rcList.right + Origin.x;
4153 rcItem.bottom = rcItem.top = Origin.y - 1;
4154 MoveToEx(hdc, rcItem.left, rcItem.top, NULL);
4155 LineTo(hdc, rcItem.right, rcItem.top);
4156 for(y=itemheight-1+Origin.y; y<=infoPtr->rcList.bottom; y+=itemheight)
4158 rcItem.bottom = rcItem.top = y;
4159 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem));
4160 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4161 LineTo (hdc, rcItem.right, rcItem.top);
4164 SelectObject( hdc, hOldPen );
4165 DeleteObject( hPen );
4169 /***
4170 * DESCRIPTION:
4171 * Draws listview items when in list display mode.
4173 * PARAMETER(S):
4174 * [I] infoPtr : valid pointer to the listview structure
4175 * [I] hdc : device context handle
4176 * [I] cdmode : custom draw mode
4178 * RETURN:
4179 * None
4181 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4183 POINT Origin, Position;
4185 /* Get scroll info once before loop */
4186 LISTVIEW_GetOrigin(infoPtr, &Origin);
4188 while(iterator_prev(i))
4190 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4191 Position.x += Origin.x;
4192 Position.y += Origin.y;
4194 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
4199 /***
4200 * DESCRIPTION:
4201 * Draws listview items.
4203 * PARAMETER(S):
4204 * [I] infoPtr : valid pointer to the listview structure
4205 * [I] hdc : device context handle
4206 * [I] prcErase : rect to be erased before refresh (may be NULL)
4208 * RETURN:
4209 * NoneX
4211 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcErase)
4213 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4214 COLORREF oldTextColor = 0, oldBkColor = 0, oldClrTextBk, oldClrText;
4215 NMLVCUSTOMDRAW nmlvcd;
4216 HFONT hOldFont = 0;
4217 DWORD cdmode;
4218 INT oldBkMode = 0;
4219 RECT rcClient;
4220 ITERATOR i;
4221 HDC hdcOrig = hdc;
4222 HBITMAP hbmp = NULL;
4224 LISTVIEW_DUMP(infoPtr);
4226 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4227 TRACE("double buffering\n");
4229 hdc = CreateCompatibleDC(hdcOrig);
4230 if (!hdc) {
4231 ERR("Failed to create DC for backbuffer\n");
4232 return;
4234 hbmp = CreateCompatibleBitmap(hdcOrig, infoPtr->rcList.right,
4235 infoPtr->rcList.bottom);
4236 if (!hbmp) {
4237 ERR("Failed to create bitmap for backbuffer\n");
4238 DeleteDC(hdc);
4239 return;
4242 SelectObject(hdc, hbmp);
4243 SelectObject(hdc, infoPtr->hFont);
4244 } else {
4245 /* Save dc values we're gonna trash while drawing
4246 * FIXME: Should be done in LISTVIEW_DrawItem() */
4247 hOldFont = SelectObject(hdc, infoPtr->hFont);
4248 oldBkMode = GetBkMode(hdc);
4249 oldBkColor = GetBkColor(hdc);
4250 oldTextColor = GetTextColor(hdc);
4253 infoPtr->bIsDrawing = TRUE;
4255 if (prcErase) {
4256 LISTVIEW_FillBkgnd(infoPtr, hdc, prcErase);
4257 } else if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4258 /* If no erasing was done (usually because RedrawWindow was called
4259 * with RDW_INVALIDATE only) we need to copy the old contents into
4260 * the backbuffer before continuing. */
4261 BitBlt(hdc, infoPtr->rcList.left, infoPtr->rcList.top,
4262 infoPtr->rcList.right - infoPtr->rcList.left,
4263 infoPtr->rcList.bottom - infoPtr->rcList.top,
4264 hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4267 /* FIXME: Shouldn't need to do this */
4268 oldClrTextBk = infoPtr->clrTextBk;
4269 oldClrText = infoPtr->clrText;
4271 infoPtr->cditemmode = CDRF_DODEFAULT;
4273 GetClientRect(infoPtr->hwndSelf, &rcClient);
4274 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4275 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4276 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4277 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4279 /* Use these colors to draw the items */
4280 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4281 infoPtr->clrText = nmlvcd.clrText;
4283 /* nothing to draw */
4284 if(infoPtr->nItemCount == 0) goto enddraw;
4286 /* figure out what we need to draw */
4287 iterator_visibleitems(&i, infoPtr, hdc);
4289 /* send cache hint notification */
4290 if (infoPtr->dwStyle & LVS_OWNERDATA)
4292 RANGE range = iterator_range(&i);
4293 NMLVCACHEHINT nmlv;
4295 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4296 nmlv.iFrom = range.lower;
4297 nmlv.iTo = range.upper - 1;
4298 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4301 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4302 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4303 else
4305 if (uView == LVS_REPORT)
4306 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4307 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4308 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4310 /* if we have a focus rect, draw it */
4311 if (infoPtr->bFocus)
4312 DrawFocusRect(hdc, &infoPtr->rcFocus);
4314 iterator_destroy(&i);
4316 enddraw:
4317 /* For LVS_EX_GRIDLINES go and draw lines */
4318 /* This includes the case where there were *no* items */
4319 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
4320 infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
4321 LISTVIEW_RefreshReportGrid(infoPtr, hdc);
4323 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4324 notify_postpaint(infoPtr, &nmlvcd);
4326 infoPtr->clrTextBk = oldClrTextBk;
4327 infoPtr->clrText = oldClrText;
4329 if(hbmp) {
4330 BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top,
4331 infoPtr->rcList.right - infoPtr->rcList.left,
4332 infoPtr->rcList.bottom - infoPtr->rcList.top,
4333 hdc, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4335 DeleteObject(hbmp);
4336 DeleteDC(hdc);
4337 } else {
4338 SelectObject(hdc, hOldFont);
4339 SetBkMode(hdc, oldBkMode);
4340 SetBkColor(hdc, oldBkColor);
4341 SetTextColor(hdc, oldTextColor);
4344 infoPtr->bIsDrawing = FALSE;
4348 /***
4349 * DESCRIPTION:
4350 * Calculates the approximate width and height of a given number of items.
4352 * PARAMETER(S):
4353 * [I] infoPtr : valid pointer to the listview structure
4354 * [I] nItemCount : number of items
4355 * [I] wWidth : width
4356 * [I] wHeight : height
4358 * RETURN:
4359 * Returns a DWORD. The width in the low word and the height in high word.
4361 static DWORD LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO *infoPtr, INT nItemCount,
4362 WORD wWidth, WORD wHeight)
4364 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4365 INT nItemCountPerColumn = 1;
4366 INT nColumnCount = 0;
4367 DWORD dwViewRect = 0;
4369 if (nItemCount == -1)
4370 nItemCount = infoPtr->nItemCount;
4372 if (uView == LVS_LIST)
4374 if (wHeight == 0xFFFF)
4376 /* use current height */
4377 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4380 if (wHeight < infoPtr->nItemHeight)
4381 wHeight = infoPtr->nItemHeight;
4383 if (nItemCount > 0)
4385 if (infoPtr->nItemHeight > 0)
4387 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4388 if (nItemCountPerColumn == 0)
4389 nItemCountPerColumn = 1;
4391 if (nItemCount % nItemCountPerColumn != 0)
4392 nColumnCount = nItemCount / nItemCountPerColumn;
4393 else
4394 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4398 /* Microsoft padding magic */
4399 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4400 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4402 dwViewRect = MAKELONG(wWidth, wHeight);
4404 else if (uView == LVS_REPORT)
4406 RECT rcBox;
4408 if (infoPtr->nItemCount > 0)
4410 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4411 wWidth = rcBox.right - rcBox.left;
4412 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4414 else
4416 /* use current height and width */
4417 if (wHeight == 0xffff)
4418 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4419 if (wWidth == 0xffff)
4420 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4423 dwViewRect = MAKELONG(wWidth, wHeight);
4425 else if (uView == LVS_SMALLICON)
4426 FIXME("uView == LVS_SMALLICON: not implemented\n");
4427 else if (uView == LVS_ICON)
4428 FIXME("uView == LVS_ICON: not implemented\n");
4430 return dwViewRect;
4434 /***
4435 * DESCRIPTION:
4436 * Create a drag image list for the specified item.
4438 * PARAMETER(S):
4439 * [I] infoPtr : valid pointer to the listview structure
4440 * [I] iItem : index of item
4441 * [O] lppt : Upperr-left corner of the image
4443 * RETURN:
4444 * Returns a handle to the image list if successful, NULL otherwise.
4446 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4448 RECT rcItem;
4449 SIZE size;
4450 POINT pos;
4451 HDC hdc, hdcOrig;
4452 HBITMAP hbmp, hOldbmp;
4453 HIMAGELIST dragList = 0;
4454 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
4456 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4457 return 0;
4459 rcItem.left = LVIR_BOUNDS;
4460 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4461 return 0;
4463 lppt->x = rcItem.left;
4464 lppt->y = rcItem.top;
4466 size.cx = rcItem.right - rcItem.left;
4467 size.cy = rcItem.bottom - rcItem.top;
4469 hdcOrig = GetDC(infoPtr->hwndSelf);
4470 hdc = CreateCompatibleDC(hdcOrig);
4471 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4472 hOldbmp = SelectObject(hdc, hbmp);
4474 rcItem.left = rcItem.top = 0;
4475 rcItem.right = size.cx;
4476 rcItem.bottom = size.cy;
4477 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4479 pos.x = pos.y = 0;
4480 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4482 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4483 SelectObject(hdc, hOldbmp);
4484 ImageList_Add(dragList, hbmp, 0);
4486 else
4487 SelectObject(hdc, hOldbmp);
4489 DeleteObject(hbmp);
4490 DeleteDC(hdc);
4491 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4493 TRACE("ret=%p\n", dragList);
4495 return dragList;
4499 /***
4500 * DESCRIPTION:
4501 * Removes all listview items and subitems.
4503 * PARAMETER(S):
4504 * [I] infoPtr : valid pointer to the listview structure
4506 * RETURN:
4507 * SUCCESS : TRUE
4508 * FAILURE : FALSE
4510 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr, BOOL destroy)
4512 NMLISTVIEW nmlv;
4513 HDPA hdpaSubItems = NULL;
4514 BOOL bSuppress;
4515 ITEMHDR *hdrItem;
4516 INT i, j;
4518 TRACE("()\n");
4520 /* we do it directly, to avoid notifications */
4521 ranges_clear(infoPtr->selectionRanges);
4522 infoPtr->nSelectionMark = -1;
4523 infoPtr->nFocusedItem = -1;
4524 SetRectEmpty(&infoPtr->rcFocus);
4525 /* But we are supposed to leave nHotItem as is! */
4528 /* send LVN_DELETEALLITEMS notification */
4529 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4530 nmlv.iItem = -1;
4531 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4533 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4535 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4537 /* send LVN_DELETEITEM notification, if not suppressed
4538 and if it is not a virtual listview */
4539 if (!bSuppress) notify_deleteitem(infoPtr, i);
4540 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
4541 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4543 hdrItem = DPA_GetPtr(hdpaSubItems, j);
4544 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4545 Free(hdrItem);
4547 DPA_Destroy(hdpaSubItems);
4548 DPA_DeletePtr(infoPtr->hdpaItems, i);
4550 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4551 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4552 infoPtr->nItemCount --;
4555 if (!destroy)
4557 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4558 LISTVIEW_UpdateScroll(infoPtr);
4560 LISTVIEW_InvalidateList(infoPtr);
4562 return TRUE;
4565 /***
4566 * DESCRIPTION:
4567 * Scrolls, and updates the columns, when a column is changing width.
4569 * PARAMETER(S):
4570 * [I] infoPtr : valid pointer to the listview structure
4571 * [I] nColumn : column to scroll
4572 * [I] dx : amount of scroll, in pixels
4574 * RETURN:
4575 * None.
4577 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4579 COLUMN_INFO *lpColumnInfo;
4580 RECT rcOld, rcCol;
4581 POINT ptOrigin;
4582 INT nCol;
4584 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4585 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4586 rcCol = lpColumnInfo->rcHeader;
4587 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4588 rcCol.left = rcCol.right;
4590 /* adjust the other columns */
4591 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4593 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4594 lpColumnInfo->rcHeader.left += dx;
4595 lpColumnInfo->rcHeader.right += dx;
4598 /* do not update screen if not in report mode */
4599 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4601 /* if we have a focus, we must first erase the focus rect */
4602 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4604 /* Need to reset the item width when inserting a new column */
4605 infoPtr->nItemWidth += dx;
4607 LISTVIEW_UpdateScroll(infoPtr);
4608 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4610 /* scroll to cover the deleted column, and invalidate for redraw */
4611 rcOld = infoPtr->rcList;
4612 rcOld.left = ptOrigin.x + rcCol.left + dx;
4613 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4615 /* we can restore focus now */
4616 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4619 /***
4620 * DESCRIPTION:
4621 * Removes a column from the listview control.
4623 * PARAMETER(S):
4624 * [I] infoPtr : valid pointer to the listview structure
4625 * [I] nColumn : column index
4627 * RETURN:
4628 * SUCCESS : TRUE
4629 * FAILURE : FALSE
4631 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4633 RECT rcCol;
4635 TRACE("nColumn=%d\n", nColumn);
4637 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4638 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4640 /* While the MSDN specifically says that column zero should not be deleted,
4641 what actually happens is that the column itself is deleted but no items or subitems
4642 are removed.
4645 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4647 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4648 return FALSE;
4650 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4651 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4653 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4655 SUBITEM_INFO *lpSubItem, *lpDelItem;
4656 HDPA hdpaSubItems;
4657 INT nItem, nSubItem, i;
4659 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4661 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
4662 nSubItem = 0;
4663 lpDelItem = 0;
4664 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4666 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
4667 if (lpSubItem->iSubItem == nColumn)
4669 nSubItem = i;
4670 lpDelItem = lpSubItem;
4672 else if (lpSubItem->iSubItem > nColumn)
4674 lpSubItem->iSubItem--;
4678 /* if we found our subitem, zapp it */
4679 if (nSubItem > 0)
4681 /* free string */
4682 if (is_textW(lpDelItem->hdr.pszText))
4683 Free(lpDelItem->hdr.pszText);
4685 /* free item */
4686 Free(lpDelItem);
4688 /* free dpa memory */
4689 DPA_DeletePtr(hdpaSubItems, nSubItem);
4694 /* update the other column info */
4695 LISTVIEW_UpdateItemSize(infoPtr);
4696 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4697 LISTVIEW_InvalidateList(infoPtr);
4698 else
4699 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4701 return TRUE;
4704 /***
4705 * DESCRIPTION:
4706 * Invalidates the listview after an item's insertion or deletion.
4708 * PARAMETER(S):
4709 * [I] infoPtr : valid pointer to the listview structure
4710 * [I] nItem : item index
4711 * [I] dir : -1 if deleting, 1 if inserting
4713 * RETURN:
4714 * None
4716 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4718 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4719 INT nPerCol, nItemCol, nItemRow;
4720 RECT rcScroll;
4721 POINT Origin;
4723 /* if we don't refresh, what's the point of scrolling? */
4724 if (!is_redrawing(infoPtr)) return;
4726 assert (abs(dir) == 1);
4728 /* arrange icons if autoarrange is on */
4729 if (is_autoarrange(infoPtr))
4731 BOOL arrange = TRUE;
4732 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4733 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4734 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4737 /* scrollbars need updating */
4738 LISTVIEW_UpdateScroll(infoPtr);
4740 /* figure out the item's position */
4741 if (uView == LVS_REPORT)
4742 nPerCol = infoPtr->nItemCount + 1;
4743 else if (uView == LVS_LIST)
4744 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4745 else /* LVS_ICON, or LVS_SMALLICON */
4746 return;
4748 nItemCol = nItem / nPerCol;
4749 nItemRow = nItem % nPerCol;
4750 LISTVIEW_GetOrigin(infoPtr, &Origin);
4752 /* move the items below up a slot */
4753 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4754 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4755 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4756 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4757 OffsetRect(&rcScroll, Origin.x, Origin.y);
4758 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
4759 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4761 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
4762 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4763 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4766 /* report has only that column, so we're done */
4767 if (uView == LVS_REPORT) return;
4769 /* now for LISTs, we have to deal with the columns to the right */
4770 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4771 rcScroll.top = 0;
4772 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4773 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4774 OffsetRect(&rcScroll, Origin.x, Origin.y);
4775 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4776 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4777 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4780 /***
4781 * DESCRIPTION:
4782 * Removes an item from the listview control.
4784 * PARAMETER(S):
4785 * [I] infoPtr : valid pointer to the listview structure
4786 * [I] nItem : item index
4788 * RETURN:
4789 * SUCCESS : TRUE
4790 * FAILURE : FALSE
4792 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4794 LVITEMW item;
4795 const UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4796 const BOOL is_icon = (uView == LVS_SMALLICON || uView == LVS_ICON);
4798 TRACE("(nItem=%d)\n", nItem);
4800 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4802 /* remove selection, and focus */
4803 item.state = 0;
4804 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4805 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4807 /* send LVN_DELETEITEM notification. */
4808 if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
4810 /* we need to do this here, because we'll be deleting stuff */
4811 if (is_icon)
4812 LISTVIEW_InvalidateItem(infoPtr, nItem);
4814 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4816 HDPA hdpaSubItems;
4817 ITEMHDR *hdrItem;
4818 INT i;
4820 hdpaSubItems = DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4821 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4823 hdrItem = DPA_GetPtr(hdpaSubItems, i);
4824 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4825 Free(hdrItem);
4827 DPA_Destroy(hdpaSubItems);
4830 if (is_icon)
4832 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4833 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4836 infoPtr->nItemCount--;
4837 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4839 /* now is the invalidation fun */
4840 if (!is_icon)
4841 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4842 return TRUE;
4846 /***
4847 * DESCRIPTION:
4848 * Callback implementation for editlabel control
4850 * PARAMETER(S):
4851 * [I] infoPtr : valid pointer to the listview structure
4852 * [I] pszText : modified text
4853 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4855 * RETURN:
4856 * SUCCESS : TRUE
4857 * FAILURE : FALSE
4859 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4861 HWND hwndSelf = infoPtr->hwndSelf;
4862 NMLVDISPINFOW dispInfo;
4863 INT editedItem = infoPtr->nEditLabelItem;
4864 BOOL bSame;
4866 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4868 infoPtr->nEditLabelItem = -1;
4870 ZeroMemory(&dispInfo, sizeof(dispInfo));
4871 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4872 dispInfo.item.iItem = editedItem;
4873 dispInfo.item.iSubItem = 0;
4874 dispInfo.item.stateMask = ~0;
4875 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4877 if (isW)
4878 bSame = (lstrcmpW(dispInfo.item.pszText, pszText) == 0);
4879 else
4881 LPWSTR tmp = textdupTtoW(pszText, FALSE);
4882 bSame = (lstrcmpW(dispInfo.item.pszText, tmp) == 0);
4883 textfreeT(tmp, FALSE);
4885 if (bSame) return TRUE;
4887 /* add the text from the edit in */
4888 dispInfo.item.mask |= LVIF_TEXT;
4889 dispInfo.item.pszText = pszText;
4890 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4892 /* Do we need to update the Item Text */
4893 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4894 if (!IsWindow(hwndSelf))
4895 return FALSE;
4896 if (!pszText) return TRUE;
4898 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4900 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, editedItem);
4901 ITEM_INFO* lpItem = DPA_GetPtr(hdpaSubItems, 0);
4902 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4904 LISTVIEW_InvalidateItem(infoPtr, editedItem);
4905 return TRUE;
4909 ZeroMemory(&dispInfo, sizeof(dispInfo));
4910 dispInfo.item.mask = LVIF_TEXT;
4911 dispInfo.item.iItem = editedItem;
4912 dispInfo.item.iSubItem = 0;
4913 dispInfo.item.pszText = pszText;
4914 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4915 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4918 /***
4919 * DESCRIPTION:
4920 * Begin in place editing of specified list view item
4922 * PARAMETER(S):
4923 * [I] infoPtr : valid pointer to the listview structure
4924 * [I] nItem : item index
4925 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4927 * RETURN:
4928 * SUCCESS : TRUE
4929 * FAILURE : FALSE
4931 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4933 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4934 NMLVDISPINFOW dispInfo;
4935 RECT rect;
4936 HWND hwndSelf = infoPtr->hwndSelf;
4938 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4940 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4941 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4943 infoPtr->nEditLabelItem = nItem;
4945 /* Is the EditBox still there, if so remove it */
4946 if(infoPtr->hwndEdit != 0)
4948 SetFocus(infoPtr->hwndSelf);
4949 infoPtr->hwndEdit = 0;
4952 LISTVIEW_SetSelection(infoPtr, nItem);
4953 LISTVIEW_SetItemFocus(infoPtr, nItem);
4954 LISTVIEW_InvalidateItem(infoPtr, nItem);
4956 rect.left = LVIR_LABEL;
4957 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4959 ZeroMemory(&dispInfo, sizeof(dispInfo));
4960 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4961 dispInfo.item.iItem = nItem;
4962 dispInfo.item.iSubItem = 0;
4963 dispInfo.item.stateMask = ~0;
4964 dispInfo.item.pszText = szDispText;
4965 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4966 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4968 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4969 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4970 if (!infoPtr->hwndEdit) return 0;
4972 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4974 if (!IsWindow(hwndSelf))
4975 return 0;
4976 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4977 infoPtr->hwndEdit = 0;
4978 return 0;
4981 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4982 SetFocus(infoPtr->hwndEdit);
4983 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4984 return infoPtr->hwndEdit;
4988 /***
4989 * DESCRIPTION:
4990 * Ensures the specified item is visible, scrolling into view if necessary.
4992 * PARAMETER(S):
4993 * [I] infoPtr : valid pointer to the listview structure
4994 * [I] nItem : item index
4995 * [I] bPartial : partially or entirely visible
4997 * RETURN:
4998 * SUCCESS : TRUE
4999 * FAILURE : FALSE
5001 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
5003 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5004 INT nScrollPosHeight = 0;
5005 INT nScrollPosWidth = 0;
5006 INT nHorzAdjust = 0;
5007 INT nVertAdjust = 0;
5008 INT nHorzDiff = 0;
5009 INT nVertDiff = 0;
5010 RECT rcItem, rcTemp;
5012 rcItem.left = LVIR_BOUNDS;
5013 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
5015 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
5017 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
5019 /* scroll left/right, but in LVS_REPORT mode */
5020 if (uView == LVS_LIST)
5021 nScrollPosWidth = infoPtr->nItemWidth;
5022 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5023 nScrollPosWidth = 1;
5025 if (rcItem.left < infoPtr->rcList.left)
5027 nHorzAdjust = -1;
5028 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
5030 else
5032 nHorzAdjust = 1;
5033 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
5037 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
5039 /* scroll up/down, but not in LVS_LIST mode */
5040 if (uView == LVS_REPORT)
5041 nScrollPosHeight = infoPtr->nItemHeight;
5042 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
5043 nScrollPosHeight = 1;
5045 if (rcItem.top < infoPtr->rcList.top)
5047 nVertAdjust = -1;
5048 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
5050 else
5052 nVertAdjust = 1;
5053 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
5057 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
5059 if (nScrollPosWidth)
5061 INT diff = nHorzDiff / nScrollPosWidth;
5062 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
5063 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
5066 if (nScrollPosHeight)
5068 INT diff = nVertDiff / nScrollPosHeight;
5069 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
5070 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
5073 return TRUE;
5076 /***
5077 * DESCRIPTION:
5078 * Searches for an item with specific characteristics.
5080 * PARAMETER(S):
5081 * [I] hwnd : window handle
5082 * [I] nStart : base item index
5083 * [I] lpFindInfo : item information to look for
5085 * RETURN:
5086 * SUCCESS : index of item
5087 * FAILURE : -1
5089 static INT LISTVIEW_FindItemW(const LISTVIEW_INFO *infoPtr, INT nStart,
5090 const LVFINDINFOW *lpFindInfo)
5092 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5093 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5094 BOOL bWrap = FALSE, bNearest = FALSE;
5095 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
5096 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
5097 POINT Position, Destination;
5098 LVITEMW lvItem;
5100 /* Search in virtual listviews should be done by application, not by
5101 listview control, so we just send LVN_ODFINDITEMW and return the result */
5102 if (infoPtr->dwStyle & LVS_OWNERDATA)
5104 NMLVFINDITEMW nmlv;
5106 nmlv.iStart = nStart;
5107 nmlv.lvfi = *lpFindInfo;
5108 return notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
5111 if (!lpFindInfo || nItem < 0) return -1;
5113 lvItem.mask = 0;
5114 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
5116 lvItem.mask |= LVIF_TEXT;
5117 lvItem.pszText = szDispText;
5118 lvItem.cchTextMax = DISP_TEXT_SIZE;
5121 if (lpFindInfo->flags & LVFI_WRAP)
5122 bWrap = TRUE;
5124 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
5125 (uView == LVS_ICON || uView ==LVS_SMALLICON))
5127 POINT Origin;
5128 RECT rcArea;
5130 LISTVIEW_GetOrigin(infoPtr, &Origin);
5131 Destination.x = lpFindInfo->pt.x - Origin.x;
5132 Destination.y = lpFindInfo->pt.y - Origin.y;
5133 switch(lpFindInfo->vkDirection)
5135 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
5136 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
5137 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
5138 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
5139 case VK_HOME: Destination.x = Destination.y = 0; break;
5140 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5141 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5142 case VK_END:
5143 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
5144 Destination.x = rcArea.right;
5145 Destination.y = rcArea.bottom;
5146 break;
5147 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
5149 bNearest = TRUE;
5151 else Destination.x = Destination.y = 0;
5153 /* if LVFI_PARAM is specified, all other flags are ignored */
5154 if (lpFindInfo->flags & LVFI_PARAM)
5156 lvItem.mask |= LVIF_PARAM;
5157 bNearest = FALSE;
5158 lvItem.mask &= ~LVIF_TEXT;
5161 again:
5162 for (; nItem < nLast; nItem++)
5164 lvItem.iItem = nItem;
5165 lvItem.iSubItem = 0;
5166 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
5168 if (lvItem.mask & LVIF_PARAM)
5170 if (lpFindInfo->lParam == lvItem.lParam)
5171 return nItem;
5172 else
5173 continue;
5176 if (lvItem.mask & LVIF_TEXT)
5178 if (lpFindInfo->flags & LVFI_PARTIAL)
5180 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
5182 else
5184 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
5188 if (!bNearest) return nItem;
5190 /* This is very inefficient. To do a good job here,
5191 * we need a sorted array of (x,y) item positions */
5192 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5194 /* compute the distance^2 to the destination */
5195 xdist = Destination.x - Position.x;
5196 ydist = Destination.y - Position.y;
5197 dist = xdist * xdist + ydist * ydist;
5199 /* remember the distance, and item if it's closer */
5200 if (dist < mindist)
5202 mindist = dist;
5203 nNearestItem = nItem;
5207 if (bWrap)
5209 nItem = 0;
5210 nLast = min(nStart + 1, infoPtr->nItemCount);
5211 bWrap = FALSE;
5212 goto again;
5215 return nNearestItem;
5218 /***
5219 * DESCRIPTION:
5220 * Searches for an item with specific characteristics.
5222 * PARAMETER(S):
5223 * [I] hwnd : window handle
5224 * [I] nStart : base item index
5225 * [I] lpFindInfo : item information to look for
5227 * RETURN:
5228 * SUCCESS : index of item
5229 * FAILURE : -1
5231 static INT LISTVIEW_FindItemA(const LISTVIEW_INFO *infoPtr, INT nStart,
5232 const LVFINDINFOA *lpFindInfo)
5234 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
5235 LVFINDINFOW fiw;
5236 INT res;
5237 LPWSTR strW = NULL;
5239 memcpy(&fiw, lpFindInfo, sizeof(fiw));
5240 if (hasText) fiw.psz = strW = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
5241 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
5242 textfreeT(strW, FALSE);
5243 return res;
5246 /***
5247 * DESCRIPTION:
5248 * Retrieves the background image of the listview control.
5250 * PARAMETER(S):
5251 * [I] infoPtr : valid pointer to the listview structure
5252 * [O] lpBkImage : background image attributes
5254 * RETURN:
5255 * SUCCESS : TRUE
5256 * FAILURE : FALSE
5258 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
5259 /* { */
5260 /* FIXME (listview, "empty stub!\n"); */
5261 /* return FALSE; */
5262 /* } */
5264 /***
5265 * DESCRIPTION:
5266 * Retrieves column attributes.
5268 * PARAMETER(S):
5269 * [I] infoPtr : valid pointer to the listview structure
5270 * [I] nColumn : column index
5271 * [IO] lpColumn : column information
5272 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5273 * otherwise it is in fact a LPLVCOLUMNA
5275 * RETURN:
5276 * SUCCESS : TRUE
5277 * FAILURE : FALSE
5279 static BOOL LISTVIEW_GetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
5281 COLUMN_INFO *lpColumnInfo;
5282 HDITEMW hdi;
5284 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5285 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
5287 /* initialize memory */
5288 ZeroMemory(&hdi, sizeof(hdi));
5290 if (lpColumn->mask & LVCF_TEXT)
5292 hdi.mask |= HDI_TEXT;
5293 hdi.pszText = lpColumn->pszText;
5294 hdi.cchTextMax = lpColumn->cchTextMax;
5297 if (lpColumn->mask & LVCF_IMAGE)
5298 hdi.mask |= HDI_IMAGE;
5300 if (lpColumn->mask & LVCF_ORDER)
5301 hdi.mask |= HDI_ORDER;
5303 if (lpColumn->mask & LVCF_SUBITEM)
5304 hdi.mask |= HDI_LPARAM;
5306 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
5308 if (lpColumn->mask & LVCF_FMT)
5309 lpColumn->fmt = lpColumnInfo->fmt;
5311 if (lpColumn->mask & LVCF_WIDTH)
5312 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
5314 if (lpColumn->mask & LVCF_IMAGE)
5315 lpColumn->iImage = hdi.iImage;
5317 if (lpColumn->mask & LVCF_ORDER)
5318 lpColumn->iOrder = hdi.iOrder;
5320 if (lpColumn->mask & LVCF_SUBITEM)
5321 lpColumn->iSubItem = hdi.lParam;
5323 return TRUE;
5327 static BOOL LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5329 INT i;
5331 if (!lpiArray)
5332 return FALSE;
5334 /* FIXME: little hack */
5335 for (i = 0; i < iCount; i++)
5336 lpiArray[i] = i;
5338 return TRUE;
5341 /***
5342 * DESCRIPTION:
5343 * Retrieves the column width.
5345 * PARAMETER(S):
5346 * [I] infoPtr : valid pointer to the listview structure
5347 * [I] int : column index
5349 * RETURN:
5350 * SUCCESS : column width
5351 * FAILURE : zero
5353 static INT LISTVIEW_GetColumnWidth(const LISTVIEW_INFO *infoPtr, INT nColumn)
5355 INT nColumnWidth = 0;
5356 HDITEMW hdItem;
5358 TRACE("nColumn=%d\n", nColumn);
5360 /* we have a 'column' in LIST and REPORT mode only */
5361 switch(infoPtr->dwStyle & LVS_TYPEMASK)
5363 case LVS_LIST:
5364 nColumnWidth = infoPtr->nItemWidth;
5365 break;
5366 case LVS_REPORT:
5367 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDM_ITEMCHANGED.
5368 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
5369 * HDM_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
5371 * TODO: should we do the same in LVM_GETCOLUMN?
5373 hdItem.mask = HDI_WIDTH;
5374 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdItem))
5376 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr->hwndSelf, nColumn);
5377 return 0;
5379 nColumnWidth = hdItem.cxy;
5380 break;
5383 TRACE("nColumnWidth=%d\n", nColumnWidth);
5384 return nColumnWidth;
5387 /***
5388 * DESCRIPTION:
5389 * In list or report display mode, retrieves the number of items that can fit
5390 * vertically in the visible area. In icon or small icon display mode,
5391 * retrieves the total number of visible items.
5393 * PARAMETER(S):
5394 * [I] infoPtr : valid pointer to the listview structure
5396 * RETURN:
5397 * Number of fully visible items.
5399 static INT LISTVIEW_GetCountPerPage(const LISTVIEW_INFO *infoPtr)
5401 switch (infoPtr->dwStyle & LVS_TYPEMASK)
5403 case LVS_ICON:
5404 case LVS_SMALLICON:
5405 return infoPtr->nItemCount;
5406 case LVS_REPORT:
5407 return LISTVIEW_GetCountPerColumn(infoPtr);
5408 case LVS_LIST:
5409 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5411 assert(FALSE);
5412 return 0;
5415 /***
5416 * DESCRIPTION:
5417 * Retrieves an image list handle.
5419 * PARAMETER(S):
5420 * [I] infoPtr : valid pointer to the listview structure
5421 * [I] nImageList : image list identifier
5423 * RETURN:
5424 * SUCCESS : image list handle
5425 * FAILURE : NULL
5427 static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImageList)
5429 switch (nImageList)
5431 case LVSIL_NORMAL: return infoPtr->himlNormal;
5432 case LVSIL_SMALL: return infoPtr->himlSmall;
5433 case LVSIL_STATE: return infoPtr->himlState;
5435 return NULL;
5438 /* LISTVIEW_GetISearchString */
5440 /***
5441 * DESCRIPTION:
5442 * Retrieves item attributes.
5444 * PARAMETER(S):
5445 * [I] hwnd : window handle
5446 * [IO] lpLVItem : item info
5447 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5448 * if FALSE, then lpLVItem is a LPLVITEMA.
5450 * NOTE:
5451 * This is the internal 'GetItem' interface -- it tries to
5452 * be smart and avoid text copies, if possible, by modifying
5453 * lpLVItem->pszText to point to the text string. Please note
5454 * that this is not always possible (e.g. OWNERDATA), so on
5455 * entry you *must* supply valid values for pszText, and cchTextMax.
5456 * The only difference to the documented interface is that upon
5457 * return, you should use *only* the lpLVItem->pszText, rather than
5458 * the buffer pointer you provided on input. Most code already does
5459 * that, so it's not a problem.
5460 * For the two cases when the text must be copied (that is,
5461 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5463 * RETURN:
5464 * SUCCESS : TRUE
5465 * FAILURE : FALSE
5467 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5469 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5470 NMLVDISPINFOW dispInfo;
5471 ITEM_INFO *lpItem;
5472 ITEMHDR* pItemHdr;
5473 HDPA hdpaSubItems;
5474 INT isubitem;
5476 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5478 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5479 return FALSE;
5481 if (lpLVItem->mask == 0) return TRUE;
5483 /* make a local copy */
5484 isubitem = lpLVItem->iSubItem;
5486 /* a quick optimization if all we're asked is the focus state
5487 * these queries are worth optimising since they are common,
5488 * and can be answered in constant time, without the heavy accesses */
5489 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5490 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5492 lpLVItem->state = 0;
5493 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5494 lpLVItem->state |= LVIS_FOCUSED;
5495 return TRUE;
5498 ZeroMemory(&dispInfo, sizeof(dispInfo));
5500 /* if the app stores all the data, handle it separately */
5501 if (infoPtr->dwStyle & LVS_OWNERDATA)
5503 dispInfo.item.state = 0;
5505 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5506 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5508 /* NOTE: copy only fields which we _know_ are initialized, some apps
5509 * depend on the uninitialized fields being 0 */
5510 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5511 dispInfo.item.iItem = lpLVItem->iItem;
5512 dispInfo.item.iSubItem = isubitem;
5513 if (lpLVItem->mask & LVIF_TEXT)
5515 dispInfo.item.pszText = lpLVItem->pszText;
5516 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5518 if (lpLVItem->mask & LVIF_STATE)
5519 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5520 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5521 dispInfo.item.stateMask = lpLVItem->stateMask;
5522 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5524 /* full size structure expected - _WIN32IE >= 0x560 */
5525 *lpLVItem = dispInfo.item;
5527 else if (lpLVItem->mask & LVIF_INDENT)
5529 /* indent member expected - _WIN32IE >= 0x300 */
5530 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5532 else
5534 /* minimal structure expected */
5535 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5537 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5540 /* make sure lParam is zeroed out */
5541 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5543 /* we store only a little state, so if we're not asked, we're done */
5544 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5546 /* if focus is handled by us, report it */
5547 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5549 lpLVItem->state &= ~LVIS_FOCUSED;
5550 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5551 lpLVItem->state |= LVIS_FOCUSED;
5554 /* and do the same for selection, if we handle it */
5555 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5557 lpLVItem->state &= ~LVIS_SELECTED;
5558 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5559 lpLVItem->state |= LVIS_SELECTED;
5562 return TRUE;
5565 /* find the item and subitem structures before we proceed */
5566 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5567 lpItem = DPA_GetPtr(hdpaSubItems, 0);
5568 assert (lpItem);
5570 if (isubitem)
5572 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5573 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5574 if (!lpSubItem)
5576 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5577 isubitem = 0;
5580 else
5581 pItemHdr = &lpItem->hdr;
5583 /* Do we need to query the state from the app? */
5584 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5586 dispInfo.item.mask |= LVIF_STATE;
5587 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5590 /* Do we need to enquire about the image? */
5591 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5592 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5594 dispInfo.item.mask |= LVIF_IMAGE;
5595 dispInfo.item.iImage = I_IMAGECALLBACK;
5598 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5599 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5601 dispInfo.item.mask |= LVIF_TEXT;
5602 dispInfo.item.pszText = lpLVItem->pszText;
5603 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5604 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5605 *dispInfo.item.pszText = '\0';
5608 /* If we don't have all the requested info, query the application */
5609 if (dispInfo.item.mask != 0)
5611 dispInfo.item.iItem = lpLVItem->iItem;
5612 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5613 dispInfo.item.lParam = lpItem->lParam;
5614 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5615 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5618 /* we should not store values for subitems */
5619 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5621 /* Now, handle the iImage field */
5622 if (dispInfo.item.mask & LVIF_IMAGE)
5624 lpLVItem->iImage = dispInfo.item.iImage;
5625 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5626 pItemHdr->iImage = dispInfo.item.iImage;
5628 else if (lpLVItem->mask & LVIF_IMAGE)
5630 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5631 lpLVItem->iImage = pItemHdr->iImage;
5632 else
5633 lpLVItem->iImage = 0;
5636 /* The pszText field */
5637 if (dispInfo.item.mask & LVIF_TEXT)
5639 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5640 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5642 lpLVItem->pszText = dispInfo.item.pszText;
5644 else if (lpLVItem->mask & LVIF_TEXT)
5646 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5647 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5650 /* Next is the lParam field */
5651 if (dispInfo.item.mask & LVIF_PARAM)
5653 lpLVItem->lParam = dispInfo.item.lParam;
5654 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5655 lpItem->lParam = dispInfo.item.lParam;
5657 else if (lpLVItem->mask & LVIF_PARAM)
5658 lpLVItem->lParam = lpItem->lParam;
5660 /* if this is a subitem, we're done */
5661 if (isubitem) return TRUE;
5663 /* ... the state field (this one is different due to uCallbackmask) */
5664 if (lpLVItem->mask & LVIF_STATE)
5666 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5667 if (dispInfo.item.mask & LVIF_STATE)
5669 lpLVItem->state &= ~dispInfo.item.stateMask;
5670 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5672 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5674 lpLVItem->state &= ~LVIS_FOCUSED;
5675 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5676 lpLVItem->state |= LVIS_FOCUSED;
5678 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5680 lpLVItem->state &= ~LVIS_SELECTED;
5681 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5682 lpLVItem->state |= LVIS_SELECTED;
5686 /* and last, but not least, the indent field */
5687 if (lpLVItem->mask & LVIF_INDENT)
5688 lpLVItem->iIndent = lpItem->iIndent;
5690 return TRUE;
5693 /***
5694 * DESCRIPTION:
5695 * Retrieves item attributes.
5697 * PARAMETER(S):
5698 * [I] hwnd : window handle
5699 * [IO] lpLVItem : item info
5700 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5701 * if FALSE, then lpLVItem is a LPLVITEMA.
5703 * NOTE:
5704 * This is the external 'GetItem' interface -- it properly copies
5705 * the text in the provided buffer.
5707 * RETURN:
5708 * SUCCESS : TRUE
5709 * FAILURE : FALSE
5711 static BOOL LISTVIEW_GetItemExtT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5713 LPWSTR pszText;
5714 BOOL bResult;
5716 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5717 return FALSE;
5719 pszText = lpLVItem->pszText;
5720 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5721 if (bResult && lpLVItem->pszText != pszText)
5722 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5723 lpLVItem->pszText = pszText;
5725 return bResult;
5729 /***
5730 * DESCRIPTION:
5731 * Retrieves the position (upper-left) of the listview control item.
5732 * Note that for LVS_ICON style, the upper-left is that of the icon
5733 * and not the bounding box.
5735 * PARAMETER(S):
5736 * [I] infoPtr : valid pointer to the listview structure
5737 * [I] nItem : item index
5738 * [O] lpptPosition : coordinate information
5740 * RETURN:
5741 * SUCCESS : TRUE
5742 * FAILURE : FALSE
5744 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5746 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5747 POINT Origin;
5749 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5751 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5753 LISTVIEW_GetOrigin(infoPtr, &Origin);
5754 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5756 if (uView == LVS_ICON)
5758 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5759 lpptPosition->y += ICON_TOP_PADDING;
5761 lpptPosition->x += Origin.x;
5762 lpptPosition->y += Origin.y;
5764 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
5765 return TRUE;
5769 /***
5770 * DESCRIPTION:
5771 * Retrieves the bounding rectangle for a listview control item.
5773 * PARAMETER(S):
5774 * [I] infoPtr : valid pointer to the listview structure
5775 * [I] nItem : item index
5776 * [IO] lprc : bounding rectangle coordinates
5777 * lprc->left specifies the portion of the item for which the bounding
5778 * rectangle will be retrieved.
5780 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5781 * including the icon and label.
5783 * * For LVS_ICON
5784 * * Experiment shows that native control returns:
5785 * * width = min (48, length of text line)
5786 * * .left = position.x - (width - iconsize.cx)/2
5787 * * .right = .left + width
5788 * * height = #lines of text * ntmHeight + icon height + 8
5789 * * .top = position.y - 2
5790 * * .bottom = .top + height
5791 * * separation between items .y = itemSpacing.cy - height
5792 * * .x = itemSpacing.cx - width
5793 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5795 * * For LVS_ICON
5796 * * Experiment shows that native control returns:
5797 * * width = iconSize.cx + 16
5798 * * .left = position.x - (width - iconsize.cx)/2
5799 * * .right = .left + width
5800 * * height = iconSize.cy + 4
5801 * * .top = position.y - 2
5802 * * .bottom = .top + height
5803 * * separation between items .y = itemSpacing.cy - height
5804 * * .x = itemSpacing.cx - width
5805 * LVIR_LABEL Returns the bounding rectangle of the item text.
5807 * * For LVS_ICON
5808 * * Experiment shows that native control returns:
5809 * * width = text length
5810 * * .left = position.x - width/2
5811 * * .right = .left + width
5812 * * height = ntmH * linecount + 2
5813 * * .top = position.y + iconSize.cy + 6
5814 * * .bottom = .top + height
5815 * * separation between items .y = itemSpacing.cy - height
5816 * * .x = itemSpacing.cx - width
5817 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5818 * rectangles, but excludes columns in report view.
5820 * RETURN:
5821 * SUCCESS : TRUE
5822 * FAILURE : FALSE
5824 * NOTES
5825 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5826 * upon whether the window has the focus currently and on whether the item
5827 * is the one with the focus. Ensure that the control's record of which
5828 * item has the focus agrees with the items' records.
5830 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5832 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5833 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5834 BOOL doLabel = TRUE, oversizedBox = FALSE;
5835 POINT Position, Origin;
5836 LVITEMW lvItem;
5838 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5840 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5842 LISTVIEW_GetOrigin(infoPtr, &Origin);
5843 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5845 /* Be smart and try to figure out the minimum we have to do */
5846 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5847 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5848 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5849 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5850 oversizedBox = TRUE;
5852 /* get what we need from the item before hand, so we make
5853 * only one request. This can speed up things, if data
5854 * is stored on the app side */
5855 lvItem.mask = 0;
5856 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5857 if (doLabel) lvItem.mask |= LVIF_TEXT;
5858 lvItem.iItem = nItem;
5859 lvItem.iSubItem = 0;
5860 lvItem.pszText = szDispText;
5861 lvItem.cchTextMax = DISP_TEXT_SIZE;
5862 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5863 /* we got the state already up, simulate it here, to avoid a reget */
5864 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5866 lvItem.mask |= LVIF_STATE;
5867 lvItem.stateMask = LVIS_FOCUSED;
5868 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5871 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5872 lprc->left = LVIR_BOUNDS;
5873 switch(lprc->left)
5875 case LVIR_ICON:
5876 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
5877 break;
5879 case LVIR_LABEL:
5880 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc);
5881 break;
5883 case LVIR_BOUNDS:
5884 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
5885 break;
5887 case LVIR_SELECTBOUNDS:
5888 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL);
5889 break;
5891 default:
5892 WARN("Unknown value: %d\n", lprc->left);
5893 return FALSE;
5896 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5898 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
5900 return TRUE;
5903 /***
5904 * DESCRIPTION:
5905 * Retrieves the spacing between listview control items.
5907 * PARAMETER(S):
5908 * [I] infoPtr : valid pointer to the listview structure
5909 * [IO] lprc : rectangle to receive the output
5910 * on input, lprc->top = nSubItem
5911 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5913 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5914 * not only those of the first column.
5915 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5917 * RETURN:
5918 * TRUE: success
5919 * FALSE: failure
5921 static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5923 POINT Position;
5924 LVITEMW lvItem;
5925 INT nColumn;
5927 if (!lprc) return FALSE;
5929 nColumn = lprc->top;
5931 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
5932 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5933 if (lprc->top == 0)
5934 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5936 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5938 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5940 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5942 lvItem.mask = 0;
5943 lvItem.iItem = nItem;
5944 lvItem.iSubItem = nColumn;
5946 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5947 switch(lprc->left)
5949 case LVIR_ICON:
5950 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
5951 break;
5953 case LVIR_LABEL:
5954 case LVIR_BOUNDS:
5955 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
5956 break;
5958 default:
5959 ERR("Unknown bounds=%d\n", lprc->left);
5960 return FALSE;
5963 OffsetRect(lprc, Position.x, Position.y);
5964 return TRUE;
5968 /***
5969 * DESCRIPTION:
5970 * Retrieves the width of a label.
5972 * PARAMETER(S):
5973 * [I] infoPtr : valid pointer to the listview structure
5975 * RETURN:
5976 * SUCCESS : string width (in pixels)
5977 * FAILURE : zero
5979 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *infoPtr, INT nItem)
5981 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5982 LVITEMW lvItem;
5984 TRACE("(nItem=%d)\n", nItem);
5986 lvItem.mask = LVIF_TEXT;
5987 lvItem.iItem = nItem;
5988 lvItem.iSubItem = 0;
5989 lvItem.pszText = szDispText;
5990 lvItem.cchTextMax = DISP_TEXT_SIZE;
5991 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5993 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5996 /***
5997 * DESCRIPTION:
5998 * Retrieves the spacing between listview control items.
6000 * PARAMETER(S):
6001 * [I] infoPtr : valid pointer to the listview structure
6002 * [I] bSmall : flag for small or large icon
6004 * RETURN:
6005 * Horizontal + vertical spacing
6007 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall)
6009 LONG lResult;
6011 if (!bSmall)
6013 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6015 else
6017 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
6018 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6019 else
6020 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6022 return lResult;
6025 /***
6026 * DESCRIPTION:
6027 * Retrieves the state of a listview control item.
6029 * PARAMETER(S):
6030 * [I] infoPtr : valid pointer to the listview structure
6031 * [I] nItem : item index
6032 * [I] uMask : state mask
6034 * RETURN:
6035 * State specified by the mask.
6037 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
6039 LVITEMW lvItem;
6041 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6043 lvItem.iItem = nItem;
6044 lvItem.iSubItem = 0;
6045 lvItem.mask = LVIF_STATE;
6046 lvItem.stateMask = uMask;
6047 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6049 return lvItem.state & uMask;
6052 /***
6053 * DESCRIPTION:
6054 * Retrieves the text of a listview control item or subitem.
6056 * PARAMETER(S):
6057 * [I] hwnd : window handle
6058 * [I] nItem : item index
6059 * [IO] lpLVItem : item information
6060 * [I] isW : TRUE if lpLVItem is Unicode
6062 * RETURN:
6063 * SUCCESS : string length
6064 * FAILURE : 0
6066 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6068 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6070 lpLVItem->mask = LVIF_TEXT;
6071 lpLVItem->iItem = nItem;
6072 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
6074 return textlenT(lpLVItem->pszText, isW);
6077 /***
6078 * DESCRIPTION:
6079 * Searches for an item based on properties + relationships.
6081 * PARAMETER(S):
6082 * [I] infoPtr : valid pointer to the listview structure
6083 * [I] nItem : item index
6084 * [I] uFlags : relationship flag
6086 * RETURN:
6087 * SUCCESS : item index
6088 * FAILURE : -1
6090 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
6092 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6093 UINT uMask = 0;
6094 LVFINDINFOW lvFindInfo;
6095 INT nCountPerColumn;
6096 INT nCountPerRow;
6097 INT i;
6099 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
6100 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
6102 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6104 if (uFlags & LVNI_CUT)
6105 uMask |= LVIS_CUT;
6107 if (uFlags & LVNI_DROPHILITED)
6108 uMask |= LVIS_DROPHILITED;
6110 if (uFlags & LVNI_FOCUSED)
6111 uMask |= LVIS_FOCUSED;
6113 if (uFlags & LVNI_SELECTED)
6114 uMask |= LVIS_SELECTED;
6116 /* if we're asked for the focused item, that's only one,
6117 * so it's worth optimizing */
6118 if (uFlags & LVNI_FOCUSED)
6120 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
6121 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
6124 if (uFlags & LVNI_ABOVE)
6126 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6128 while (nItem >= 0)
6130 nItem--;
6131 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6132 return nItem;
6135 else
6137 /* Special case for autoarrange - move 'til the top of a list */
6138 if (is_autoarrange(infoPtr))
6140 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6141 while (nItem - nCountPerRow >= 0)
6143 nItem -= nCountPerRow;
6144 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6145 return nItem;
6147 return -1;
6149 lvFindInfo.flags = LVFI_NEARESTXY;
6150 lvFindInfo.vkDirection = VK_UP;
6151 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6152 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6154 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6155 return nItem;
6159 else if (uFlags & LVNI_BELOW)
6161 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6163 while (nItem < infoPtr->nItemCount)
6165 nItem++;
6166 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6167 return nItem;
6170 else
6172 /* Special case for autoarrange - move 'til the bottom of a list */
6173 if (is_autoarrange(infoPtr))
6175 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6176 while (nItem + nCountPerRow < infoPtr->nItemCount )
6178 nItem += nCountPerRow;
6179 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6180 return nItem;
6182 return -1;
6184 lvFindInfo.flags = LVFI_NEARESTXY;
6185 lvFindInfo.vkDirection = VK_DOWN;
6186 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6187 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6189 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6190 return nItem;
6194 else if (uFlags & LVNI_TOLEFT)
6196 if (uView == LVS_LIST)
6198 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6199 while (nItem - nCountPerColumn >= 0)
6201 nItem -= nCountPerColumn;
6202 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6203 return nItem;
6206 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6208 /* Special case for autoarrange - move 'ti the beginning of a row */
6209 if (is_autoarrange(infoPtr))
6211 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6212 while (nItem % nCountPerRow > 0)
6214 nItem --;
6215 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6216 return nItem;
6218 return -1;
6220 lvFindInfo.flags = LVFI_NEARESTXY;
6221 lvFindInfo.vkDirection = VK_LEFT;
6222 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6223 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6225 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6226 return nItem;
6230 else if (uFlags & LVNI_TORIGHT)
6232 if (uView == LVS_LIST)
6234 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6235 while (nItem + nCountPerColumn < infoPtr->nItemCount)
6237 nItem += nCountPerColumn;
6238 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6239 return nItem;
6242 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6244 /* Special case for autoarrange - move 'til the end of a row */
6245 if (is_autoarrange(infoPtr))
6247 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6248 while (nItem % nCountPerRow < nCountPerRow - 1 )
6250 nItem ++;
6251 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6252 return nItem;
6254 return -1;
6256 lvFindInfo.flags = LVFI_NEARESTXY;
6257 lvFindInfo.vkDirection = VK_RIGHT;
6258 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6259 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6261 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6262 return nItem;
6266 else
6268 nItem++;
6270 /* search by index */
6271 for (i = nItem; i < infoPtr->nItemCount; i++)
6273 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
6274 return i;
6278 return -1;
6281 /* LISTVIEW_GetNumberOfWorkAreas */
6283 /***
6284 * DESCRIPTION:
6285 * Retrieves the origin coordinates when in icon or small icon display mode.
6287 * PARAMETER(S):
6288 * [I] infoPtr : valid pointer to the listview structure
6289 * [O] lpptOrigin : coordinate information
6291 * RETURN:
6292 * None.
6294 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
6296 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6297 INT nHorzPos = 0, nVertPos = 0;
6298 SCROLLINFO scrollInfo;
6300 scrollInfo.cbSize = sizeof(SCROLLINFO);
6301 scrollInfo.fMask = SIF_POS;
6303 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6304 nHorzPos = scrollInfo.nPos;
6305 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6306 nVertPos = scrollInfo.nPos;
6308 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6310 lpptOrigin->x = infoPtr->rcList.left;
6311 lpptOrigin->y = infoPtr->rcList.top;
6312 if (uView == LVS_LIST)
6313 nHorzPos *= infoPtr->nItemWidth;
6314 else if (uView == LVS_REPORT)
6315 nVertPos *= infoPtr->nItemHeight;
6317 lpptOrigin->x -= nHorzPos;
6318 lpptOrigin->y -= nVertPos;
6320 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
6323 /***
6324 * DESCRIPTION:
6325 * Retrieves the width of a string.
6327 * PARAMETER(S):
6328 * [I] hwnd : window handle
6329 * [I] lpszText : text string to process
6330 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6332 * RETURN:
6333 * SUCCESS : string width (in pixels)
6334 * FAILURE : zero
6336 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6338 SIZE stringSize;
6340 stringSize.cx = 0;
6341 if (is_textT(lpszText, isW))
6343 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6344 HDC hdc = GetDC(infoPtr->hwndSelf);
6345 HFONT hOldFont = SelectObject(hdc, hFont);
6347 if (isW)
6348 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6349 else
6350 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6351 SelectObject(hdc, hOldFont);
6352 ReleaseDC(infoPtr->hwndSelf, hdc);
6354 return stringSize.cx;
6357 /***
6358 * DESCRIPTION:
6359 * Determines which listview item is located at the specified position.
6361 * PARAMETER(S):
6362 * [I] infoPtr : valid pointer to the listview structure
6363 * [IO] lpht : hit test information
6364 * [I] subitem : fill out iSubItem.
6365 * [I] select : return the index only if the hit selects the item
6367 * NOTE:
6368 * (mm 20001022): We must not allow iSubItem to be touched, for
6369 * an app might pass only a structure with space up to iItem!
6370 * (MS Office 97 does that for instance in the file open dialog)
6372 * RETURN:
6373 * SUCCESS : item index
6374 * FAILURE : -1
6376 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6378 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6379 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6380 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6381 POINT Origin, Position, opt;
6382 LVITEMW lvItem;
6383 ITERATOR i;
6384 INT iItem;
6386 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6388 lpht->flags = 0;
6389 lpht->iItem = -1;
6390 if (subitem) lpht->iSubItem = 0;
6392 if (infoPtr->rcList.left > lpht->pt.x)
6393 lpht->flags |= LVHT_TOLEFT;
6394 else if (infoPtr->rcList.right < lpht->pt.x)
6395 lpht->flags |= LVHT_TORIGHT;
6397 if (infoPtr->rcList.top > lpht->pt.y)
6398 lpht->flags |= LVHT_ABOVE;
6399 else if (infoPtr->rcList.bottom < lpht->pt.y)
6400 lpht->flags |= LVHT_BELOW;
6402 TRACE("lpht->flags=0x%x\n", lpht->flags);
6403 if (lpht->flags) return -1;
6405 lpht->flags |= LVHT_NOWHERE;
6407 LISTVIEW_GetOrigin(infoPtr, &Origin);
6409 /* first deal with the large items */
6410 rcSearch.left = lpht->pt.x;
6411 rcSearch.top = lpht->pt.y;
6412 rcSearch.right = rcSearch.left + 1;
6413 rcSearch.bottom = rcSearch.top + 1;
6415 iterator_frameditems(&i, infoPtr, &rcSearch);
6416 iterator_next(&i); /* go to first item in the sequence */
6417 iItem = i.nItem;
6418 iterator_destroy(&i);
6420 TRACE("lpht->iItem=%d\n", iItem);
6421 if (iItem == -1) return -1;
6423 lvItem.mask = LVIF_STATE | LVIF_TEXT;
6424 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6425 lvItem.stateMask = LVIS_STATEIMAGEMASK;
6426 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6427 lvItem.iItem = iItem;
6428 lvItem.iSubItem = 0;
6429 lvItem.pszText = szDispText;
6430 lvItem.cchTextMax = DISP_TEXT_SIZE;
6431 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6432 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6434 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
6435 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6436 opt.x = lpht->pt.x - Position.x - Origin.x;
6437 opt.y = lpht->pt.y - Position.y - Origin.y;
6439 if (uView == LVS_REPORT)
6440 rcBounds = rcBox;
6441 else
6443 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6444 UnionRect(&rcBounds, &rcBounds, &rcState);
6446 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6447 if (!PtInRect(&rcBounds, opt)) return -1;
6449 if (PtInRect(&rcIcon, opt))
6450 lpht->flags |= LVHT_ONITEMICON;
6451 else if (PtInRect(&rcLabel, opt))
6452 lpht->flags |= LVHT_ONITEMLABEL;
6453 else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6454 lpht->flags |= LVHT_ONITEMSTATEICON;
6455 if (lpht->flags & LVHT_ONITEM)
6456 lpht->flags &= ~LVHT_NOWHERE;
6458 TRACE("lpht->flags=0x%x\n", lpht->flags);
6459 if (uView == LVS_REPORT && subitem)
6461 INT j;
6463 rcBounds.right = rcBounds.left;
6464 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6466 rcBounds.left = rcBounds.right;
6467 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6468 if (PtInRect(&rcBounds, opt))
6470 lpht->iSubItem = j;
6471 break;
6476 if (select && !(uView == LVS_REPORT &&
6477 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6478 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6480 if (uView == LVS_REPORT)
6482 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6483 UnionRect(&rcBounds, &rcBounds, &rcState);
6485 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6487 return lpht->iItem = iItem;
6491 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6492 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6493 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6494 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6495 their own sort proc. when sending LVM_SORTITEMS.
6497 /* Platform SDK:
6498 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6500 LVS_SORTXXX must be specified,
6501 LVS_OWNERDRAW is not set,
6502 <item>.pszText is not LPSTR_TEXTCALLBACK.
6504 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6505 are sorted based on item text..."
6507 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6509 ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
6510 ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
6511 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6513 /* if we're sorting descending, negate the return value */
6514 return (((const LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6517 /***
6518 * DESCRIPTION:
6519 * Inserts a new item in the listview control.
6521 * PARAMETER(S):
6522 * [I] infoPtr : valid pointer to the listview structure
6523 * [I] lpLVItem : item information
6524 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6526 * RETURN:
6527 * SUCCESS : new item index
6528 * FAILURE : -1
6530 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6532 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6533 INT nItem;
6534 HDPA hdpaSubItems;
6535 NMLISTVIEW nmlv;
6536 ITEM_INFO *lpItem;
6537 BOOL is_sorted, has_changed;
6538 LVITEMW item;
6539 HWND hwndSelf = infoPtr->hwndSelf;
6541 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6543 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6545 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6546 if (!lpLVItem || lpLVItem->iSubItem) return -1;
6548 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6550 if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
6552 /* insert item in listview control data structure */
6553 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6554 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6556 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6557 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6559 if (lpLVItem->iItem < 0 && !is_sorted) return -1;
6561 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6562 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6563 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6564 if (nItem == -1) goto fail;
6565 infoPtr->nItemCount++;
6567 /* shift indices first so they don't get tangled */
6568 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6570 /* set the item attributes */
6571 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6573 /* full size structure expected - _WIN32IE >= 0x560 */
6574 item = *lpLVItem;
6576 else if (lpLVItem->mask & LVIF_INDENT)
6578 /* indent member expected - _WIN32IE >= 0x300 */
6579 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6581 else
6583 /* minimal structure expected */
6584 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6586 item.iItem = nItem;
6587 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6589 item.mask |= LVIF_STATE;
6590 item.stateMask |= LVIS_STATEIMAGEMASK;
6591 item.state &= ~LVIS_STATEIMAGEMASK;
6592 item.state |= INDEXTOSTATEIMAGEMASK(1);
6594 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6596 /* if we're sorted, sort the list, and update the index */
6597 if (is_sorted)
6599 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6600 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6601 assert(nItem != -1);
6604 /* make room for the position, if we are in the right mode */
6605 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6607 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6608 goto undo;
6609 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6611 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6612 goto undo;
6616 /* send LVN_INSERTITEM notification */
6617 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6618 nmlv.iItem = nItem;
6619 nmlv.lParam = lpItem->lParam;
6620 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6621 if (!IsWindow(hwndSelf))
6622 return -1;
6624 /* align items (set position of each item) */
6625 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6627 POINT pt;
6629 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6630 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6631 else
6632 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6634 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6637 /* now is the invalidation fun */
6638 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6639 return nItem;
6641 undo:
6642 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6643 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6644 infoPtr->nItemCount--;
6645 fail:
6646 DPA_DeletePtr(hdpaSubItems, 0);
6647 DPA_Destroy (hdpaSubItems);
6648 Free (lpItem);
6649 return -1;
6652 /***
6653 * DESCRIPTION:
6654 * Redraws a range of items.
6656 * PARAMETER(S):
6657 * [I] infoPtr : valid pointer to the listview structure
6658 * [I] nFirst : first item
6659 * [I] nLast : last item
6661 * RETURN:
6662 * SUCCESS : TRUE
6663 * FAILURE : FALSE
6665 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6667 INT i;
6669 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6670 max(nFirst, nLast) >= infoPtr->nItemCount)
6671 return FALSE;
6673 for (i = nFirst; i <= nLast; i++)
6674 LISTVIEW_InvalidateItem(infoPtr, i);
6676 return TRUE;
6679 /***
6680 * DESCRIPTION:
6681 * Scroll the content of a listview.
6683 * PARAMETER(S):
6684 * [I] infoPtr : valid pointer to the listview structure
6685 * [I] dx : horizontal scroll amount in pixels
6686 * [I] dy : vertical scroll amount in pixels
6688 * RETURN:
6689 * SUCCESS : TRUE
6690 * FAILURE : FALSE
6692 * COMMENTS:
6693 * If the control is in report mode (LVS_REPORT) the control can
6694 * be scrolled only in line increments. "dy" will be rounded to the
6695 * nearest number of pixels that are a whole line. Ex: if line height
6696 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6697 * is passed, then the scroll will be 0. (per MSDN 7/2002)
6699 * For: (per experimentation with native control and CSpy ListView)
6700 * LVS_ICON dy=1 = 1 pixel (vertical only)
6701 * dx ignored
6702 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6703 * dx ignored
6704 * LVS_LIST dx=1 = 1 column (horizontal only)
6705 * but will only scroll 1 column per message
6706 * no matter what the value.
6707 * dy must be 0 or FALSE returned.
6708 * LVS_REPORT dx=1 = 1 pixel
6709 * dy= see above
6712 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6714 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6715 case LVS_REPORT:
6716 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6717 dy /= infoPtr->nItemHeight;
6718 break;
6719 case LVS_LIST:
6720 if (dy != 0) return FALSE;
6721 break;
6722 default: /* icon */
6723 dx = 0;
6724 break;
6727 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6728 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6730 return TRUE;
6733 /***
6734 * DESCRIPTION:
6735 * Sets the background color.
6737 * PARAMETER(S):
6738 * [I] infoPtr : valid pointer to the listview structure
6739 * [I] clrBk : background color
6741 * RETURN:
6742 * SUCCESS : TRUE
6743 * FAILURE : FALSE
6745 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6747 TRACE("(clrBk=%x)\n", clrBk);
6749 if(infoPtr->clrBk != clrBk) {
6750 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6751 infoPtr->clrBk = clrBk;
6752 if (clrBk == CLR_NONE)
6753 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6754 else
6755 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6756 LISTVIEW_InvalidateList(infoPtr);
6759 return TRUE;
6762 /* LISTVIEW_SetBkImage */
6764 /*** Helper for {Insert,Set}ColumnT *only* */
6765 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
6766 const LVCOLUMNW *lpColumn, BOOL isW)
6768 if (lpColumn->mask & LVCF_FMT)
6770 /* format member is valid */
6771 lphdi->mask |= HDI_FORMAT;
6773 /* set text alignment (leftmost column must be left-aligned) */
6774 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6775 lphdi->fmt |= HDF_LEFT;
6776 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6777 lphdi->fmt |= HDF_RIGHT;
6778 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6779 lphdi->fmt |= HDF_CENTER;
6781 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6782 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6784 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6786 lphdi->fmt |= HDF_IMAGE;
6787 lphdi->iImage = I_IMAGECALLBACK;
6791 if (lpColumn->mask & LVCF_WIDTH)
6793 lphdi->mask |= HDI_WIDTH;
6794 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6796 /* make it fill the remainder of the controls width */
6797 RECT rcHeader;
6798 INT item_index;
6800 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6802 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6803 lphdi->cxy += rcHeader.right - rcHeader.left;
6806 /* retrieve the layout of the header */
6807 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6808 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
6810 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6812 else
6813 lphdi->cxy = lpColumn->cx;
6816 if (lpColumn->mask & LVCF_TEXT)
6818 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6819 lphdi->fmt |= HDF_STRING;
6820 lphdi->pszText = lpColumn->pszText;
6821 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6824 if (lpColumn->mask & LVCF_IMAGE)
6826 lphdi->mask |= HDI_IMAGE;
6827 lphdi->iImage = lpColumn->iImage;
6830 if (lpColumn->mask & LVCF_ORDER)
6832 lphdi->mask |= HDI_ORDER;
6833 lphdi->iOrder = lpColumn->iOrder;
6838 /***
6839 * DESCRIPTION:
6840 * Inserts a new column.
6842 * PARAMETER(S):
6843 * [I] infoPtr : valid pointer to the listview structure
6844 * [I] nColumn : column index
6845 * [I] lpColumn : column information
6846 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6848 * RETURN:
6849 * SUCCESS : new column index
6850 * FAILURE : -1
6852 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6853 const LVCOLUMNW *lpColumn, BOOL isW)
6855 COLUMN_INFO *lpColumnInfo;
6856 INT nNewColumn;
6857 HDITEMW hdi;
6859 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6861 if (!lpColumn || nColumn < 0) return -1;
6862 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6864 ZeroMemory(&hdi, sizeof(HDITEMW));
6865 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6868 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
6869 * (can be seen in SPY) otherwise column never gets added.
6871 if (!(lpColumn->mask & LVCF_WIDTH)) {
6872 hdi.mask |= HDI_WIDTH;
6873 hdi.cxy = 10;
6877 * when the iSubItem is available Windows copies it to the header lParam. It seems
6878 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
6880 if (lpColumn->mask & LVCF_SUBITEM)
6882 hdi.mask |= HDI_LPARAM;
6883 hdi.lParam = lpColumn->iSubItem;
6886 /* insert item in header control */
6887 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6888 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6889 (WPARAM)nColumn, (LPARAM)&hdi);
6890 if (nNewColumn == -1) return -1;
6891 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6893 /* create our own column info */
6894 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6895 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6897 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6898 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6900 /* now we have to actually adjust the data */
6901 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6903 SUBITEM_INFO *lpSubItem;
6904 HDPA hdpaSubItems;
6905 INT nItem, i;
6907 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6909 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
6910 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6912 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
6913 if (lpSubItem->iSubItem >= nNewColumn)
6914 lpSubItem->iSubItem++;
6919 /* make space for the new column */
6920 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6921 LISTVIEW_UpdateItemSize(infoPtr);
6923 return nNewColumn;
6925 fail:
6926 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6927 if (lpColumnInfo)
6929 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6930 Free(lpColumnInfo);
6932 return -1;
6935 /***
6936 * DESCRIPTION:
6937 * Sets the attributes of a header item.
6939 * PARAMETER(S):
6940 * [I] infoPtr : valid pointer to the listview structure
6941 * [I] nColumn : column index
6942 * [I] lpColumn : column attributes
6943 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6945 * RETURN:
6946 * SUCCESS : TRUE
6947 * FAILURE : FALSE
6949 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
6950 const LVCOLUMNW *lpColumn, BOOL isW)
6952 HDITEMW hdi, hdiget;
6953 BOOL bResult;
6955 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6957 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6959 ZeroMemory(&hdi, sizeof(HDITEMW));
6960 if (lpColumn->mask & LVCF_FMT)
6962 hdi.mask |= HDI_FORMAT;
6963 hdiget.mask = HDI_FORMAT;
6964 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6965 hdi.fmt = hdiget.fmt & HDF_STRING;
6967 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6969 /* set header item attributes */
6970 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6971 if (!bResult) return FALSE;
6973 if (lpColumn->mask & LVCF_FMT)
6975 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6976 int oldFmt = lpColumnInfo->fmt;
6978 lpColumnInfo->fmt = lpColumn->fmt;
6979 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6981 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6982 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6986 return TRUE;
6989 /***
6990 * DESCRIPTION:
6991 * Sets the column order array
6993 * PARAMETERS:
6994 * [I] infoPtr : valid pointer to the listview structure
6995 * [I] iCount : number of elements in column order array
6996 * [I] lpiArray : pointer to column order array
6998 * RETURN:
6999 * SUCCESS : TRUE
7000 * FAILURE : FALSE
7002 static BOOL LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
7004 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7006 if (!lpiArray)
7007 return FALSE;
7009 return TRUE;
7013 /***
7014 * DESCRIPTION:
7015 * Sets the width of a column
7017 * PARAMETERS:
7018 * [I] infoPtr : valid pointer to the listview structure
7019 * [I] nColumn : column index
7020 * [I] cx : column width
7022 * RETURN:
7023 * SUCCESS : TRUE
7024 * FAILURE : FALSE
7026 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
7028 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7029 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
7030 INT max_cx = 0;
7031 HDITEMW hdi;
7033 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
7035 /* set column width only if in report or list mode */
7036 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
7038 /* take care of invalid cx values */
7039 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
7040 else if (uView == LVS_LIST && cx < 1) return FALSE;
7042 /* resize all columns if in LVS_LIST mode */
7043 if(uView == LVS_LIST)
7045 infoPtr->nItemWidth = cx;
7046 LISTVIEW_InvalidateList(infoPtr);
7047 return TRUE;
7050 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7052 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
7054 INT nLabelWidth;
7055 LVITEMW lvItem;
7057 lvItem.mask = LVIF_TEXT;
7058 lvItem.iItem = 0;
7059 lvItem.iSubItem = nColumn;
7060 lvItem.pszText = szDispText;
7061 lvItem.cchTextMax = DISP_TEXT_SIZE;
7062 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7064 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
7065 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
7066 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
7068 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
7069 max_cx += infoPtr->iconSize.cx;
7070 max_cx += TRAILING_LABEL_PADDING;
7073 /* autosize based on listview items width */
7074 if(cx == LVSCW_AUTOSIZE)
7075 cx = max_cx;
7076 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7078 /* if iCol is the last column make it fill the remainder of the controls width */
7079 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
7081 RECT rcHeader;
7082 POINT Origin;
7084 LISTVIEW_GetOrigin(infoPtr, &Origin);
7085 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
7087 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
7089 else
7091 /* Despite what the MS docs say, if this is not the last
7092 column, then MS resizes the column to the width of the
7093 largest text string in the column, including headers
7094 and items. This is different from LVSCW_AUTOSIZE in that
7095 LVSCW_AUTOSIZE ignores the header string length. */
7096 cx = 0;
7098 /* retrieve header text */
7099 hdi.mask = HDI_TEXT;
7100 hdi.cchTextMax = DISP_TEXT_SIZE;
7101 hdi.pszText = szDispText;
7102 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
7104 HDC hdc = GetDC(infoPtr->hwndSelf);
7105 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
7106 SIZE size;
7108 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
7109 cx = size.cx + TRAILING_HEADER_PADDING;
7110 /* FIXME: Take into account the header image, if one is present */
7111 SelectObject(hdc, old_font);
7112 ReleaseDC(infoPtr->hwndSelf, hdc);
7114 cx = max (cx, max_cx);
7118 if (cx < 0) return FALSE;
7120 /* call header to update the column change */
7121 hdi.mask = HDI_WIDTH;
7122 hdi.cxy = cx;
7123 TRACE("hdi.cxy=%d\n", hdi.cxy);
7124 return Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7127 /***
7128 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
7131 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
7133 HDC hdc_wnd, hdc;
7134 HBITMAP hbm_im, hbm_mask, hbm_orig;
7135 RECT rc;
7136 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
7137 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
7138 HIMAGELIST himl;
7140 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
7141 ILC_COLOR | ILC_MASK, 2, 2);
7142 hdc_wnd = GetDC(infoPtr->hwndSelf);
7143 hdc = CreateCompatibleDC(hdc_wnd);
7144 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
7145 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
7146 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
7148 rc.left = rc.top = 0;
7149 rc.right = GetSystemMetrics(SM_CXSMICON);
7150 rc.bottom = GetSystemMetrics(SM_CYSMICON);
7152 hbm_orig = SelectObject(hdc, hbm_mask);
7153 FillRect(hdc, &rc, hbr_white);
7154 InflateRect(&rc, -3, -3);
7155 FillRect(hdc, &rc, hbr_black);
7157 SelectObject(hdc, hbm_im);
7158 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
7159 SelectObject(hdc, hbm_orig);
7160 ImageList_Add(himl, hbm_im, hbm_mask);
7162 SelectObject(hdc, hbm_im);
7163 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
7164 SelectObject(hdc, hbm_orig);
7165 ImageList_Add(himl, hbm_im, hbm_mask);
7167 DeleteObject(hbm_mask);
7168 DeleteObject(hbm_im);
7169 DeleteDC(hdc);
7171 return himl;
7174 /***
7175 * DESCRIPTION:
7176 * Sets the extended listview style.
7178 * PARAMETERS:
7179 * [I] infoPtr : valid pointer to the listview structure
7180 * [I] dwMask : mask
7181 * [I] dwStyle : style
7183 * RETURN:
7184 * SUCCESS : previous style
7185 * FAILURE : 0
7187 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwExStyle)
7189 DWORD dwOldExStyle = infoPtr->dwLvExStyle;
7191 /* set new style */
7192 if (dwMask)
7193 infoPtr->dwLvExStyle = (dwOldExStyle & ~dwMask) | (dwExStyle & dwMask);
7194 else
7195 infoPtr->dwLvExStyle = dwExStyle;
7197 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_CHECKBOXES)
7199 HIMAGELIST himl = 0;
7200 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7202 LVITEMW item;
7203 item.mask = LVIF_STATE;
7204 item.stateMask = LVIS_STATEIMAGEMASK;
7205 item.state = INDEXTOSTATEIMAGEMASK(1);
7206 LISTVIEW_SetItemState(infoPtr, -1, &item);
7208 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
7210 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
7213 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_HEADERDRAGDROP)
7215 DWORD dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
7216 if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
7217 dwStyle |= HDS_DRAGDROP;
7218 else
7219 dwStyle &= ~HDS_DRAGDROP;
7220 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
7223 /* GRIDLINES adds decoration at top so changes sizes */
7224 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_GRIDLINES)
7226 LISTVIEW_UpdateSize(infoPtr);
7230 LISTVIEW_InvalidateList(infoPtr);
7231 return dwOldExStyle;
7234 /***
7235 * DESCRIPTION:
7236 * Sets the new hot cursor used during hot tracking and hover selection.
7238 * PARAMETER(S):
7239 * [I] infoPtr : valid pointer to the listview structure
7240 * [I] hCursor : the new hot cursor handle
7242 * RETURN:
7243 * Returns the previous hot cursor
7245 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
7247 HCURSOR oldCursor = infoPtr->hHotCursor;
7249 infoPtr->hHotCursor = hCursor;
7251 return oldCursor;
7255 /***
7256 * DESCRIPTION:
7257 * Sets the hot item index.
7259 * PARAMETERS:
7260 * [I] infoPtr : valid pointer to the listview structure
7261 * [I] iIndex : index
7263 * RETURN:
7264 * SUCCESS : previous hot item index
7265 * FAILURE : -1 (no hot item)
7267 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
7269 INT iOldIndex = infoPtr->nHotItem;
7271 infoPtr->nHotItem = iIndex;
7273 return iOldIndex;
7277 /***
7278 * DESCRIPTION:
7279 * Sets the amount of time the cursor must hover over an item before it is selected.
7281 * PARAMETER(S):
7282 * [I] infoPtr : valid pointer to the listview structure
7283 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7285 * RETURN:
7286 * Returns the previous hover time
7288 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
7290 DWORD oldHoverTime = infoPtr->dwHoverTime;
7292 infoPtr->dwHoverTime = dwHoverTime;
7294 return oldHoverTime;
7297 /***
7298 * DESCRIPTION:
7299 * Sets spacing for icons of LVS_ICON style.
7301 * PARAMETER(S):
7302 * [I] infoPtr : valid pointer to the listview structure
7303 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7304 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7306 * RETURN:
7307 * MAKELONG(oldcx, oldcy)
7309 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
7311 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7312 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7314 TRACE("requested=(%d,%d)\n", cx, cy);
7316 /* this is supported only for LVS_ICON style */
7317 if (uView != LVS_ICON) return oldspacing;
7319 /* set to defaults, if instructed to */
7320 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
7321 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
7323 /* if 0 then compute width
7324 * FIXME: Should scan each item and determine max width of
7325 * icon or label, then make that the width */
7326 if (cx == 0)
7327 cx = infoPtr->iconSpacing.cx;
7329 /* if 0 then compute height */
7330 if (cy == 0)
7331 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
7332 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
7335 infoPtr->iconSpacing.cx = cx;
7336 infoPtr->iconSpacing.cy = cy;
7338 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7339 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
7340 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7341 infoPtr->ntmHeight);
7343 /* these depend on the iconSpacing */
7344 LISTVIEW_UpdateItemSize(infoPtr);
7346 return oldspacing;
7349 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
7351 INT cx, cy;
7353 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
7355 size->cx = cx;
7356 size->cy = cy;
7358 else
7360 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
7361 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7365 /***
7366 * DESCRIPTION:
7367 * Sets image lists.
7369 * PARAMETER(S):
7370 * [I] infoPtr : valid pointer to the listview structure
7371 * [I] nType : image list type
7372 * [I] himl : image list handle
7374 * RETURN:
7375 * SUCCESS : old image list
7376 * FAILURE : NULL
7378 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7380 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7381 INT oldHeight = infoPtr->nItemHeight;
7382 HIMAGELIST himlOld = 0;
7384 TRACE("(nType=%d, himl=%p\n", nType, himl);
7386 switch (nType)
7388 case LVSIL_NORMAL:
7389 himlOld = infoPtr->himlNormal;
7390 infoPtr->himlNormal = himl;
7391 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7392 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7393 break;
7395 case LVSIL_SMALL:
7396 himlOld = infoPtr->himlSmall;
7397 infoPtr->himlSmall = himl;
7398 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7399 break;
7401 case LVSIL_STATE:
7402 himlOld = infoPtr->himlState;
7403 infoPtr->himlState = himl;
7404 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7405 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7406 break;
7408 default:
7409 ERR("Unknown icon type=%d\n", nType);
7410 return NULL;
7413 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7414 if (infoPtr->nItemHeight != oldHeight)
7415 LISTVIEW_UpdateScroll(infoPtr);
7417 return himlOld;
7420 /***
7421 * DESCRIPTION:
7422 * Preallocates memory (does *not* set the actual count of items !)
7424 * PARAMETER(S):
7425 * [I] infoPtr : valid pointer to the listview structure
7426 * [I] nItems : item count (projected number of items to allocate)
7427 * [I] dwFlags : update flags
7429 * RETURN:
7430 * SUCCESS : TRUE
7431 * FAILURE : FALSE
7433 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7435 TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
7437 if (infoPtr->dwStyle & LVS_OWNERDATA)
7439 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7440 INT nOldCount = infoPtr->nItemCount;
7442 if (nItems < nOldCount)
7444 RANGE range = { nItems, nOldCount };
7445 ranges_del(infoPtr->selectionRanges, range);
7446 if (infoPtr->nFocusedItem >= nItems)
7448 infoPtr->nFocusedItem = -1;
7449 SetRectEmpty(&infoPtr->rcFocus);
7453 infoPtr->nItemCount = nItems;
7454 LISTVIEW_UpdateScroll(infoPtr);
7456 /* the flags are valid only in ownerdata report and list modes */
7457 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7459 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7460 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7462 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7463 LISTVIEW_InvalidateList(infoPtr);
7464 else
7466 INT nFrom, nTo;
7467 POINT Origin;
7468 RECT rcErase;
7470 LISTVIEW_GetOrigin(infoPtr, &Origin);
7471 nFrom = min(nOldCount, nItems);
7472 nTo = max(nOldCount, nItems);
7474 if (uView == LVS_REPORT)
7476 rcErase.left = 0;
7477 rcErase.top = nFrom * infoPtr->nItemHeight;
7478 rcErase.right = infoPtr->nItemWidth;
7479 rcErase.bottom = nTo * infoPtr->nItemHeight;
7480 OffsetRect(&rcErase, Origin.x, Origin.y);
7481 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7482 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7484 else /* LVS_LIST */
7486 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7488 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7489 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7490 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7491 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7492 OffsetRect(&rcErase, Origin.x, Origin.y);
7493 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7494 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7496 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7497 rcErase.top = 0;
7498 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7499 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7500 OffsetRect(&rcErase, Origin.x, Origin.y);
7501 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7502 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7506 else
7508 /* According to MSDN for non-LVS_OWNERDATA this is just
7509 * a performance issue. The control allocates its internal
7510 * data structures for the number of items specified. It
7511 * cuts down on the number of memory allocations. Therefore
7512 * we will just issue a WARN here
7514 WARN("for non-ownerdata performance option not implemented.\n");
7517 return TRUE;
7520 /***
7521 * DESCRIPTION:
7522 * Sets the position of an item.
7524 * PARAMETER(S):
7525 * [I] infoPtr : valid pointer to the listview structure
7526 * [I] nItem : item index
7527 * [I] pt : coordinate
7529 * RETURN:
7530 * SUCCESS : TRUE
7531 * FAILURE : FALSE
7533 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7535 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7536 POINT Origin;
7538 TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7540 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7541 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7543 LISTVIEW_GetOrigin(infoPtr, &Origin);
7545 /* This point value seems to be an undocumented feature.
7546 * The best guess is that it means either at the origin,
7547 * or at true beginning of the list. I will assume the origin. */
7548 if ((pt.x == -1) && (pt.y == -1))
7549 pt = Origin;
7551 if (uView == LVS_ICON)
7553 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7554 pt.y -= ICON_TOP_PADDING;
7556 pt.x -= Origin.x;
7557 pt.y -= Origin.y;
7559 infoPtr->bAutoarrange = FALSE;
7561 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7564 /***
7565 * DESCRIPTION:
7566 * Sets the state of one or many items.
7568 * PARAMETER(S):
7569 * [I] infoPtr : valid pointer to the listview structure
7570 * [I] nItem : item index
7571 * [I] lpLVItem : item or subitem info
7573 * RETURN:
7574 * SUCCESS : TRUE
7575 * FAILURE : FALSE
7577 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7579 BOOL bResult = TRUE;
7580 LVITEMW lvItem;
7582 lvItem.iItem = nItem;
7583 lvItem.iSubItem = 0;
7584 lvItem.mask = LVIF_STATE;
7585 lvItem.state = lpLVItem->state;
7586 lvItem.stateMask = lpLVItem->stateMask;
7587 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7589 if (nItem == -1)
7591 /* apply to all items */
7592 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7593 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7595 else
7596 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7599 * Update selection mark
7601 * Investigation on windows 2k showed that selection mark was updated
7602 * whenever a new selection was made, but if the selected item was
7603 * unselected it was not updated.
7605 * we are probably still not 100% accurate, but this at least sets the
7606 * proper selection mark when it is needed
7609 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7610 (infoPtr->nSelectionMark == -1))
7612 int i;
7613 for (i = 0; i < infoPtr->nItemCount; i++)
7615 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7617 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7619 infoPtr->nSelectionMark = i;
7620 break;
7623 else if (ranges_contain(infoPtr->selectionRanges, i))
7625 infoPtr->nSelectionMark = i;
7626 break;
7631 return bResult;
7634 /***
7635 * DESCRIPTION:
7636 * Sets the text of an item or subitem.
7638 * PARAMETER(S):
7639 * [I] hwnd : window handle
7640 * [I] nItem : item index
7641 * [I] lpLVItem : item or subitem info
7642 * [I] isW : TRUE if input is Unicode
7644 * RETURN:
7645 * SUCCESS : TRUE
7646 * FAILURE : FALSE
7648 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7650 LVITEMW lvItem;
7652 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7654 lvItem.iItem = nItem;
7655 lvItem.iSubItem = lpLVItem->iSubItem;
7656 lvItem.mask = LVIF_TEXT;
7657 lvItem.pszText = lpLVItem->pszText;
7658 lvItem.cchTextMax = lpLVItem->cchTextMax;
7660 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7662 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7665 /***
7666 * DESCRIPTION:
7667 * Set item index that marks the start of a multiple selection.
7669 * PARAMETER(S):
7670 * [I] infoPtr : valid pointer to the listview structure
7671 * [I] nIndex : index
7673 * RETURN:
7674 * Index number or -1 if there is no selection mark.
7676 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7678 INT nOldIndex = infoPtr->nSelectionMark;
7680 TRACE("(nIndex=%d)\n", nIndex);
7682 infoPtr->nSelectionMark = nIndex;
7684 return nOldIndex;
7687 /***
7688 * DESCRIPTION:
7689 * Sets the text background color.
7691 * PARAMETER(S):
7692 * [I] infoPtr : valid pointer to the listview structure
7693 * [I] clrTextBk : text background color
7695 * RETURN:
7696 * SUCCESS : TRUE
7697 * FAILURE : FALSE
7699 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7701 TRACE("(clrTextBk=%x)\n", clrTextBk);
7703 if (infoPtr->clrTextBk != clrTextBk)
7705 infoPtr->clrTextBk = clrTextBk;
7706 LISTVIEW_InvalidateList(infoPtr);
7709 return TRUE;
7712 /***
7713 * DESCRIPTION:
7714 * Sets the text foreground color.
7716 * PARAMETER(S):
7717 * [I] infoPtr : valid pointer to the listview structure
7718 * [I] clrText : text color
7720 * RETURN:
7721 * SUCCESS : TRUE
7722 * FAILURE : FALSE
7724 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7726 TRACE("(clrText=%x)\n", clrText);
7728 if (infoPtr->clrText != clrText)
7730 infoPtr->clrText = clrText;
7731 LISTVIEW_InvalidateList(infoPtr);
7734 return TRUE;
7737 /***
7738 * DESCRIPTION:
7739 * Determines which listview item is located at the specified position.
7741 * PARAMETER(S):
7742 * [I] infoPtr : valid pointer to the listview structure
7743 * [I] hwndNewToolTip : handle to new ToolTip
7745 * RETURN:
7746 * old tool tip
7748 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7750 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7751 infoPtr->hwndToolTip = hwndNewToolTip;
7752 return hwndOldToolTip;
7756 * DESCRIPTION:
7757 * sets the Unicode character format flag for the control
7758 * PARAMETER(S):
7759 * [I] infoPtr :valid pointer to the listview structure
7760 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
7762 * RETURN:
7763 * Old Unicode Format
7765 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL fUnicode)
7767 BOOL rc = infoPtr->notifyFormat;
7768 infoPtr->notifyFormat = (fUnicode)?NFR_UNICODE:NFR_ANSI;
7769 return rc;
7772 /* LISTVIEW_SetWorkAreas */
7774 /***
7775 * DESCRIPTION:
7776 * Callback internally used by LISTVIEW_SortItems()
7778 * PARAMETER(S):
7779 * [I] first : pointer to first ITEM_INFO to compare
7780 * [I] second : pointer to second ITEM_INFO to compare
7781 * [I] lParam : HWND of control
7783 * RETURN:
7784 * if first comes before second : negative
7785 * if first comes after second : positive
7786 * if first and second are equivalent : zero
7788 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7790 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7791 ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
7792 ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
7794 /* Forward the call to the client defined callback */
7795 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7798 /***
7799 * DESCRIPTION:
7800 * Sorts the listview items.
7802 * PARAMETER(S):
7803 * [I] infoPtr : valid pointer to the listview structure
7804 * [I] pfnCompare : application-defined value
7805 * [I] lParamSort : pointer to comparison callback
7807 * RETURN:
7808 * SUCCESS : TRUE
7809 * FAILURE : FALSE
7811 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7813 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7814 HDPA hdpaSubItems;
7815 ITEM_INFO *lpItem;
7816 LPVOID selectionMarkItem;
7817 LVITEMW item;
7818 int i;
7820 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7822 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7824 if (!pfnCompare) return FALSE;
7825 if (!infoPtr->hdpaItems) return FALSE;
7827 /* if there are 0 or 1 items, there is no need to sort */
7828 if (infoPtr->nItemCount < 2) return TRUE;
7830 if (infoPtr->nFocusedItem >= 0)
7832 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7833 lpItem = DPA_GetPtr(hdpaSubItems, 0);
7834 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7836 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7837 /* clear the lpItem->state for non-selected ones */
7838 /* remove the selection ranges */
7840 infoPtr->pfnCompare = pfnCompare;
7841 infoPtr->lParamSort = lParamSort;
7842 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7844 /* Adjust selections and indices so that they are the way they should
7845 * be after the sort (otherwise, the list items move around, but
7846 * whatever is at the item's previous original position will be
7847 * selected instead)
7849 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7850 for (i=0; i < infoPtr->nItemCount; i++)
7852 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
7853 lpItem = DPA_GetPtr(hdpaSubItems, 0);
7855 if (lpItem->state & LVIS_SELECTED)
7857 item.state = LVIS_SELECTED;
7858 item.stateMask = LVIS_SELECTED;
7859 LISTVIEW_SetItemState(infoPtr, i, &item);
7861 if (lpItem->state & LVIS_FOCUSED)
7863 infoPtr->nFocusedItem = i;
7864 lpItem->state &= ~LVIS_FOCUSED;
7867 if (selectionMarkItem != NULL)
7868 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7869 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7871 /* refresh the display */
7872 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7873 LISTVIEW_InvalidateList(infoPtr);
7875 return TRUE;
7878 /***
7879 * DESCRIPTION:
7880 * Update theme handle after a theme change.
7882 * PARAMETER(S):
7883 * [I] infoPtr : valid pointer to the listview structure
7885 * RETURN:
7886 * SUCCESS : 0
7887 * FAILURE : something else
7889 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr)
7891 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7892 CloseThemeData(theme);
7893 OpenThemeData(infoPtr->hwndSelf, themeClass);
7894 return 0;
7897 /***
7898 * DESCRIPTION:
7899 * Updates an items or rearranges the listview control.
7901 * PARAMETER(S):
7902 * [I] infoPtr : valid pointer to the listview structure
7903 * [I] nItem : item index
7905 * RETURN:
7906 * SUCCESS : TRUE
7907 * FAILURE : FALSE
7909 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7911 TRACE("(nItem=%d)\n", nItem);
7913 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7915 /* rearrange with default alignment style */
7916 if (is_autoarrange(infoPtr))
7917 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7918 else
7919 LISTVIEW_InvalidateItem(infoPtr, nItem);
7921 return TRUE;
7924 /***
7925 * DESCRIPTION:
7926 * Draw the track line at the place defined in the infoPtr structure.
7927 * The line is drawn with a XOR pen so drawing the line for the second time
7928 * in the same place erases the line.
7930 * PARAMETER(S):
7931 * [I] infoPtr : valid pointer to the listview structure
7933 * RETURN:
7934 * SUCCESS : TRUE
7935 * FAILURE : FALSE
7937 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
7939 HPEN hOldPen;
7940 HDC hdc;
7941 INT oldROP;
7943 if (infoPtr->xTrackLine == -1)
7944 return FALSE;
7946 if (!(hdc = GetDC(infoPtr->hwndSelf)))
7947 return FALSE;
7948 hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
7949 oldROP = SetROP2(hdc, R2_XORPEN);
7950 MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
7951 LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
7952 SetROP2(hdc, oldROP);
7953 SelectObject(hdc, hOldPen);
7954 ReleaseDC(infoPtr->hwndSelf, hdc);
7955 return TRUE;
7958 /***
7959 * DESCRIPTION:
7960 * Called when an edit control should be displayed. This function is called after
7961 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
7963 * PARAMETER(S):
7964 * [I] hwnd : Handle to the listview
7965 * [I] uMsg : WM_TIMER (ignored)
7966 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
7967 * [I] dwTimer : The elapsed time (ignored)
7969 * RETURN:
7970 * None.
7972 static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
7974 DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
7975 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
7977 KillTimer(hwnd, idEvent);
7978 editItem->fEnabled = FALSE;
7979 /* check if the item is still selected */
7980 if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
7981 LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
7984 /***
7985 * DESCRIPTION:
7986 * Creates the listview control - the WM_NCCREATE phase.
7988 * PARAMETER(S):
7989 * [I] hwnd : window handle
7990 * [I] lpcs : the create parameters
7992 * RETURN:
7993 * Success: TRUE
7994 * Failure: FALSE
7996 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
7998 LISTVIEW_INFO *infoPtr;
7999 LOGFONTW logFont;
8001 TRACE("(lpcs=%p)\n", lpcs);
8003 /* initialize info pointer */
8004 infoPtr = Alloc(sizeof(LISTVIEW_INFO));
8005 if (!infoPtr) return FALSE;
8007 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
8009 infoPtr->hwndSelf = hwnd;
8010 infoPtr->dwStyle = lpcs->style; /* Note: may be changed in WM_CREATE */
8011 /* determine the type of structures to use */
8012 infoPtr->hwndNotify = lpcs->hwndParent;
8013 /* infoPtr->notifyFormat will be filled in WM_CREATE */
8015 /* initialize color information */
8016 infoPtr->clrBk = CLR_NONE;
8017 infoPtr->clrText = CLR_DEFAULT;
8018 infoPtr->clrTextBk = CLR_DEFAULT;
8019 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
8021 /* set default values */
8022 infoPtr->nFocusedItem = -1;
8023 infoPtr->nSelectionMark = -1;
8024 infoPtr->nHotItem = -1;
8025 infoPtr->bRedraw = TRUE;
8026 infoPtr->bNoItemMetrics = TRUE;
8027 infoPtr->bDoChangeNotify = TRUE;
8028 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
8029 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
8030 infoPtr->nEditLabelItem = -1;
8031 infoPtr->dwHoverTime = -1; /* default system hover time */
8032 infoPtr->nMeasureItemHeight = 0;
8033 infoPtr->xTrackLine = -1; /* no track line */
8034 infoPtr->itemEdit.fEnabled = FALSE;
8036 /* get default font (icon title) */
8037 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
8038 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
8039 infoPtr->hFont = infoPtr->hDefaultFont;
8040 LISTVIEW_SaveTextMetrics(infoPtr);
8042 /* allocate memory for the data structure */
8043 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
8044 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
8045 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
8046 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
8047 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
8048 return TRUE;
8050 fail:
8051 DestroyWindow(infoPtr->hwndHeader);
8052 ranges_destroy(infoPtr->selectionRanges);
8053 DPA_Destroy(infoPtr->hdpaItems);
8054 DPA_Destroy(infoPtr->hdpaPosX);
8055 DPA_Destroy(infoPtr->hdpaPosY);
8056 DPA_Destroy(infoPtr->hdpaColumns);
8057 Free(infoPtr);
8058 return FALSE;
8061 /***
8062 * DESCRIPTION:
8063 * Creates the listview control - the WM_CREATE phase. Most of the data is
8064 * already set up in LISTVIEW_NCCreate
8066 * PARAMETER(S):
8067 * [I] hwnd : window handle
8068 * [I] lpcs : the create parameters
8070 * RETURN:
8071 * Success: 0
8072 * Failure: -1
8074 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
8076 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8077 UINT uView = lpcs->style & LVS_TYPEMASK;
8078 DWORD dFlags = WS_CHILD | HDS_HORZ | HDS_FULLDRAG | HDS_DRAGDROP;
8080 TRACE("(lpcs=%p)\n", lpcs);
8082 infoPtr->dwStyle = lpcs->style;
8083 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
8084 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
8086 /* setup creation flags */
8087 dFlags |= (LVS_NOSORTHEADER & lpcs->style) ? 0 : HDS_BUTTONS;
8088 dFlags |= (LVS_NOCOLUMNHEADER & lpcs->style) ? HDS_HIDDEN : 0;
8090 /* create header */
8091 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL, dFlags,
8092 0, 0, 0, 0, hwnd, NULL,
8093 lpcs->hInstance, NULL);
8094 if (!infoPtr->hwndHeader) return -1;
8096 /* set header unicode format */
8097 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, TRUE, 0);
8099 /* set header font */
8100 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
8102 /* init item size to avoid division by 0 */
8103 LISTVIEW_UpdateItemSize (infoPtr);
8105 if (uView == LVS_REPORT)
8107 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
8109 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8111 LISTVIEW_UpdateSize(infoPtr);
8112 LISTVIEW_UpdateScroll(infoPtr);
8115 OpenThemeData(hwnd, themeClass);
8117 /* initialize the icon sizes */
8118 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
8119 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
8120 return 0;
8123 /***
8124 * DESCRIPTION:
8125 * Destroys the listview control.
8127 * PARAMETER(S):
8128 * [I] infoPtr : valid pointer to the listview structure
8130 * RETURN:
8131 * Success: 0
8132 * Failure: -1
8134 static LRESULT LISTVIEW_Destroy(const LISTVIEW_INFO *infoPtr)
8136 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8137 CloseThemeData(theme);
8138 return 0;
8141 /***
8142 * DESCRIPTION:
8143 * Enables the listview control.
8145 * PARAMETER(S):
8146 * [I] infoPtr : valid pointer to the listview structure
8147 * [I] bEnable : specifies whether to enable or disable the window
8149 * RETURN:
8150 * SUCCESS : TRUE
8151 * FAILURE : FALSE
8153 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr, BOOL bEnable)
8155 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
8156 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8157 return TRUE;
8160 /***
8161 * DESCRIPTION:
8162 * Erases the background of the listview control.
8164 * PARAMETER(S):
8165 * [I] infoPtr : valid pointer to the listview structure
8166 * [I] hdc : device context handle
8168 * RETURN:
8169 * SUCCESS : TRUE
8170 * FAILURE : FALSE
8172 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
8174 RECT rc;
8176 TRACE("(hdc=%p)\n", hdc);
8178 if (!GetClipBox(hdc, &rc)) return FALSE;
8180 /* for double buffered controls we need to do this during refresh */
8181 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
8183 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
8187 /***
8188 * DESCRIPTION:
8189 * Helper function for LISTVIEW_[HV]Scroll *only*.
8190 * Performs vertical/horizontal scrolling by a give amount.
8192 * PARAMETER(S):
8193 * [I] infoPtr : valid pointer to the listview structure
8194 * [I] dx : amount of horizontal scroll
8195 * [I] dy : amount of vertical scroll
8197 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
8199 /* now we can scroll the list */
8200 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
8201 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
8202 /* if we have focus, adjust rect */
8203 OffsetRect(&infoPtr->rcFocus, dx, dy);
8204 UpdateWindow(infoPtr->hwndSelf);
8207 /***
8208 * DESCRIPTION:
8209 * Performs vertical scrolling.
8211 * PARAMETER(S):
8212 * [I] infoPtr : valid pointer to the listview structure
8213 * [I] nScrollCode : scroll code
8214 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8215 * [I] hScrollWnd : scrollbar control window handle
8217 * RETURN:
8218 * Zero
8220 * NOTES:
8221 * SB_LINEUP/SB_LINEDOWN:
8222 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8223 * for LVS_REPORT is 1 line
8224 * for LVS_LIST cannot occur
8227 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8228 INT nScrollDiff, HWND hScrollWnd)
8230 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8231 INT nOldScrollPos, nNewScrollPos;
8232 SCROLLINFO scrollInfo;
8233 BOOL is_an_icon;
8235 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8236 debugscrollcode(nScrollCode), nScrollDiff);
8238 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8240 scrollInfo.cbSize = sizeof(SCROLLINFO);
8241 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8243 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
8245 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
8247 nOldScrollPos = scrollInfo.nPos;
8248 switch (nScrollCode)
8250 case SB_INTERNAL:
8251 break;
8253 case SB_LINEUP:
8254 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
8255 break;
8257 case SB_LINEDOWN:
8258 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
8259 break;
8261 case SB_PAGEUP:
8262 nScrollDiff = -scrollInfo.nPage;
8263 break;
8265 case SB_PAGEDOWN:
8266 nScrollDiff = scrollInfo.nPage;
8267 break;
8269 case SB_THUMBPOSITION:
8270 case SB_THUMBTRACK:
8271 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8272 break;
8274 default:
8275 nScrollDiff = 0;
8278 /* quit right away if pos isn't changing */
8279 if (nScrollDiff == 0) return 0;
8281 /* calculate new position, and handle overflows */
8282 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8283 if (nScrollDiff > 0) {
8284 if (nNewScrollPos < nOldScrollPos ||
8285 nNewScrollPos > scrollInfo.nMax)
8286 nNewScrollPos = scrollInfo.nMax;
8287 } else {
8288 if (nNewScrollPos > nOldScrollPos ||
8289 nNewScrollPos < scrollInfo.nMin)
8290 nNewScrollPos = scrollInfo.nMin;
8293 /* set the new position, and reread in case it changed */
8294 scrollInfo.fMask = SIF_POS;
8295 scrollInfo.nPos = nNewScrollPos;
8296 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
8298 /* carry on only if it really changed */
8299 if (nNewScrollPos == nOldScrollPos) return 0;
8301 /* now adjust to client coordinates */
8302 nScrollDiff = nOldScrollPos - nNewScrollPos;
8303 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
8305 /* and scroll the window */
8306 scroll_list(infoPtr, 0, nScrollDiff);
8308 return 0;
8311 /***
8312 * DESCRIPTION:
8313 * Performs horizontal scrolling.
8315 * PARAMETER(S):
8316 * [I] infoPtr : valid pointer to the listview structure
8317 * [I] nScrollCode : scroll code
8318 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8319 * [I] hScrollWnd : scrollbar control window handle
8321 * RETURN:
8322 * Zero
8324 * NOTES:
8325 * SB_LINELEFT/SB_LINERIGHT:
8326 * for LVS_ICON, LVS_SMALLICON 1 pixel
8327 * for LVS_REPORT is 1 pixel
8328 * for LVS_LIST is 1 column --> which is a 1 because the
8329 * scroll is based on columns not pixels
8332 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8333 INT nScrollDiff, HWND hScrollWnd)
8335 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8336 INT nOldScrollPos, nNewScrollPos;
8337 SCROLLINFO scrollInfo;
8339 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8340 debugscrollcode(nScrollCode), nScrollDiff);
8342 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8344 scrollInfo.cbSize = sizeof(SCROLLINFO);
8345 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8347 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
8349 nOldScrollPos = scrollInfo.nPos;
8351 switch (nScrollCode)
8353 case SB_INTERNAL:
8354 break;
8356 case SB_LINELEFT:
8357 nScrollDiff = -1;
8358 break;
8360 case SB_LINERIGHT:
8361 nScrollDiff = 1;
8362 break;
8364 case SB_PAGELEFT:
8365 nScrollDiff = -scrollInfo.nPage;
8366 break;
8368 case SB_PAGERIGHT:
8369 nScrollDiff = scrollInfo.nPage;
8370 break;
8372 case SB_THUMBPOSITION:
8373 case SB_THUMBTRACK:
8374 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8375 break;
8377 default:
8378 nScrollDiff = 0;
8381 /* quit right away if pos isn't changing */
8382 if (nScrollDiff == 0) return 0;
8384 /* calculate new position, and handle overflows */
8385 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8386 if (nScrollDiff > 0) {
8387 if (nNewScrollPos < nOldScrollPos ||
8388 nNewScrollPos > scrollInfo.nMax)
8389 nNewScrollPos = scrollInfo.nMax;
8390 } else {
8391 if (nNewScrollPos > nOldScrollPos ||
8392 nNewScrollPos < scrollInfo.nMin)
8393 nNewScrollPos = scrollInfo.nMin;
8396 /* set the new position, and reread in case it changed */
8397 scrollInfo.fMask = SIF_POS;
8398 scrollInfo.nPos = nNewScrollPos;
8399 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
8401 /* carry on only if it really changed */
8402 if (nNewScrollPos == nOldScrollPos) return 0;
8404 if(uView == LVS_REPORT)
8405 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
8407 /* now adjust to client coordinates */
8408 nScrollDiff = nOldScrollPos - nNewScrollPos;
8409 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
8411 /* and scroll the window */
8412 scroll_list(infoPtr, nScrollDiff, 0);
8414 return 0;
8417 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
8419 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8420 INT gcWheelDelta = 0;
8421 INT pulScrollLines = 3;
8422 SCROLLINFO scrollInfo;
8424 TRACE("(wheelDelta=%d)\n", wheelDelta);
8426 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8427 gcWheelDelta -= wheelDelta;
8429 scrollInfo.cbSize = sizeof(SCROLLINFO);
8430 scrollInfo.fMask = SIF_POS;
8432 switch(uView)
8434 case LVS_ICON:
8435 case LVS_SMALLICON:
8437 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8438 * should be fixed in the future.
8440 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
8441 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8442 break;
8444 case LVS_REPORT:
8445 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8447 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
8448 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8449 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
8451 break;
8453 case LVS_LIST:
8454 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8455 break;
8457 return 0;
8460 /***
8461 * DESCRIPTION:
8462 * ???
8464 * PARAMETER(S):
8465 * [I] infoPtr : valid pointer to the listview structure
8466 * [I] nVirtualKey : virtual key
8467 * [I] lKeyData : key data
8469 * RETURN:
8470 * Zero
8472 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8474 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8475 HWND hwndSelf = infoPtr->hwndSelf;
8476 INT nItem = -1;
8477 NMLVKEYDOWN nmKeyDown;
8479 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
8481 /* send LVN_KEYDOWN notification */
8482 nmKeyDown.wVKey = nVirtualKey;
8483 nmKeyDown.flags = 0;
8484 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8485 if (!IsWindow(hwndSelf))
8486 return 0;
8488 switch (nVirtualKey)
8490 case VK_SPACE:
8491 nItem = infoPtr->nFocusedItem;
8492 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
8493 toggle_checkbox_state(infoPtr, infoPtr->nFocusedItem);
8494 break;
8496 case VK_RETURN:
8497 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8499 if (!notify(infoPtr, NM_RETURN)) return 0;
8500 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8502 break;
8504 case VK_HOME:
8505 if (infoPtr->nItemCount > 0)
8506 nItem = 0;
8507 break;
8509 case VK_END:
8510 if (infoPtr->nItemCount > 0)
8511 nItem = infoPtr->nItemCount - 1;
8512 break;
8514 case VK_LEFT:
8515 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8516 break;
8518 case VK_UP:
8519 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8520 break;
8522 case VK_RIGHT:
8523 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8524 break;
8526 case VK_DOWN:
8527 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8528 break;
8530 case VK_PRIOR:
8531 if (uView == LVS_REPORT)
8533 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8534 if (infoPtr->nFocusedItem == topidx)
8535 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8536 else
8537 nItem = topidx;
8539 else
8540 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8541 * LISTVIEW_GetCountPerRow(infoPtr);
8542 if(nItem < 0) nItem = 0;
8543 break;
8545 case VK_NEXT:
8546 if (uView == LVS_REPORT)
8548 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8549 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8550 if (infoPtr->nFocusedItem == topidx + cnt - 1)
8551 nItem = infoPtr->nFocusedItem + cnt - 1;
8552 else
8553 nItem = topidx + cnt - 1;
8555 else
8556 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8557 * LISTVIEW_GetCountPerRow(infoPtr);
8558 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8559 break;
8562 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8563 LISTVIEW_KeySelection(infoPtr, nItem);
8565 return 0;
8568 /***
8569 * DESCRIPTION:
8570 * Kills the focus.
8572 * PARAMETER(S):
8573 * [I] infoPtr : valid pointer to the listview structure
8575 * RETURN:
8576 * Zero
8578 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8580 TRACE("()\n");
8582 /* if we did not have the focus, there's nothing to do */
8583 if (!infoPtr->bFocus) return 0;
8585 /* send NM_KILLFOCUS notification */
8586 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8588 /* if we have a focus rectagle, get rid of it */
8589 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8591 /* set window focus flag */
8592 infoPtr->bFocus = FALSE;
8594 /* invalidate the selected items before resetting focus flag */
8595 LISTVIEW_InvalidateSelectedItems(infoPtr);
8597 return 0;
8600 /***
8601 * DESCRIPTION:
8602 * Processes double click messages (left mouse button).
8604 * PARAMETER(S):
8605 * [I] infoPtr : valid pointer to the listview structure
8606 * [I] wKey : key flag
8607 * [I] x,y : mouse coordinate
8609 * RETURN:
8610 * Zero
8612 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8614 LVHITTESTINFO htInfo;
8616 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8618 /* Cancel the item edition if any */
8619 if (infoPtr->itemEdit.fEnabled)
8621 KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
8622 infoPtr->itemEdit.fEnabled = FALSE;
8625 /* send NM_RELEASEDCAPTURE notification */
8626 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8628 htInfo.pt.x = x;
8629 htInfo.pt.y = y;
8631 /* send NM_DBLCLK notification */
8632 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8633 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8635 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8636 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8638 return 0;
8641 /***
8642 * DESCRIPTION:
8643 * Processes mouse down messages (left mouse button).
8645 * PARAMETERS:
8646 * infoPtr [I ] valid pointer to the listview structure
8647 * wKey [I ] key flag
8648 * x,y [I ] mouse coordinate
8650 * RETURN:
8651 * Zero
8653 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8655 LVHITTESTINFO lvHitTestInfo;
8656 static BOOL bGroupSelect = TRUE;
8657 POINT pt = { x, y };
8658 INT nItem;
8660 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8662 /* send NM_RELEASEDCAPTURE notification */
8663 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8665 /* set left button down flag and record the click position */
8666 infoPtr->bLButtonDown = TRUE;
8667 infoPtr->ptClickPos = pt;
8668 infoPtr->bDragging = FALSE;
8670 lvHitTestInfo.pt.x = x;
8671 lvHitTestInfo.pt.y = y;
8673 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8674 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
8675 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8677 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8679 toggle_checkbox_state(infoPtr, nItem);
8680 return 0;
8683 if (infoPtr->dwStyle & LVS_SINGLESEL)
8685 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8686 infoPtr->nEditLabelItem = nItem;
8687 else
8688 LISTVIEW_SetSelection(infoPtr, nItem);
8690 else
8692 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8694 if (bGroupSelect)
8696 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8697 LISTVIEW_SetItemFocus(infoPtr, nItem);
8698 infoPtr->nSelectionMark = nItem;
8700 else
8702 LVITEMW item;
8704 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8705 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8707 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8708 infoPtr->nSelectionMark = nItem;
8711 else if (wKey & MK_CONTROL)
8713 LVITEMW item;
8715 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8717 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8718 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8719 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8720 infoPtr->nSelectionMark = nItem;
8722 else if (wKey & MK_SHIFT)
8724 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8726 else
8728 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8729 infoPtr->nEditLabelItem = nItem;
8731 /* set selection (clears other pre-existing selections) */
8732 LISTVIEW_SetSelection(infoPtr, nItem);
8736 if (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE)
8737 if(lvHitTestInfo.iItem != -1) notify_itemactivate(infoPtr,&lvHitTestInfo);
8739 else
8741 /* remove all selections */
8742 LISTVIEW_DeselectAll(infoPtr);
8743 ReleaseCapture();
8746 return 0;
8749 /***
8750 * DESCRIPTION:
8751 * Processes mouse up messages (left mouse button).
8753 * PARAMETERS:
8754 * infoPtr [I ] valid pointer to the listview structure
8755 * wKey [I ] key flag
8756 * x,y [I ] mouse coordinate
8758 * RETURN:
8759 * Zero
8761 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8763 LVHITTESTINFO lvHitTestInfo;
8765 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8767 if (!infoPtr->bLButtonDown) return 0;
8769 lvHitTestInfo.pt.x = x;
8770 lvHitTestInfo.pt.y = y;
8772 /* send NM_CLICK notification */
8773 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8774 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8776 /* set left button flag */
8777 infoPtr->bLButtonDown = FALSE;
8779 if (infoPtr->bDragging)
8781 infoPtr->bDragging = FALSE;
8782 return 0;
8785 /* if we clicked on a selected item, edit the label */
8786 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8788 /* we want to make sure the user doesn't want to do a double click. So we will
8789 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
8791 infoPtr->itemEdit.fEnabled = TRUE;
8792 infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
8793 SetTimer(infoPtr->hwndSelf,
8794 (UINT_PTR)&infoPtr->itemEdit,
8795 GetDoubleClickTime(),
8796 LISTVIEW_DelayedEditItem);
8799 if (!infoPtr->bFocus)
8800 SetFocus(infoPtr->hwndSelf);
8802 return 0;
8805 /***
8806 * DESCRIPTION:
8807 * Destroys the listview control (called after WM_DESTROY).
8809 * PARAMETER(S):
8810 * [I] infoPtr : valid pointer to the listview structure
8812 * RETURN:
8813 * Zero
8815 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8817 TRACE("()\n");
8819 /* delete all items */
8820 LISTVIEW_DeleteAllItems(infoPtr, TRUE);
8822 /* destroy data structure */
8823 DPA_Destroy(infoPtr->hdpaItems);
8824 DPA_Destroy(infoPtr->hdpaPosX);
8825 DPA_Destroy(infoPtr->hdpaPosY);
8826 DPA_Destroy(infoPtr->hdpaColumns);
8827 ranges_destroy(infoPtr->selectionRanges);
8829 /* destroy image lists */
8830 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8832 if (infoPtr->himlNormal)
8833 ImageList_Destroy(infoPtr->himlNormal);
8834 if (infoPtr->himlSmall)
8835 ImageList_Destroy(infoPtr->himlSmall);
8836 if (infoPtr->himlState)
8837 ImageList_Destroy(infoPtr->himlState);
8840 /* destroy font, bkgnd brush */
8841 infoPtr->hFont = 0;
8842 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8843 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8845 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8847 /* free listview info pointer*/
8848 Free(infoPtr);
8850 return 0;
8853 /***
8854 * DESCRIPTION:
8855 * Handles notifications from header.
8857 * PARAMETER(S):
8858 * [I] infoPtr : valid pointer to the listview structure
8859 * [I] nCtrlId : control identifier
8860 * [I] lpnmh : notification information
8862 * RETURN:
8863 * Zero
8865 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8867 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8868 HWND hwndSelf = infoPtr->hwndSelf;
8870 TRACE("(lpnmh=%p)\n", lpnmh);
8872 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8874 switch (lpnmh->hdr.code)
8876 case HDN_TRACKW:
8877 case HDN_TRACKA:
8879 COLUMN_INFO *lpColumnInfo;
8880 POINT ptOrigin;
8881 INT x;
8883 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8884 break;
8886 /* remove the old line (if any) */
8887 LISTVIEW_DrawTrackLine(infoPtr);
8889 /* compute & draw the new line */
8890 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8891 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
8892 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8893 infoPtr->xTrackLine = x + ptOrigin.x;
8894 LISTVIEW_DrawTrackLine(infoPtr);
8895 break;
8898 case HDN_ENDTRACKA:
8899 case HDN_ENDTRACKW:
8900 /* remove the track line (if any) */
8901 LISTVIEW_DrawTrackLine(infoPtr);
8902 infoPtr->xTrackLine = -1;
8903 break;
8905 case HDN_ENDDRAG:
8906 FIXME("Changing column order not implemented\n");
8907 return TRUE;
8909 case HDN_ITEMCHANGINGW:
8910 case HDN_ITEMCHANGINGA:
8911 return notify_forward_header(infoPtr, lpnmh);
8913 case HDN_ITEMCHANGEDW:
8914 case HDN_ITEMCHANGEDA:
8916 COLUMN_INFO *lpColumnInfo;
8917 INT dx, cxy;
8919 notify_forward_header(infoPtr, lpnmh);
8920 if (!IsWindow(hwndSelf))
8921 break;
8923 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8925 HDITEMW hdi;
8927 hdi.mask = HDI_WIDTH;
8928 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, &hdi)) return 0;
8929 cxy = hdi.cxy;
8931 else
8932 cxy = lpnmh->pitem->cxy;
8934 /* determine how much we change since the last know position */
8935 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8936 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8937 if (dx != 0)
8939 lpColumnInfo->rcHeader.right += dx;
8940 if (lpnmh->iItem + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
8941 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8942 else
8944 /* only needs to update the scrolls */
8945 infoPtr->nItemWidth += dx;
8946 LISTVIEW_UpdateScroll(infoPtr);
8948 LISTVIEW_UpdateItemSize(infoPtr);
8949 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8951 POINT ptOrigin;
8952 RECT rcCol = lpColumnInfo->rcHeader;
8954 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8955 OffsetRect(&rcCol, ptOrigin.x, 0);
8957 rcCol.top = infoPtr->rcList.top;
8958 rcCol.bottom = infoPtr->rcList.bottom;
8960 /* resizing left-aligned columns leaves most of the left side untouched */
8961 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8963 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
8964 if (dx > 0)
8965 nMaxDirty += dx;
8966 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
8969 /* when shrinking the last column clear the now unused field */
8970 if (lpnmh->iItem == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1 && dx < 0)
8971 rcCol.right -= dx;
8973 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8977 break;
8979 case HDN_ITEMCLICKW:
8980 case HDN_ITEMCLICKA:
8982 /* Handle sorting by Header Column */
8983 NMLISTVIEW nmlv;
8985 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8986 nmlv.iItem = -1;
8987 nmlv.iSubItem = lpnmh->iItem;
8988 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8990 break;
8992 case HDN_DIVIDERDBLCLICKW:
8993 case HDN_DIVIDERDBLCLICKA:
8994 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
8995 break;
8998 return 0;
9001 /***
9002 * DESCRIPTION:
9003 * Paint non-client area of control.
9005 * PARAMETER(S):
9006 * [I] infoPtr : valid pointer to the listview structureof the sender
9007 * [I] region : update region
9009 * RETURN:
9010 * TRUE - frame was painted
9011 * FALSE - call default window proc
9013 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
9015 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
9016 HDC dc;
9017 RECT r;
9018 HRGN cliprgn;
9019 int cxEdge = GetSystemMetrics (SM_CXEDGE),
9020 cyEdge = GetSystemMetrics (SM_CYEDGE);
9022 if (!theme) return FALSE;
9024 GetWindowRect(infoPtr->hwndSelf, &r);
9026 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
9027 r.right - cxEdge, r.bottom - cyEdge);
9028 if (region != (HRGN)1)
9029 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
9030 OffsetRect(&r, -r.left, -r.top);
9032 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
9033 OffsetRect(&r, -r.left, -r.top);
9035 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
9036 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
9037 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
9038 ReleaseDC(infoPtr->hwndSelf, dc);
9040 /* Call default proc to get the scrollbars etc. painted */
9041 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
9043 return TRUE;
9046 /***
9047 * DESCRIPTION:
9048 * Determines the type of structure to use.
9050 * PARAMETER(S):
9051 * [I] infoPtr : valid pointer to the listview structureof the sender
9052 * [I] hwndFrom : listview window handle
9053 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
9055 * RETURN:
9056 * Zero
9058 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
9060 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
9062 if (nCommand == NF_REQUERY)
9063 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
9065 return infoPtr->notifyFormat;
9068 /***
9069 * DESCRIPTION:
9070 * Paints/Repaints the listview control.
9072 * PARAMETER(S):
9073 * [I] infoPtr : valid pointer to the listview structure
9074 * [I] hdc : device context handle
9076 * RETURN:
9077 * Zero
9079 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
9081 TRACE("(hdc=%p)\n", hdc);
9083 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
9085 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9087 infoPtr->bNoItemMetrics = FALSE;
9088 LISTVIEW_UpdateItemSize(infoPtr);
9089 if (uView == LVS_ICON || uView == LVS_SMALLICON)
9090 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9091 LISTVIEW_UpdateScroll(infoPtr);
9094 UpdateWindow(infoPtr->hwndHeader);
9096 if (hdc)
9097 LISTVIEW_Refresh(infoPtr, hdc, NULL);
9098 else
9100 PAINTSTRUCT ps;
9102 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
9103 if (!hdc) return 1;
9104 LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
9105 EndPaint(infoPtr->hwndSelf, &ps);
9108 return 0;
9112 /***
9113 * DESCRIPTION:
9114 * Paints/Repaints the listview control.
9116 * PARAMETER(S):
9117 * [I] infoPtr : valid pointer to the listview structure
9118 * [I] hdc : device context handle
9119 * [I] options : drawing options
9121 * RETURN:
9122 * Zero
9124 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
9126 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
9128 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
9129 return 0;
9131 if (options & PRF_ERASEBKGND)
9132 LISTVIEW_EraseBkgnd(infoPtr, hdc);
9134 if (options & PRF_CLIENT)
9135 LISTVIEW_Paint(infoPtr, hdc);
9137 return 0;
9141 /***
9142 * DESCRIPTION:
9143 * Processes double click messages (right mouse button).
9145 * PARAMETER(S):
9146 * [I] infoPtr : valid pointer to the listview structure
9147 * [I] wKey : key flag
9148 * [I] x,y : mouse coordinate
9150 * RETURN:
9151 * Zero
9153 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9155 LVHITTESTINFO lvHitTestInfo;
9157 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9159 /* send NM_RELEASEDCAPTURE notification */
9160 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9162 /* send NM_RDBLCLK notification */
9163 lvHitTestInfo.pt.x = x;
9164 lvHitTestInfo.pt.y = y;
9165 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9166 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
9168 return 0;
9171 /***
9172 * DESCRIPTION:
9173 * Processes mouse down messages (right mouse button).
9175 * PARAMETER(S):
9176 * [I] infoPtr : valid pointer to the listview structure
9177 * [I] wKey : key flag
9178 * [I] x,y : mouse coordinate
9180 * RETURN:
9181 * Zero
9183 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9185 LVHITTESTINFO lvHitTestInfo;
9186 INT nItem;
9188 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9190 /* send NM_RELEASEDCAPTURE notification */
9191 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9193 /* make sure the listview control window has the focus */
9194 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
9196 /* set right button down flag */
9197 infoPtr->bRButtonDown = TRUE;
9199 /* determine the index of the selected item */
9200 lvHitTestInfo.pt.x = x;
9201 lvHitTestInfo.pt.y = y;
9202 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
9204 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9206 LISTVIEW_SetItemFocus(infoPtr, nItem);
9207 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9208 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9209 LISTVIEW_SetSelection(infoPtr, nItem);
9211 else
9213 LISTVIEW_DeselectAll(infoPtr);
9216 return 0;
9219 /***
9220 * DESCRIPTION:
9221 * Processes mouse up messages (right mouse button).
9223 * PARAMETER(S):
9224 * [I] infoPtr : valid pointer to the listview structure
9225 * [I] wKey : key flag
9226 * [I] x,y : mouse coordinate
9228 * RETURN:
9229 * Zero
9231 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9233 LVHITTESTINFO lvHitTestInfo;
9234 POINT pt;
9236 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9238 if (!infoPtr->bRButtonDown) return 0;
9240 /* set button flag */
9241 infoPtr->bRButtonDown = FALSE;
9243 /* Send NM_RClICK notification */
9244 lvHitTestInfo.pt.x = x;
9245 lvHitTestInfo.pt.y = y;
9246 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9247 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
9249 /* Change to screen coordinate for WM_CONTEXTMENU */
9250 pt = lvHitTestInfo.pt;
9251 ClientToScreen(infoPtr->hwndSelf, &pt);
9253 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9254 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
9255 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
9257 return 0;
9261 /***
9262 * DESCRIPTION:
9263 * Sets the cursor.
9265 * PARAMETER(S):
9266 * [I] infoPtr : valid pointer to the listview structure
9267 * [I] hwnd : window handle of window containing the cursor
9268 * [I] nHittest : hit-test code
9269 * [I] wMouseMsg : ideintifier of the mouse message
9271 * RETURN:
9272 * TRUE if cursor is set
9273 * FALSE otherwise
9275 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
9277 LVHITTESTINFO lvHitTestInfo;
9279 if(!(LISTVIEW_isHotTracking(infoPtr))) return FALSE;
9281 if(!infoPtr->hHotCursor) return FALSE;
9283 GetCursorPos(&lvHitTestInfo.pt);
9284 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
9286 SetCursor(infoPtr->hHotCursor);
9288 return TRUE;
9291 /***
9292 * DESCRIPTION:
9293 * Sets the focus.
9295 * PARAMETER(S):
9296 * [I] infoPtr : valid pointer to the listview structure
9297 * [I] hwndLoseFocus : handle of previously focused window
9299 * RETURN:
9300 * Zero
9302 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
9304 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
9306 /* if we have the focus already, there's nothing to do */
9307 if (infoPtr->bFocus) return 0;
9309 /* send NM_SETFOCUS notification */
9310 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
9312 /* set window focus flag */
9313 infoPtr->bFocus = TRUE;
9315 /* put the focus rect back on */
9316 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
9318 /* redraw all visible selected items */
9319 LISTVIEW_InvalidateSelectedItems(infoPtr);
9321 return 0;
9324 /***
9325 * DESCRIPTION:
9326 * Sets the font.
9328 * PARAMETER(S):
9329 * [I] infoPtr : valid pointer to the listview structure
9330 * [I] fRedraw : font handle
9331 * [I] fRedraw : redraw flag
9333 * RETURN:
9334 * Zero
9336 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
9338 HFONT oldFont = infoPtr->hFont;
9340 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
9342 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9343 if (infoPtr->hFont == oldFont) return 0;
9345 LISTVIEW_SaveTextMetrics(infoPtr);
9347 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
9349 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
9350 LISTVIEW_UpdateSize(infoPtr);
9351 LISTVIEW_UpdateScroll(infoPtr);
9354 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
9356 return 0;
9359 /***
9360 * DESCRIPTION:
9361 * Message handling for WM_SETREDRAW.
9362 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9364 * PARAMETER(S):
9365 * [I] infoPtr : valid pointer to the listview structure
9366 * [I] bRedraw: state of redraw flag
9368 * RETURN:
9369 * DefWinProc return value
9371 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
9373 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
9375 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9376 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
9378 infoPtr->bRedraw = bRedraw;
9380 if(!bRedraw) return 0;
9382 if (is_autoarrange(infoPtr))
9383 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9384 LISTVIEW_UpdateScroll(infoPtr);
9386 /* despite what the WM_SETREDRAW docs says, apps expect us
9387 * to invalidate the listview here... stupid! */
9388 LISTVIEW_InvalidateList(infoPtr);
9390 return 0;
9393 /***
9394 * DESCRIPTION:
9395 * Resizes the listview control. This function processes WM_SIZE
9396 * messages. At this time, the width and height are not used.
9398 * PARAMETER(S):
9399 * [I] infoPtr : valid pointer to the listview structure
9400 * [I] Width : new width
9401 * [I] Height : new height
9403 * RETURN:
9404 * Zero
9406 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
9408 RECT rcOld = infoPtr->rcList;
9410 TRACE("(width=%d, height=%d)\n", Width, Height);
9412 LISTVIEW_UpdateSize(infoPtr);
9413 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
9415 /* do not bother with display related stuff if we're not redrawing */
9416 if (!is_redrawing(infoPtr)) return 0;
9418 if (is_autoarrange(infoPtr))
9419 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9421 LISTVIEW_UpdateScroll(infoPtr);
9423 /* refresh all only for lists whose height changed significantly */
9424 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
9425 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
9426 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
9427 LISTVIEW_InvalidateList(infoPtr);
9429 return 0;
9432 /***
9433 * DESCRIPTION:
9434 * Sets the size information.
9436 * PARAMETER(S):
9437 * [I] infoPtr : valid pointer to the listview structure
9439 * RETURN:
9440 * None
9442 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
9444 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9446 TRACE("uView=%d, rcList(old)=%s\n", uView, wine_dbgstr_rect(&infoPtr->rcList));
9448 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
9450 if (uView == LVS_LIST)
9452 /* Apparently the "LIST" style is supposed to have the same
9453 * number of items in a column even if there is no scroll bar.
9454 * Since if a scroll bar already exists then the bottom is already
9455 * reduced, only reduce if the scroll bar does not currently exist.
9456 * The "2" is there to mimic the native control. I think it may be
9457 * related to either padding or edges. (GLA 7/2002)
9459 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
9460 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
9461 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
9463 else if (uView == LVS_REPORT)
9465 HDLAYOUT hl;
9466 WINDOWPOS wp;
9468 hl.prc = &infoPtr->rcList;
9469 hl.pwpos = &wp;
9470 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9471 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp.flags, wp.x, wp.y, wp.cx, wp.cy);
9472 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy,
9473 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9474 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9475 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy);
9477 infoPtr->rcList.top = max(wp.cy, 0);
9478 infoPtr->rcList.top += (infoPtr->dwLvExStyle & LVS_EX_GRIDLINES) ? 2 : 0;
9481 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
9484 /***
9485 * DESCRIPTION:
9486 * Processes WM_STYLECHANGED messages.
9488 * PARAMETER(S):
9489 * [I] infoPtr : valid pointer to the listview structure
9490 * [I] wStyleType : window style type (normal or extended)
9491 * [I] lpss : window style information
9493 * RETURN:
9494 * Zero
9496 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9497 const STYLESTRUCT *lpss)
9499 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9500 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9501 UINT style;
9503 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9504 wStyleType, lpss->styleOld, lpss->styleNew);
9506 if (wStyleType != GWL_STYLE) return 0;
9508 /* FIXME: if LVS_NOSORTHEADER changed, update header */
9509 /* what if LVS_OWNERDATA changed? */
9510 /* or LVS_SINGLESEL */
9511 /* or LVS_SORT{AS,DES}CENDING */
9513 infoPtr->dwStyle = lpss->styleNew;
9515 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9516 ((lpss->styleNew & WS_HSCROLL) == 0))
9517 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9519 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9520 ((lpss->styleNew & WS_VSCROLL) == 0))
9521 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9523 if (uNewView != uOldView)
9525 SIZE oldIconSize = infoPtr->iconSize;
9526 HIMAGELIST himl;
9528 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9529 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9531 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9532 SetRectEmpty(&infoPtr->rcFocus);
9534 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9535 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
9537 if (uNewView == LVS_ICON)
9539 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
9541 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9542 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9543 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
9546 else if (uNewView == LVS_REPORT)
9548 HDLAYOUT hl;
9549 WINDOWPOS wp;
9551 hl.prc = &infoPtr->rcList;
9552 hl.pwpos = &wp;
9553 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9554 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
9555 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9556 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9559 LISTVIEW_UpdateItemSize(infoPtr);
9562 if (uNewView == LVS_REPORT)
9564 if ((lpss->styleOld ^ lpss->styleNew) & LVS_NOCOLUMNHEADER)
9566 if (lpss->styleNew & LVS_NOCOLUMNHEADER)
9568 /* Turn off the header control */
9569 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
9570 TRACE("Hide header control, was 0x%08x\n", style);
9571 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style | HDS_HIDDEN);
9572 } else {
9573 /* Turn on the header control */
9574 if ((style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE)) & HDS_HIDDEN)
9576 TRACE("Show header control, was 0x%08x\n", style);
9577 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, (style & ~HDS_HIDDEN) | WS_VISIBLE);
9583 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9584 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9585 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9587 /* update the size of the client area */
9588 LISTVIEW_UpdateSize(infoPtr);
9590 /* add scrollbars if needed */
9591 LISTVIEW_UpdateScroll(infoPtr);
9593 /* invalidate client area + erase background */
9594 LISTVIEW_InvalidateList(infoPtr);
9596 return 0;
9599 /***
9600 * DESCRIPTION:
9601 * Window procedure of the listview control.
9604 static LRESULT WINAPI
9605 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9607 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9609 TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg, wParam, lParam);
9611 if (!infoPtr && (uMsg != WM_NCCREATE))
9612 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9614 switch (uMsg)
9616 case LVM_APPROXIMATEVIEWRECT:
9617 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9618 LOWORD(lParam), HIWORD(lParam));
9619 case LVM_ARRANGE:
9620 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9622 /* case LVM_CANCELEDITLABEL: */
9624 case LVM_CREATEDRAGIMAGE:
9625 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9627 case LVM_DELETEALLITEMS:
9628 return LISTVIEW_DeleteAllItems(infoPtr, FALSE);
9630 case LVM_DELETECOLUMN:
9631 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9633 case LVM_DELETEITEM:
9634 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9636 case LVM_EDITLABELW:
9637 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9639 case LVM_EDITLABELA:
9640 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9642 /* case LVM_ENABLEGROUPVIEW: */
9644 case LVM_ENSUREVISIBLE:
9645 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9647 case LVM_FINDITEMW:
9648 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9650 case LVM_FINDITEMA:
9651 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9653 case LVM_GETBKCOLOR:
9654 return infoPtr->clrBk;
9656 /* case LVM_GETBKIMAGE: */
9658 case LVM_GETCALLBACKMASK:
9659 return infoPtr->uCallbackMask;
9661 case LVM_GETCOLUMNA:
9662 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9664 case LVM_GETCOLUMNW:
9665 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9667 case LVM_GETCOLUMNORDERARRAY:
9668 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9670 case LVM_GETCOLUMNWIDTH:
9671 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9673 case LVM_GETCOUNTPERPAGE:
9674 return LISTVIEW_GetCountPerPage(infoPtr);
9676 case LVM_GETEDITCONTROL:
9677 return (LRESULT)infoPtr->hwndEdit;
9679 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9680 return infoPtr->dwLvExStyle;
9682 /* case LVM_GETGROUPINFO: */
9684 /* case LVM_GETGROUPMETRICS: */
9686 case LVM_GETHEADER:
9687 return (LRESULT)infoPtr->hwndHeader;
9689 case LVM_GETHOTCURSOR:
9690 return (LRESULT)infoPtr->hHotCursor;
9692 case LVM_GETHOTITEM:
9693 return infoPtr->nHotItem;
9695 case LVM_GETHOVERTIME:
9696 return infoPtr->dwHoverTime;
9698 case LVM_GETIMAGELIST:
9699 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9701 /* case LVM_GETINSERTMARK: */
9703 /* case LVM_GETINSERTMARKCOLOR: */
9705 /* case LVM_GETINSERTMARKRECT: */
9707 case LVM_GETISEARCHSTRINGA:
9708 case LVM_GETISEARCHSTRINGW:
9709 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9710 return FALSE;
9712 case LVM_GETITEMA:
9713 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9715 case LVM_GETITEMW:
9716 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9718 case LVM_GETITEMCOUNT:
9719 return infoPtr->nItemCount;
9721 case LVM_GETITEMPOSITION:
9722 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9724 case LVM_GETITEMRECT:
9725 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9727 case LVM_GETITEMSPACING:
9728 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9730 case LVM_GETITEMSTATE:
9731 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9733 case LVM_GETITEMTEXTA:
9734 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9736 case LVM_GETITEMTEXTW:
9737 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9739 case LVM_GETNEXTITEM:
9740 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9742 case LVM_GETNUMBEROFWORKAREAS:
9743 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9744 return 1;
9746 case LVM_GETORIGIN:
9747 if (!lParam) return FALSE;
9748 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT ||
9749 (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST) return FALSE;
9750 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9751 return TRUE;
9753 /* case LVM_GETOUTLINECOLOR: */
9755 /* case LVM_GETSELECTEDCOLUMN: */
9757 case LVM_GETSELECTEDCOUNT:
9758 return LISTVIEW_GetSelectedCount(infoPtr);
9760 case LVM_GETSELECTIONMARK:
9761 return infoPtr->nSelectionMark;
9763 case LVM_GETSTRINGWIDTHA:
9764 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9766 case LVM_GETSTRINGWIDTHW:
9767 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9769 case LVM_GETSUBITEMRECT:
9770 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
9772 case LVM_GETTEXTBKCOLOR:
9773 return infoPtr->clrTextBk;
9775 case LVM_GETTEXTCOLOR:
9776 return infoPtr->clrText;
9778 /* case LVM_GETTILEINFO: */
9780 /* case LVM_GETTILEVIEWINFO: */
9782 case LVM_GETTOOLTIPS:
9783 if( !infoPtr->hwndToolTip )
9784 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9785 return (LRESULT)infoPtr->hwndToolTip;
9787 case LVM_GETTOPINDEX:
9788 return LISTVIEW_GetTopIndex(infoPtr);
9790 case LVM_GETUNICODEFORMAT:
9791 return (infoPtr->notifyFormat == NFR_UNICODE);
9793 /* case LVM_GETVIEW: */
9795 case LVM_GETVIEWRECT:
9796 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9798 case LVM_GETWORKAREAS:
9799 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9800 return FALSE;
9802 /* case LVM_HASGROUP: */
9804 case LVM_HITTEST:
9805 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9807 case LVM_INSERTCOLUMNA:
9808 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9810 case LVM_INSERTCOLUMNW:
9811 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9813 /* case LVM_INSERTGROUP: */
9815 /* case LVM_INSERTGROUPSORTED: */
9817 case LVM_INSERTITEMA:
9818 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9820 case LVM_INSERTITEMW:
9821 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9823 /* case LVM_INSERTMARKHITTEST: */
9825 /* case LVM_ISGROUPVIEWENABLED: */
9827 /* case LVM_MAPIDTOINDEX: */
9829 /* case LVM_MAPINDEXTOID: */
9831 /* case LVM_MOVEGROUP: */
9833 /* case LVM_MOVEITEMTOGROUP: */
9835 case LVM_REDRAWITEMS:
9836 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9838 /* case LVM_REMOVEALLGROUPS: */
9840 /* case LVM_REMOVEGROUP: */
9842 case LVM_SCROLL:
9843 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9845 case LVM_SETBKCOLOR:
9846 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9848 /* case LVM_SETBKIMAGE: */
9850 case LVM_SETCALLBACKMASK:
9851 infoPtr->uCallbackMask = (UINT)wParam;
9852 return TRUE;
9854 case LVM_SETCOLUMNA:
9855 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9857 case LVM_SETCOLUMNW:
9858 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9860 case LVM_SETCOLUMNORDERARRAY:
9861 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9863 case LVM_SETCOLUMNWIDTH:
9864 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9866 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9867 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9869 /* case LVM_SETGROUPINFO: */
9871 /* case LVM_SETGROUPMETRICS: */
9873 case LVM_SETHOTCURSOR:
9874 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9876 case LVM_SETHOTITEM:
9877 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9879 case LVM_SETHOVERTIME:
9880 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9882 case LVM_SETICONSPACING:
9883 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9885 case LVM_SETIMAGELIST:
9886 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9888 /* case LVM_SETINFOTIP: */
9890 /* case LVM_SETINSERTMARK: */
9892 /* case LVM_SETINSERTMARKCOLOR: */
9894 case LVM_SETITEMA:
9895 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9897 case LVM_SETITEMW:
9898 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9900 case LVM_SETITEMCOUNT:
9901 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9903 case LVM_SETITEMPOSITION:
9905 POINT pt;
9906 pt.x = (short)LOWORD(lParam);
9907 pt.y = (short)HIWORD(lParam);
9908 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9911 case LVM_SETITEMPOSITION32:
9912 if (lParam == 0) return FALSE;
9913 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9915 case LVM_SETITEMSTATE:
9916 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9918 case LVM_SETITEMTEXTA:
9919 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9921 case LVM_SETITEMTEXTW:
9922 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9924 /* case LVM_SETOUTLINECOLOR: */
9926 /* case LVM_SETSELECTEDCOLUMN: */
9928 case LVM_SETSELECTIONMARK:
9929 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9931 case LVM_SETTEXTBKCOLOR:
9932 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9934 case LVM_SETTEXTCOLOR:
9935 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9937 /* case LVM_SETTILEINFO: */
9939 /* case LVM_SETTILEVIEWINFO: */
9941 /* case LVM_SETTILEWIDTH: */
9943 case LVM_SETTOOLTIPS:
9944 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9946 case LVM_SETUNICODEFORMAT:
9947 return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
9949 /* case LVM_SETVIEW: */
9951 /* case LVM_SETWORKAREAS: */
9953 /* case LVM_SORTGROUPS: */
9955 case LVM_SORTITEMS:
9956 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9958 /* LVM_SORTITEMSEX: */
9960 case LVM_SUBITEMHITTEST:
9961 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9963 case LVM_UPDATE:
9964 return LISTVIEW_Update(infoPtr, (INT)wParam);
9966 case WM_CHAR:
9967 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9969 case WM_COMMAND:
9970 return LISTVIEW_Command(infoPtr, wParam, lParam);
9972 case WM_NCCREATE:
9973 return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
9975 case WM_CREATE:
9976 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9978 case WM_DESTROY:
9979 return LISTVIEW_Destroy(infoPtr);
9981 case WM_ENABLE:
9982 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
9984 case WM_ERASEBKGND:
9985 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9987 case WM_GETDLGCODE:
9988 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9990 case WM_GETFONT:
9991 return (LRESULT)infoPtr->hFont;
9993 case WM_HSCROLL:
9994 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9996 case WM_KEYDOWN:
9997 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9999 case WM_KILLFOCUS:
10000 return LISTVIEW_KillFocus(infoPtr);
10002 case WM_LBUTTONDBLCLK:
10003 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10005 case WM_LBUTTONDOWN:
10006 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10008 case WM_LBUTTONUP:
10009 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10011 case WM_MOUSEMOVE:
10012 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10014 case WM_MOUSEHOVER:
10015 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10017 case WM_NCDESTROY:
10018 return LISTVIEW_NCDestroy(infoPtr);
10020 case WM_NCPAINT:
10021 if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
10022 return 0;
10023 goto fwd_msg;
10025 case WM_NOTIFY:
10026 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
10027 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
10028 else return 0;
10030 case WM_NOTIFYFORMAT:
10031 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
10033 case WM_PRINTCLIENT:
10034 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
10036 case WM_PAINT:
10037 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
10039 case WM_RBUTTONDBLCLK:
10040 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10042 case WM_RBUTTONDOWN:
10043 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10045 case WM_RBUTTONUP:
10046 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10048 case WM_SETCURSOR:
10049 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
10050 return TRUE;
10051 goto fwd_msg;
10053 case WM_SETFOCUS:
10054 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
10056 case WM_SETFONT:
10057 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
10059 case WM_SETREDRAW:
10060 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
10062 case WM_SIZE:
10063 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10065 case WM_STYLECHANGED:
10066 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10068 case WM_SYSCOLORCHANGE:
10069 COMCTL32_RefreshSysColors();
10070 return 0;
10072 /* case WM_TIMER: */
10073 case WM_THEMECHANGED:
10074 return LISTVIEW_ThemeChanged(infoPtr);
10076 case WM_VSCROLL:
10077 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10079 case WM_MOUSEWHEEL:
10080 if (wParam & (MK_SHIFT | MK_CONTROL))
10081 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10082 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
10084 case WM_WINDOWPOSCHANGED:
10085 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
10087 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
10088 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
10089 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
10091 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
10093 MEASUREITEMSTRUCT mis;
10094 mis.CtlType = ODT_LISTVIEW;
10095 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
10096 mis.itemID = -1;
10097 mis.itemWidth = 0;
10098 mis.itemData = 0;
10099 mis.itemHeight= infoPtr->nItemHeight;
10100 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
10101 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
10102 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
10105 LISTVIEW_UpdateSize(infoPtr);
10106 LISTVIEW_UpdateScroll(infoPtr);
10108 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10110 /* case WM_WININICHANGE: */
10112 default:
10113 if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
10114 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
10116 fwd_msg:
10117 /* call default window procedure */
10118 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10123 /***
10124 * DESCRIPTION:
10125 * Registers the window class.
10127 * PARAMETER(S):
10128 * None
10130 * RETURN:
10131 * None
10133 void LISTVIEW_Register(void)
10135 WNDCLASSW wndClass;
10137 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
10138 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
10139 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
10140 wndClass.cbClsExtra = 0;
10141 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
10142 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
10143 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
10144 wndClass.lpszClassName = WC_LISTVIEWW;
10145 RegisterClassW(&wndClass);
10148 /***
10149 * DESCRIPTION:
10150 * Unregisters the window class.
10152 * PARAMETER(S):
10153 * None
10155 * RETURN:
10156 * None
10158 void LISTVIEW_Unregister(void)
10160 UnregisterClassW(WC_LISTVIEWW, NULL);
10163 /***
10164 * DESCRIPTION:
10165 * Handle any WM_COMMAND messages
10167 * PARAMETER(S):
10168 * [I] infoPtr : valid pointer to the listview structure
10169 * [I] wParam : the first message parameter
10170 * [I] lParam : the second message parameter
10172 * RETURN:
10173 * Zero.
10175 static LRESULT LISTVIEW_Command(const LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
10177 switch (HIWORD(wParam))
10179 case EN_UPDATE:
10182 * Adjust the edit window size
10184 WCHAR buffer[1024];
10185 HDC hdc = GetDC(infoPtr->hwndEdit);
10186 HFONT hFont, hOldFont = 0;
10187 RECT rect;
10188 SIZE sz;
10190 if (!infoPtr->hwndEdit || !hdc) return 0;
10191 GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
10192 GetWindowRect(infoPtr->hwndEdit, &rect);
10194 /* Select font to get the right dimension of the string */
10195 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
10196 if(hFont != 0)
10198 hOldFont = SelectObject(hdc, hFont);
10201 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
10203 TEXTMETRICW textMetric;
10205 /* Add Extra spacing for the next character */
10206 GetTextMetricsW(hdc, &textMetric);
10207 sz.cx += (textMetric.tmMaxCharWidth * 2);
10209 SetWindowPos (
10210 infoPtr->hwndEdit,
10211 HWND_TOP,
10214 sz.cx,
10215 rect.bottom - rect.top,
10216 SWP_DRAWFRAME|SWP_NOMOVE);
10218 if(hFont != 0)
10219 SelectObject(hdc, hOldFont);
10221 ReleaseDC(infoPtr->hwndEdit, hdc);
10223 break;
10226 default:
10227 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
10230 return 0;
10234 /***
10235 * DESCRIPTION:
10236 * Subclassed edit control windproc function
10238 * PARAMETER(S):
10239 * [I] hwnd : the edit window handle
10240 * [I] uMsg : the message that is to be processed
10241 * [I] wParam : first message parameter
10242 * [I] lParam : second message parameter
10243 * [I] isW : TRUE if input is Unicode
10245 * RETURN:
10246 * Zero.
10248 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
10250 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
10251 BOOL cancel = FALSE;
10253 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10254 hwnd, uMsg, wParam, lParam, isW);
10256 switch (uMsg)
10258 case WM_GETDLGCODE:
10259 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
10261 case WM_KILLFOCUS:
10262 break;
10264 case WM_DESTROY:
10266 WNDPROC editProc = infoPtr->EditWndProc;
10267 infoPtr->EditWndProc = 0;
10268 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
10269 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
10272 case WM_KEYDOWN:
10273 if (VK_ESCAPE == (INT)wParam)
10275 cancel = TRUE;
10276 break;
10278 else if (VK_RETURN == (INT)wParam)
10279 break;
10281 default:
10282 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
10285 /* kill the edit */
10286 if (infoPtr->hwndEdit)
10288 LPWSTR buffer = NULL;
10290 infoPtr->hwndEdit = 0;
10291 if (!cancel)
10293 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
10295 if (len)
10297 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
10299 if (isW) GetWindowTextW(hwnd, buffer, len+1);
10300 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
10304 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
10306 Free(buffer);
10309 SendMessageW(hwnd, WM_CLOSE, 0, 0);
10310 return 0;
10313 /***
10314 * DESCRIPTION:
10315 * Subclassed edit control Unicode windproc function
10317 * PARAMETER(S):
10318 * [I] hwnd : the edit window handle
10319 * [I] uMsg : the message that is to be processed
10320 * [I] wParam : first message parameter
10321 * [I] lParam : second message parameter
10323 * RETURN:
10325 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10327 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
10330 /***
10331 * DESCRIPTION:
10332 * Subclassed edit control ANSI windproc function
10334 * PARAMETER(S):
10335 * [I] hwnd : the edit window handle
10336 * [I] uMsg : the message that is to be processed
10337 * [I] wParam : first message parameter
10338 * [I] lParam : second message parameter
10340 * RETURN:
10342 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10344 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
10347 /***
10348 * DESCRIPTION:
10349 * Creates a subclassed edit control
10351 * PARAMETER(S):
10352 * [I] infoPtr : valid pointer to the listview structure
10353 * [I] text : initial text for the edit
10354 * [I] style : the window style
10355 * [I] isW : TRUE if input is Unicode
10357 * RETURN:
10359 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
10360 INT x, INT y, INT width, INT height, BOOL isW)
10362 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
10363 HWND hedit;
10364 SIZE sz;
10365 HDC hdc;
10366 HDC hOldFont=0;
10367 TEXTMETRICW textMetric;
10368 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
10370 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
10372 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
10373 hdc = GetDC(infoPtr->hwndSelf);
10375 /* Select the font to get appropriate metric dimensions */
10376 if(infoPtr->hFont != 0)
10377 hOldFont = SelectObject(hdc, infoPtr->hFont);
10379 /*Get String Length in pixels */
10380 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
10382 /*Add Extra spacing for the next character */
10383 GetTextMetricsW(hdc, &textMetric);
10384 sz.cx += (textMetric.tmMaxCharWidth * 2);
10386 if(infoPtr->hFont != 0)
10387 SelectObject(hdc, hOldFont);
10389 ReleaseDC(infoPtr->hwndSelf, hdc);
10390 if (isW)
10391 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10392 else
10393 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10395 if (!hedit) return 0;
10397 infoPtr->EditWndProc = (WNDPROC)
10398 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
10399 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
10401 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
10403 return hedit;