push 0bdbb9493a3f833df10c918231fa8ec20b56a418
[wine/hacks.git] / dlls / comctl32 / listview.c
blob9d1b4ffcaded730a340ede4fd6af3b2decdc47a1
1 /*
2 * Listview control
4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2001 CodeWeavers Inc.
8 * Copyright 2002 Dimitrie O. Paun
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 * NOTES
26 * This code was audited for completeness against the documented features
27 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
29 * Unless otherwise noted, we believe this code to be complete, as per
30 * the specification mentioned above.
31 * If you discover missing features, or bugs, please note them below.
33 * TODO:
35 * Default Message Processing
36 * -- EN_KILLFOCUS should be handled in WM_COMMAND
37 * -- WM_CREATE: create the icon and small icon image lists at this point only if
38 * the LVS_SHAREIMAGELISTS style is not specified.
39 * -- WM_ERASEBKGND: forward this message to the parent window if the bkgnd
40 * color is CLR_NONE.
41 * -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
42 * or small icon and the LVS_AUTOARRANGE style is specified.
43 * -- WM_TIMER
44 * -- WM_WININICHANGE
46 * Features
47 * -- Hot item handling, mouse hovering
48 * -- Workareas support
49 * -- Tilemode support
50 * -- Groups support
52 * Bugs
53 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
54 * -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
55 * -- LVA_SNAPTOGRID not implemented
56 * -- LISTVIEW_ApproximateViewRect partially implemented
57 * -- LISTVIEW_[GS]etColumnOrderArray stubs
58 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
59 * -- LISTVIEW_SetIconSpacing is incomplete
60 * -- LISTVIEW_SortItems is broken
61 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
63 * Speedups
64 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
65 * linear in the number of items in the list, and this is
66 * unacceptable for large lists.
67 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
68 * instead of inserting in the right spot
69 * -- we should keep an ordered array of coordinates in iconic mode
70 * this would allow to frame items (iterator_frameditems),
71 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
73 * Flags
74 * -- LVIF_COLUMNS
75 * -- LVIF_GROUPID
76 * -- LVIF_NORECOMPUTE
78 * States
79 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
80 * -- LVIS_CUT
81 * -- LVIS_DROPHILITED
82 * -- LVIS_OVERLAYMASK
84 * Styles
85 * -- LVS_NOLABELWRAP
86 * -- LVS_NOSCROLL (see Q137520)
87 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
88 * -- LVS_ALIGNTOP
89 * -- LVS_TYPESTYLEMASK
91 * Extended Styles
92 * -- LVS_EX_BORDERSELECT
93 * -- LVS_EX_FLATSB
94 * -- LVS_EX_HEADERDRAGDROP
95 * -- LVS_EX_INFOTIP
96 * -- LVS_EX_LABELTIP
97 * -- LVS_EX_MULTIWORKAREAS
98 * -- LVS_EX_REGIONAL
99 * -- LVS_EX_SIMPLESELECT
100 * -- LVS_EX_TWOCLICKACTIVATE
101 * -- LVS_EX_UNDERLINECOLD
102 * -- LVS_EX_UNDERLINEHOT
104 * Notifications:
105 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
106 * -- LVN_GETINFOTIP
107 * -- LVN_HOTTRACK
108 * -- LVN_MARQUEEBEGIN
109 * -- LVN_SETDISPINFO
110 * -- NM_HOVER
111 * -- LVN_BEGINRDRAG
113 * Messages:
114 * -- LVM_CANCELEDITLABEL
115 * -- LVM_ENABLEGROUPVIEW
116 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
117 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
118 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
119 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
120 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
121 * -- LVM_GETINSERTMARKRECT
122 * -- LVM_GETNUMBEROFWORKAREAS
123 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
124 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
125 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
126 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
127 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
128 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
129 * -- LVM_GETVIEW, LVM_SETVIEW
130 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
131 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
132 * -- LVM_INSERTGROUPSORTED
133 * -- LVM_INSERTMARKHITTEST
134 * -- LVM_ISGROUPVIEWENABLED
135 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
136 * -- LVM_MOVEGROUP
137 * -- LVM_MOVEITEMTOGROUP
138 * -- LVM_SETINFOTIP
139 * -- LVM_SETTILEWIDTH
140 * -- LVM_SORTGROUPS
141 * -- LVM_SORTITEMSEX
143 * Macros:
144 * -- ListView_GetCheckSate, ListView_SetCheckState
145 * -- ListView_GetHoverTime, ListView_SetHoverTime
146 * -- ListView_GetISearchString
147 * -- ListView_GetNumberOfWorkAreas
148 * -- ListView_GetOrigin
149 * -- ListView_GetTextBkColor
150 * -- ListView_GetUnicodeFormat, ListView_SetUnicodeFormat
151 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
152 * -- ListView_SortItemsEx
154 * Functions:
155 * -- LVGroupComparE
157 * Known differences in message stream from native control (not known if
158 * these differences cause problems):
159 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
160 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
161 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
162 * processing for "USEDOUBLECLICKTIME".
165 #include "config.h"
166 #include "wine/port.h"
168 #include <assert.h>
169 #include <ctype.h>
170 #include <string.h>
171 #include <stdlib.h>
172 #include <stdarg.h>
173 #include <stdio.h>
175 #include "windef.h"
176 #include "winbase.h"
177 #include "winnt.h"
178 #include "wingdi.h"
179 #include "winuser.h"
180 #include "winnls.h"
181 #include "commctrl.h"
182 #include "comctl32.h"
183 #include "uxtheme.h"
185 #include "wine/debug.h"
186 #include "wine/unicode.h"
188 WINE_DEFAULT_DEBUG_CHANNEL(listview);
190 /* make sure you set this to 0 for production use! */
191 #define DEBUG_RANGES 1
193 typedef struct tagCOLUMN_INFO
195 RECT rcHeader; /* tracks the header's rectangle */
196 int fmt; /* same as LVCOLUMN.fmt */
197 } COLUMN_INFO;
199 typedef struct tagITEMHDR
201 LPWSTR pszText;
202 INT iImage;
203 } ITEMHDR, *LPITEMHDR;
205 typedef struct tagSUBITEM_INFO
207 ITEMHDR hdr;
208 INT iSubItem;
209 } SUBITEM_INFO;
211 typedef struct tagITEM_INFO
213 ITEMHDR hdr;
214 UINT state;
215 LPARAM lParam;
216 INT iIndent;
217 } ITEM_INFO;
219 typedef struct tagRANGE
221 INT lower;
222 INT upper;
223 } RANGE;
225 typedef struct tagRANGES
227 HDPA hdpa;
228 } *RANGES;
230 typedef struct tagITERATOR
232 INT nItem;
233 INT nSpecial;
234 RANGE range;
235 RANGES ranges;
236 INT index;
237 } ITERATOR;
239 typedef struct tagDELAYED_ITEM_EDIT
241 BOOL fEnabled;
242 INT iItem;
243 } DELAYED_ITEM_EDIT;
245 typedef struct tagLISTVIEW_INFO
247 HWND hwndSelf;
248 HBRUSH hBkBrush;
249 COLORREF clrBk;
250 COLORREF clrText;
251 COLORREF clrTextBk;
252 HIMAGELIST himlNormal;
253 HIMAGELIST himlSmall;
254 HIMAGELIST himlState;
255 BOOL bLButtonDown;
256 BOOL bRButtonDown;
257 BOOL bDragging;
258 POINT ptClickPos; /* point where the user clicked */
259 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
260 INT nItemHeight;
261 INT nItemWidth;
262 RANGES selectionRanges;
263 INT nSelectionMark;
264 INT nHotItem;
265 SHORT notifyFormat;
266 HWND hwndNotify;
267 RECT rcList; /* This rectangle is really the window
268 * client rectangle possibly reduced by the
269 * horizontal scroll bar and/or header - see
270 * LISTVIEW_UpdateSize. This rectangle offset
271 * by the LISTVIEW_GetOrigin value is in
272 * client coordinates */
273 SIZE iconSize;
274 SIZE iconSpacing;
275 SIZE iconStateSize;
276 UINT uCallbackMask;
277 HWND hwndHeader;
278 HCURSOR hHotCursor;
279 HFONT hDefaultFont;
280 HFONT hFont;
281 INT ntmHeight; /* Some cached metrics of the font used */
282 INT ntmMaxCharWidth; /* by the listview to draw items */
283 INT nEllipsisWidth;
284 BOOL bRedraw; /* Turns on/off repaints & invalidations */
285 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
286 BOOL bFocus;
287 BOOL bDoChangeNotify; /* send change notification messages? */
288 INT nFocusedItem;
289 RECT rcFocus;
290 DWORD dwStyle; /* the cached window GWL_STYLE */
291 DWORD dwLvExStyle; /* extended listview style */
292 INT nItemCount; /* the number of items in the list */
293 HDPA hdpaItems; /* array ITEM_INFO pointers */
294 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
295 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
296 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
297 POINT currIconPos; /* this is the position next icon will be placed */
298 PFNLVCOMPARE pfnCompare;
299 LPARAM lParamSort;
300 HWND hwndEdit;
301 WNDPROC EditWndProc;
302 INT nEditLabelItem;
303 DWORD dwHoverTime;
304 HWND hwndToolTip;
306 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
308 DWORD lastKeyPressTimestamp;
309 WPARAM charCode;
310 INT nSearchParamLength;
311 WCHAR szSearchParam[ MAX_PATH ];
312 BOOL bIsDrawing;
313 INT nMeasureItemHeight;
314 INT xTrackLine; /* The x coefficient of the track line or -1 if none */
315 DELAYED_ITEM_EDIT itemEdit; /* Pointer to this structure will be the timer ID */
316 } LISTVIEW_INFO;
319 * constants
321 /* How many we debug buffer to allocate */
322 #define DEBUG_BUFFERS 20
323 /* The size of a single debug bbuffer */
324 #define DEBUG_BUFFER_SIZE 256
326 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
327 #define SB_INTERNAL -1
329 /* maximum size of a label */
330 #define DISP_TEXT_SIZE 512
332 /* padding for items in list and small icon display modes */
333 #define WIDTH_PADDING 12
335 /* padding for items in list, report and small icon display modes */
336 #define HEIGHT_PADDING 1
338 /* offset of items in report display mode */
339 #define REPORT_MARGINX 2
341 /* padding for icon in large icon display mode
342 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
343 * that HITTEST will see.
344 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
345 * ICON_TOP_PADDING - sum of the two above.
346 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
347 * LABEL_HOR_PADDING - between text and sides of box
348 * LABEL_VERT_PADDING - between bottom of text and end of box
350 * ICON_LR_PADDING - additional width above icon size.
351 * ICON_LR_HALF - half of the above value
353 #define ICON_TOP_PADDING_NOTHITABLE 2
354 #define ICON_TOP_PADDING_HITABLE 2
355 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
356 #define ICON_BOTTOM_PADDING 4
357 #define LABEL_HOR_PADDING 5
358 #define LABEL_VERT_PADDING 7
359 #define ICON_LR_PADDING 16
360 #define ICON_LR_HALF (ICON_LR_PADDING/2)
362 /* default label width for items in list and small icon display modes */
363 #define DEFAULT_LABEL_WIDTH 40
365 /* default column width for items in list display mode */
366 #define DEFAULT_COLUMN_WIDTH 128
368 /* Size of "line" scroll for V & H scrolls */
369 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
371 /* Padding between image and label */
372 #define IMAGE_PADDING 2
374 /* Padding behind the label */
375 #define TRAILING_LABEL_PADDING 12
376 #define TRAILING_HEADER_PADDING 11
378 /* Border for the icon caption */
379 #define CAPTION_BORDER 2
381 /* Standard DrawText flags */
382 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
383 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
384 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
386 /* Image index from state */
387 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
389 /* The time in milliseconds to reset the search in the list */
390 #define KEY_DELAY 450
392 /* Dump the LISTVIEW_INFO structure to the debug channel */
393 #define LISTVIEW_DUMP(iP) do { \
394 TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
395 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
396 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
397 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
398 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
399 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
400 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
401 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
402 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
403 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
404 } while(0)
406 static const WCHAR themeClass[] = {'L','i','s','t','V','i','e','w',0};
409 * forward declarations
411 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *, LPLVITEMW, BOOL);
412 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *, INT, LPRECT);
413 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *, INT, LPPOINT);
414 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *, INT, LPPOINT);
415 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *, INT, LPRECT);
416 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *, INT);
417 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *, LPPOINT);
418 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *, LPRECT);
419 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
420 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LVITEMW *, BOOL);
421 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *);
422 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
423 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
424 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
425 static LRESULT LISTVIEW_Command(const LISTVIEW_INFO *, WPARAM, LPARAM);
426 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
427 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *, LPCWSTR, BOOL);
428 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT, BOOL);
429 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *, INT, UINT);
430 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
431 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
432 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
433 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *);
434 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
435 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
436 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
437 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
439 /******** Text handling functions *************************************/
441 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
442 * text string. The string may be ANSI or Unicode, in which case
443 * the boolean isW tells us the type of the string.
445 * The name of the function tell what type of strings it expects:
446 * W: Unicode, T: ANSI/Unicode - function of isW
449 static inline BOOL is_textW(LPCWSTR text)
451 return text != NULL && text != LPSTR_TEXTCALLBACKW;
454 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
456 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
457 return is_textW(text);
460 static inline int textlenT(LPCWSTR text, BOOL isW)
462 return !is_textT(text, isW) ? 0 :
463 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
466 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
468 if (isDestW)
469 if (isSrcW) lstrcpynW(dest, src, max);
470 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
471 else
472 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
473 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
476 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
478 LPWSTR wstr = (LPWSTR)text;
480 if (!isW && is_textT(text, isW))
482 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
483 wstr = Alloc(len * sizeof(WCHAR));
484 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
486 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
487 return wstr;
490 static inline void textfreeT(LPWSTR wstr, BOOL isW)
492 if (!isW && is_textT(wstr, isW)) Free (wstr);
496 * dest is a pointer to a Unicode string
497 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
499 static BOOL textsetptrT(LPWSTR *dest, LPCWSTR src, BOOL isW)
501 BOOL bResult = TRUE;
503 if (src == LPSTR_TEXTCALLBACKW)
505 if (is_textW(*dest)) Free(*dest);
506 *dest = LPSTR_TEXTCALLBACKW;
508 else
510 LPWSTR pszText = textdupTtoW(src, isW);
511 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
512 bResult = Str_SetPtrW(dest, pszText);
513 textfreeT(pszText, isW);
515 return bResult;
519 * compares a Unicode to a Unicode/ANSI text string
521 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
523 if (!aw) return bt ? -1 : 0;
524 if (!bt) return aw ? 1 : 0;
525 if (aw == LPSTR_TEXTCALLBACKW)
526 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
527 if (bt != LPSTR_TEXTCALLBACKW)
529 LPWSTR bw = textdupTtoW(bt, isW);
530 int r = bw ? lstrcmpW(aw, bw) : 1;
531 textfreeT(bw, isW);
532 return r;
535 return 1;
538 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
540 int res;
542 n = min(min(n, lstrlenW(s1)), lstrlenW(s2));
543 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
544 return res ? res - sizeof(WCHAR) : res;
547 /******** Debugging functions *****************************************/
549 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
551 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
552 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
555 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
557 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
558 n = min(textlenT(text, isW), n);
559 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
562 static char* debug_getbuf(void)
564 static int index = 0;
565 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
566 return buffers[index++ % DEBUG_BUFFERS];
569 static inline const char* debugrange(const RANGE *lprng)
571 if (!lprng) return "(null)";
572 return wine_dbg_sprintf("[%d, %d]", lprng->lower, lprng->upper);
575 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
577 char* buf = debug_getbuf(), *text = buf;
578 int len, size = DEBUG_BUFFER_SIZE;
580 if (pScrollInfo == NULL) return "(null)";
581 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
582 if (len == -1) goto end; buf += len; size -= len;
583 if (pScrollInfo->fMask & SIF_RANGE)
584 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
585 else len = 0;
586 if (len == -1) goto end; buf += len; size -= len;
587 if (pScrollInfo->fMask & SIF_PAGE)
588 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
589 else len = 0;
590 if (len == -1) goto end; buf += len; size -= len;
591 if (pScrollInfo->fMask & SIF_POS)
592 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
593 else len = 0;
594 if (len == -1) goto end; buf += len; size -= len;
595 if (pScrollInfo->fMask & SIF_TRACKPOS)
596 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
597 else len = 0;
598 if (len == -1) goto end; buf += len; size -= len;
599 goto undo;
600 end:
601 buf = text + strlen(text);
602 undo:
603 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
604 return text;
607 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
609 if (!plvnm) return "(null)";
610 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
611 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
612 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
613 plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam);
616 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
618 char* buf = debug_getbuf(), *text = buf;
619 int len, size = DEBUG_BUFFER_SIZE;
621 if (lpLVItem == NULL) return "(null)";
622 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
623 if (len == -1) goto end; buf += len; size -= len;
624 if (lpLVItem->mask & LVIF_STATE)
625 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
626 else len = 0;
627 if (len == -1) goto end; buf += len; size -= len;
628 if (lpLVItem->mask & LVIF_TEXT)
629 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
630 else len = 0;
631 if (len == -1) goto end; buf += len; size -= len;
632 if (lpLVItem->mask & LVIF_IMAGE)
633 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
634 else len = 0;
635 if (len == -1) goto end; buf += len; size -= len;
636 if (lpLVItem->mask & LVIF_PARAM)
637 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
638 else len = 0;
639 if (len == -1) goto end; buf += len; size -= len;
640 if (lpLVItem->mask & LVIF_INDENT)
641 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
642 else len = 0;
643 if (len == -1) goto end; buf += len; size -= len;
644 goto undo;
645 end:
646 buf = text + strlen(text);
647 undo:
648 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
649 return text;
652 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
654 char* buf = debug_getbuf(), *text = buf;
655 int len, size = DEBUG_BUFFER_SIZE;
657 if (lpColumn == NULL) return "(null)";
658 len = snprintf(buf, size, "{");
659 if (len == -1) goto end; buf += len; size -= len;
660 if (lpColumn->mask & LVCF_SUBITEM)
661 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
662 else len = 0;
663 if (len == -1) goto end; buf += len; size -= len;
664 if (lpColumn->mask & LVCF_FMT)
665 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
666 else len = 0;
667 if (len == -1) goto end; buf += len; size -= len;
668 if (lpColumn->mask & LVCF_WIDTH)
669 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
670 else len = 0;
671 if (len == -1) goto end; buf += len; size -= len;
672 if (lpColumn->mask & LVCF_TEXT)
673 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
674 else len = 0;
675 if (len == -1) goto end; buf += len; size -= len;
676 if (lpColumn->mask & LVCF_IMAGE)
677 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
678 else len = 0;
679 if (len == -1) goto end; buf += len; size -= len;
680 if (lpColumn->mask & LVCF_ORDER)
681 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
682 else len = 0;
683 if (len == -1) goto end; buf += len; size -= len;
684 goto undo;
685 end:
686 buf = text + strlen(text);
687 undo:
688 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
689 return text;
692 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
694 if (!lpht) return "(null)";
696 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
697 wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
700 /* Return the corresponding text for a given scroll value */
701 static inline LPCSTR debugscrollcode(int nScrollCode)
703 switch(nScrollCode)
705 case SB_LINELEFT: return "SB_LINELEFT";
706 case SB_LINERIGHT: return "SB_LINERIGHT";
707 case SB_PAGELEFT: return "SB_PAGELEFT";
708 case SB_PAGERIGHT: return "SB_PAGERIGHT";
709 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
710 case SB_THUMBTRACK: return "SB_THUMBTRACK";
711 case SB_ENDSCROLL: return "SB_ENDSCROLL";
712 case SB_INTERNAL: return "SB_INTERNAL";
713 default: return "unknown";
718 /******** Notification functions i************************************/
720 static LRESULT notify_forward_header(const LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
722 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
723 (WPARAM)lpnmh->hdr.idFrom, (LPARAM)lpnmh);
726 static LRESULT notify_hdr(const LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
728 LRESULT result;
730 TRACE("(code=%d)\n", code);
732 pnmh->hwndFrom = infoPtr->hwndSelf;
733 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
734 pnmh->code = code;
735 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, pnmh->idFrom, (LPARAM)pnmh);
737 TRACE(" <= %ld\n", result);
739 return result;
742 static inline BOOL notify(const LISTVIEW_INFO *infoPtr, INT code)
744 NMHDR nmh;
745 HWND hwnd = infoPtr->hwndSelf;
746 notify_hdr(infoPtr, code, &nmh);
747 return IsWindow(hwnd);
750 static inline void notify_itemactivate(const LISTVIEW_INFO *infoPtr, const LVHITTESTINFO *htInfo)
752 NMITEMACTIVATE nmia;
753 LVITEMW item;
755 if (htInfo) {
756 nmia.uNewState = 0;
757 nmia.uOldState = 0;
758 nmia.uChanged = 0;
759 nmia.uKeyFlags = 0;
761 item.mask = LVIF_PARAM|LVIF_STATE;
762 item.iItem = htInfo->iItem;
763 item.iSubItem = 0;
764 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
765 nmia.lParam = item.lParam;
766 nmia.uOldState = item.state;
767 nmia.uNewState = item.state | LVIS_ACTIVATING;
768 nmia.uChanged = LVIF_STATE;
771 nmia.iItem = htInfo->iItem;
772 nmia.iSubItem = htInfo->iSubItem;
773 nmia.ptAction = htInfo->pt;
775 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
776 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
777 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
779 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
782 static inline LRESULT notify_listview(const LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
784 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
785 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
788 static BOOL notify_click(const LISTVIEW_INFO *infoPtr, INT code, const LVHITTESTINFO *lvht)
790 NMLISTVIEW nmlv;
791 LVITEMW item;
792 HWND hwnd = infoPtr->hwndSelf;
794 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
795 ZeroMemory(&nmlv, sizeof(nmlv));
796 nmlv.iItem = lvht->iItem;
797 nmlv.iSubItem = lvht->iSubItem;
798 nmlv.ptAction = lvht->pt;
799 item.mask = LVIF_PARAM;
800 item.iItem = lvht->iItem;
801 item.iSubItem = 0;
802 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
803 notify_listview(infoPtr, code, &nmlv);
804 return IsWindow(hwnd);
807 static BOOL notify_deleteitem(const LISTVIEW_INFO *infoPtr, INT nItem)
809 NMLISTVIEW nmlv;
810 LVITEMW item;
811 HWND hwnd = infoPtr->hwndSelf;
813 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
814 nmlv.iItem = nItem;
815 item.mask = LVIF_PARAM;
816 item.iItem = nItem;
817 item.iSubItem = 0;
818 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
819 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
820 return IsWindow(hwnd);
823 static int get_ansi_notification(UINT unicodeNotificationCode)
825 switch (unicodeNotificationCode)
827 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
828 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
829 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
830 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
831 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
832 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
834 ERR("unknown notification %x\n", unicodeNotificationCode);
835 assert(FALSE);
836 return 0;
840 Send notification. depends on dispinfoW having same
841 structure as dispinfoA.
842 infoPtr : listview struct
843 notificationCode : *Unicode* notification code
844 pdi : dispinfo structure (can be unicode or ansi)
845 isW : TRUE if dispinfo is Unicode
847 static BOOL notify_dispinfoT(const LISTVIEW_INFO *infoPtr, UINT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
849 BOOL bResult = FALSE;
850 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
851 INT cchTempBufMax = 0, savCchTextMax = 0;
852 UINT realNotifCode;
853 LPWSTR pszTempBuf = NULL, savPszText = NULL;
855 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
857 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
858 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
861 if (convertToAnsi || convertToUnicode)
863 if (notificationCode != LVN_GETDISPINFOW)
865 cchTempBufMax = convertToUnicode ?
866 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
867 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
869 else
871 cchTempBufMax = pdi->item.cchTextMax;
872 *pdi->item.pszText = 0; /* make sure we don't process garbage */
875 pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
876 if (!pszTempBuf) return FALSE;
878 if (convertToUnicode)
879 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
880 pszTempBuf, cchTempBufMax);
881 else
882 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
883 cchTempBufMax, NULL, NULL);
885 savCchTextMax = pdi->item.cchTextMax;
886 savPszText = pdi->item.pszText;
887 pdi->item.pszText = pszTempBuf;
888 pdi->item.cchTextMax = cchTempBufMax;
891 if (infoPtr->notifyFormat == NFR_ANSI)
892 realNotifCode = get_ansi_notification(notificationCode);
893 else
894 realNotifCode = notificationCode;
895 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
896 bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
898 if (convertToUnicode || convertToAnsi)
900 if (convertToUnicode) /* note : pointer can be changed by app ! */
901 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
902 savCchTextMax, NULL, NULL);
903 else
904 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
905 savPszText, savCchTextMax);
906 pdi->item.pszText = savPszText; /* restores our buffer */
907 pdi->item.cchTextMax = savCchTextMax;
908 Free (pszTempBuf);
910 return bResult;
913 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, const LISTVIEW_INFO *infoPtr, HDC hdc,
914 const RECT *rcBounds, const LVITEMW *lplvItem)
916 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
917 lpnmlvcd->nmcd.hdc = hdc;
918 lpnmlvcd->nmcd.rc = *rcBounds;
919 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
920 lpnmlvcd->clrText = infoPtr->clrText;
921 if (!lplvItem) return;
922 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
923 lpnmlvcd->iSubItem = lplvItem->iSubItem;
924 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
925 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
926 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
927 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
930 static inline DWORD notify_customdraw (const LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
932 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
933 DWORD result;
935 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
936 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
937 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
938 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
939 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
940 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
941 return result;
944 static void prepaint_setup (const LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd, BOOL SubItem)
946 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
947 lpnmlvcd->clrTextBk = comctl32_color.clrWindow;
948 if (lpnmlvcd->clrText == CLR_DEFAULT)
949 lpnmlvcd->clrText = comctl32_color.clrWindowText;
951 /* apparently, for selected items, we have to override the returned values */
952 if (!SubItem)
954 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
956 if (infoPtr->bFocus)
958 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
959 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
961 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
963 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
964 lpnmlvcd->clrText = comctl32_color.clrBtnText;
969 /* Set the text attributes */
970 if (lpnmlvcd->clrTextBk != CLR_NONE)
972 SetBkMode(hdc, OPAQUE);
973 SetBkColor(hdc,lpnmlvcd->clrTextBk);
975 else
976 SetBkMode(hdc, TRANSPARENT);
977 SetTextColor(hdc, lpnmlvcd->clrText);
980 static inline DWORD notify_postpaint (const LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
982 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
985 /******** Item iterator functions **********************************/
987 static RANGES ranges_create(int count);
988 static void ranges_destroy(RANGES ranges);
989 static BOOL ranges_add(RANGES ranges, RANGE range);
990 static BOOL ranges_del(RANGES ranges, RANGE range);
991 static void ranges_dump(RANGES ranges);
993 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
995 RANGE range = { nItem, nItem + 1 };
997 return ranges_add(ranges, range);
1000 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
1002 RANGE range = { nItem, nItem + 1 };
1004 return ranges_del(ranges, range);
1007 /***
1008 * ITERATOR DOCUMENTATION
1010 * The iterator functions allow for easy, and convenient iteration
1011 * over items of interest in the list. Typically, you create a
1012 * iterator, use it, and destroy it, as such:
1013 * ITERATOR i;
1015 * iterator_xxxitems(&i, ...);
1016 * while (iterator_{prev,next}(&i)
1018 * //code which uses i.nItem
1020 * iterator_destroy(&i);
1022 * where xxx is either: framed, or visible.
1023 * Note that it is important that the code destroys the iterator
1024 * after it's done with it, as the creation of the iterator may
1025 * allocate memory, which thus needs to be freed.
1027 * You can iterate both forwards, and backwards through the list,
1028 * by using iterator_next or iterator_prev respectively.
1030 * Lower numbered items are draw on top of higher number items in
1031 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1032 * items may overlap). So, to test items, you should use
1033 * iterator_next
1034 * which lists the items top to bottom (in Z-order).
1035 * For drawing items, you should use
1036 * iterator_prev
1037 * which lists the items bottom to top (in Z-order).
1038 * If you keep iterating over the items after the end-of-items
1039 * marker (-1) is returned, the iterator will start from the
1040 * beginning. Typically, you don't need to test for -1,
1041 * because iterator_{next,prev} will return TRUE if more items
1042 * are to be iterated over, or FALSE otherwise.
1044 * Note: the iterator is defined to be bidirectional. That is,
1045 * any number of prev followed by any number of next, or
1046 * five versa, should leave the iterator at the same item:
1047 * prev * n, next * n = next * n, prev * n
1049 * The iterator has a notion of an out-of-order, special item,
1050 * which sits at the start of the list. This is used in
1051 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1052 * which needs to be first, as it may overlap other items.
1054 * The code is a bit messy because we have:
1055 * - a special item to deal with
1056 * - simple range, or composite range
1057 * - empty range.
1058 * If you find bugs, or want to add features, please make sure you
1059 * always check/modify *both* iterator_prev, and iterator_next.
1062 /****
1063 * This function iterates through the items in increasing order,
1064 * but prefixed by the special item, then -1. That is:
1065 * special, 1, 2, 3, ..., n, -1.
1066 * Each item is listed only once.
1068 static inline BOOL iterator_next(ITERATOR* i)
1070 if (i->nItem == -1)
1072 i->nItem = i->nSpecial;
1073 if (i->nItem != -1) return TRUE;
1075 if (i->nItem == i->nSpecial)
1077 if (i->ranges) i->index = 0;
1078 goto pickarange;
1081 i->nItem++;
1082 testitem:
1083 if (i->nItem == i->nSpecial) i->nItem++;
1084 if (i->nItem < i->range.upper) return TRUE;
1086 pickarange:
1087 if (i->ranges)
1089 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1090 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1091 else goto end;
1093 else if (i->nItem >= i->range.upper) goto end;
1095 i->nItem = i->range.lower;
1096 if (i->nItem >= 0) goto testitem;
1097 end:
1098 i->nItem = -1;
1099 return FALSE;
1102 /****
1103 * This function iterates through the items in decreasing order,
1104 * followed by the special item, then -1. That is:
1105 * n, n-1, ..., 3, 2, 1, special, -1.
1106 * Each item is listed only once.
1108 static inline BOOL iterator_prev(ITERATOR* i)
1110 BOOL start = FALSE;
1112 if (i->nItem == -1)
1114 start = TRUE;
1115 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1116 goto pickarange;
1118 if (i->nItem == i->nSpecial)
1120 i->nItem = -1;
1121 return FALSE;
1124 testitem:
1125 i->nItem--;
1126 if (i->nItem == i->nSpecial) i->nItem--;
1127 if (i->nItem >= i->range.lower) return TRUE;
1129 pickarange:
1130 if (i->ranges)
1132 if (i->index > 0)
1133 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1134 else goto end;
1136 else if (!start && i->nItem < i->range.lower) goto end;
1138 i->nItem = i->range.upper;
1139 if (i->nItem > 0) goto testitem;
1140 end:
1141 return (i->nItem = i->nSpecial) != -1;
1144 static RANGE iterator_range(const ITERATOR *i)
1146 RANGE range;
1148 if (!i->ranges) return i->range;
1150 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1152 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1153 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1155 else range.lower = range.upper = 0;
1157 return range;
1160 /***
1161 * Releases resources associated with this ierator.
1163 static inline void iterator_destroy(const ITERATOR *i)
1165 ranges_destroy(i->ranges);
1168 /***
1169 * Create an empty iterator.
1171 static inline BOOL iterator_empty(ITERATOR* i)
1173 ZeroMemory(i, sizeof(*i));
1174 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1175 return TRUE;
1178 /***
1179 * Create an iterator over a range.
1181 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1183 iterator_empty(i);
1184 i->range = range;
1185 return TRUE;
1188 /***
1189 * Create an iterator over a bunch of ranges.
1190 * Please note that the iterator will take ownership of the ranges,
1191 * and will free them upon destruction.
1193 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1195 iterator_empty(i);
1196 i->ranges = ranges;
1197 return TRUE;
1200 /***
1201 * Creates an iterator over the items which intersect lprc.
1203 static BOOL iterator_frameditems(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *lprc)
1205 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1206 RECT frame = *lprc, rcItem, rcTemp;
1207 POINT Origin;
1209 /* in case we fail, we want to return an empty iterator */
1210 if (!iterator_empty(i)) return FALSE;
1212 LISTVIEW_GetOrigin(infoPtr, &Origin);
1214 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc));
1215 OffsetRect(&frame, -Origin.x, -Origin.y);
1217 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1219 INT nItem;
1221 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1223 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1224 if (IntersectRect(&rcTemp, &rcItem, lprc))
1225 i->nSpecial = infoPtr->nFocusedItem;
1227 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1228 /* to do better here, we need to have PosX, and PosY sorted */
1229 TRACE("building icon ranges:\n");
1230 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1232 rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1233 rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1234 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1235 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1236 if (IntersectRect(&rcTemp, &rcItem, &frame))
1237 ranges_additem(i->ranges, nItem);
1239 return TRUE;
1241 else if (uView == LVS_REPORT)
1243 RANGE range;
1245 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1246 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1248 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1249 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1250 if (range.upper <= range.lower) return TRUE;
1251 if (!iterator_rangeitems(i, range)) return FALSE;
1252 TRACE(" report=%s\n", debugrange(&i->range));
1254 else
1256 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1257 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1258 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1259 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1260 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1261 INT lower = nFirstCol * nPerCol + nFirstRow;
1262 RANGE item_range;
1263 INT nCol;
1265 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1266 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1268 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1270 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1271 TRACE("building list ranges:\n");
1272 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1274 item_range.lower = nCol * nPerCol + nFirstRow;
1275 if(item_range.lower >= infoPtr->nItemCount) break;
1276 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1277 TRACE(" list=%s\n", debugrange(&item_range));
1278 ranges_add(i->ranges, item_range);
1282 return TRUE;
1285 /***
1286 * Creates an iterator over the items which intersect the visible region of hdc.
1288 static BOOL iterator_visibleitems(ITERATOR *i, const LISTVIEW_INFO *infoPtr, HDC hdc)
1290 POINT Origin, Position;
1291 RECT rcItem, rcClip;
1292 INT rgntype;
1294 rgntype = GetClipBox(hdc, &rcClip);
1295 if (rgntype == NULLREGION) return iterator_empty(i);
1296 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1297 if (rgntype == SIMPLEREGION) return TRUE;
1299 /* first deal with the special item */
1300 if (i->nSpecial != -1)
1302 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1303 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1306 /* if we can't deal with the region, we'll just go with the simple range */
1307 LISTVIEW_GetOrigin(infoPtr, &Origin);
1308 TRACE("building visible range:\n");
1309 if (!i->ranges && i->range.lower < i->range.upper)
1311 if (!(i->ranges = ranges_create(50))) return TRUE;
1312 if (!ranges_add(i->ranges, i->range))
1314 ranges_destroy(i->ranges);
1315 i->ranges = 0;
1316 return TRUE;
1320 /* now delete the invisible items from the list */
1321 while(iterator_next(i))
1323 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1324 rcItem.left = Position.x + Origin.x;
1325 rcItem.top = Position.y + Origin.y;
1326 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1327 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1328 if (!RectVisible(hdc, &rcItem))
1329 ranges_delitem(i->ranges, i->nItem);
1331 /* the iterator should restart on the next iterator_next */
1332 TRACE("done\n");
1334 return TRUE;
1337 /******** Misc helper functions ************************************/
1339 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1340 WPARAM wParam, LPARAM lParam, BOOL isW)
1342 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1343 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1346 static inline BOOL is_autoarrange(const LISTVIEW_INFO *infoPtr)
1348 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1350 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1351 (uView == LVS_ICON || uView == LVS_SMALLICON);
1354 static void toggle_checkbox_state(LISTVIEW_INFO *infoPtr, INT nItem)
1356 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
1357 if(state == 1 || state == 2)
1359 LVITEMW lvitem;
1360 state ^= 3;
1361 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
1362 lvitem.stateMask = LVIS_STATEIMAGEMASK;
1363 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
1367 /******** Internal API functions ************************************/
1369 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(const LISTVIEW_INFO *infoPtr, INT nSubItem)
1371 static COLUMN_INFO mainItem;
1373 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1374 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1375 return DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1378 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO *infoPtr, INT nSubItem, LPRECT lprc)
1380 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1383 static inline BOOL LISTVIEW_GetItemW(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1385 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1388 /* Listview invalidation functions: use _only_ these functions to invalidate */
1390 static inline BOOL is_redrawing(const LISTVIEW_INFO *infoPtr)
1392 return infoPtr->bRedraw;
1395 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO *infoPtr, const RECT* rect)
1397 if(!is_redrawing(infoPtr)) return;
1398 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect));
1399 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1402 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO *infoPtr, INT nItem)
1404 RECT rcBox;
1406 if(!is_redrawing(infoPtr)) return;
1407 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1408 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1411 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1413 POINT Origin, Position;
1414 RECT rcBox;
1416 if(!is_redrawing(infoPtr)) return;
1417 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1418 LISTVIEW_GetOrigin(infoPtr, &Origin);
1419 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1420 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1421 rcBox.top = 0;
1422 rcBox.bottom = infoPtr->nItemHeight;
1423 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1424 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1427 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO *infoPtr)
1429 LISTVIEW_InvalidateRect(infoPtr, NULL);
1432 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO *infoPtr, INT nColumn)
1434 RECT rcCol;
1436 if(!is_redrawing(infoPtr)) return;
1437 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1438 rcCol.top = infoPtr->rcList.top;
1439 rcCol.bottom = infoPtr->rcList.bottom;
1440 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1443 /***
1444 * DESCRIPTION:
1445 * Retrieves the number of items that can fit vertically in the client area.
1447 * PARAMETER(S):
1448 * [I] infoPtr : valid pointer to the listview structure
1450 * RETURN:
1451 * Number of items per row.
1453 static inline INT LISTVIEW_GetCountPerRow(const LISTVIEW_INFO *infoPtr)
1455 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1457 return max(nListWidth/infoPtr->nItemWidth, 1);
1460 /***
1461 * DESCRIPTION:
1462 * Retrieves the number of items that can fit horizontally in the client
1463 * area.
1465 * PARAMETER(S):
1466 * [I] infoPtr : valid pointer to the listview structure
1468 * RETURN:
1469 * Number of items per column.
1471 static inline INT LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO *infoPtr)
1473 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1475 return max(nListHeight / infoPtr->nItemHeight, 1);
1479 /*************************************************************************
1480 * LISTVIEW_ProcessLetterKeys
1482 * Processes keyboard messages generated by pressing the letter keys
1483 * on the keyboard.
1484 * What this does is perform a case insensitive search from the
1485 * current position with the following quirks:
1486 * - If two chars or more are pressed in quick succession we search
1487 * for the corresponding string (e.g. 'abc').
1488 * - If there is a delay we wipe away the current search string and
1489 * restart with just that char.
1490 * - If the user keeps pressing the same character, whether slowly or
1491 * fast, so that the search string is entirely composed of this
1492 * character ('aaaaa' for instance), then we search for first item
1493 * that starting with that character.
1494 * - If the user types the above character in quick succession, then
1495 * we must also search for the corresponding string ('aaaaa'), and
1496 * go to that string if there is a match.
1498 * PARAMETERS
1499 * [I] hwnd : handle to the window
1500 * [I] charCode : the character code, the actual character
1501 * [I] keyData : key data
1503 * RETURNS
1505 * Zero.
1507 * BUGS
1509 * - The current implementation has a list of characters it will
1510 * accept and it ignores everything else. In particular it will
1511 * ignore accentuated characters which seems to match what
1512 * Windows does. But I'm not sure it makes sense to follow
1513 * Windows there.
1514 * - We don't sound a beep when the search fails.
1516 * SEE ALSO
1518 * TREEVIEW_ProcessLetterKeys
1520 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1522 INT nItem;
1523 INT endidx,idx;
1524 LVITEMW item;
1525 WCHAR buffer[MAX_PATH];
1526 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1528 /* simple parameter checking */
1529 if (!charCode || !keyData) return 0;
1531 /* only allow the valid WM_CHARs through */
1532 if (!isalnumW(charCode) &&
1533 charCode != '.' && charCode != '`' && charCode != '!' &&
1534 charCode != '@' && charCode != '#' && charCode != '$' &&
1535 charCode != '%' && charCode != '^' && charCode != '&' &&
1536 charCode != '*' && charCode != '(' && charCode != ')' &&
1537 charCode != '-' && charCode != '_' && charCode != '+' &&
1538 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1539 charCode != '}' && charCode != '[' && charCode != '{' &&
1540 charCode != '/' && charCode != '?' && charCode != '>' &&
1541 charCode != '<' && charCode != ',' && charCode != '~')
1542 return 0;
1544 /* if there's one item or less, there is no where to go */
1545 if (infoPtr->nItemCount <= 1) return 0;
1547 /* update the search parameters */
1548 infoPtr->lastKeyPressTimestamp = GetTickCount();
1549 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1550 if (infoPtr->nSearchParamLength < MAX_PATH-1)
1551 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1552 if (infoPtr->charCode != charCode)
1553 infoPtr->charCode = charCode = 0;
1554 } else {
1555 infoPtr->charCode=charCode;
1556 infoPtr->szSearchParam[0]=charCode;
1557 infoPtr->nSearchParamLength=1;
1558 /* Redundant with the 1 char string */
1559 charCode=0;
1562 /* and search from the current position */
1563 nItem=-1;
1564 if (infoPtr->nFocusedItem >= 0) {
1565 endidx=infoPtr->nFocusedItem;
1566 idx=endidx;
1567 /* if looking for single character match,
1568 * then we must always move forward
1570 if (infoPtr->nSearchParamLength == 1)
1571 idx++;
1572 } else {
1573 endidx=infoPtr->nItemCount;
1574 idx=0;
1577 /* Let application handle this for virtual listview */
1578 if (infoPtr->dwStyle & LVS_OWNERDATA)
1580 NMLVFINDITEMW nmlv;
1581 LVFINDINFOW lvfi;
1583 ZeroMemory(&lvfi, sizeof(lvfi));
1584 lvfi.flags = (LVFI_WRAP | LVFI_PARTIAL);
1585 infoPtr->szSearchParam[infoPtr->nSearchParamLength] = '\0';
1586 lvfi.psz = infoPtr->szSearchParam;
1587 nmlv.iStart = idx;
1588 nmlv.lvfi = lvfi;
1590 nItem = notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
1592 if (nItem != -1)
1593 LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1595 return 0;
1598 do {
1599 if (idx == infoPtr->nItemCount) {
1600 if (endidx == infoPtr->nItemCount || endidx == 0)
1601 break;
1602 idx=0;
1605 /* get item */
1606 item.mask = LVIF_TEXT;
1607 item.iItem = idx;
1608 item.iSubItem = 0;
1609 item.pszText = buffer;
1610 item.cchTextMax = MAX_PATH;
1611 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1613 /* check for a match */
1614 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1615 nItem=idx;
1616 break;
1617 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1618 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1619 /* This would work but we must keep looking for a longer match */
1620 nItem=idx;
1622 idx++;
1623 } while (idx != endidx);
1625 if (nItem != -1)
1626 LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1628 return 0;
1631 /*************************************************************************
1632 * LISTVIEW_UpdateHeaderSize [Internal]
1634 * Function to resize the header control
1636 * PARAMS
1637 * [I] hwnd : handle to a window
1638 * [I] nNewScrollPos : scroll pos to set
1640 * RETURNS
1641 * None.
1643 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1645 RECT winRect;
1646 POINT point[2];
1648 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1650 GetWindowRect(infoPtr->hwndHeader, &winRect);
1651 point[0].x = winRect.left;
1652 point[0].y = winRect.top;
1653 point[1].x = winRect.right;
1654 point[1].y = winRect.bottom;
1656 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1657 point[0].x = -nNewScrollPos;
1658 point[1].x += nNewScrollPos;
1660 SetWindowPos(infoPtr->hwndHeader,0,
1661 point[0].x,point[0].y,point[1].x,point[1].y,
1662 (infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW |
1663 SWP_NOZORDER | SWP_NOACTIVATE);
1666 /***
1667 * DESCRIPTION:
1668 * Update the scrollbars. This functions should be called whenever
1669 * the content, size or view changes.
1671 * PARAMETER(S):
1672 * [I] infoPtr : valid pointer to the listview structure
1674 * RETURN:
1675 * None
1677 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *infoPtr)
1679 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1680 SCROLLINFO horzInfo, vertInfo;
1681 INT dx, dy;
1683 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1685 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1686 horzInfo.cbSize = sizeof(SCROLLINFO);
1687 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1689 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1690 if (uView == LVS_LIST)
1692 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1693 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1695 /* scroll by at least one column per page */
1696 if(horzInfo.nPage < infoPtr->nItemWidth)
1697 horzInfo.nPage = infoPtr->nItemWidth;
1699 horzInfo.nPage /= infoPtr->nItemWidth;
1701 else if (uView == LVS_REPORT)
1703 horzInfo.nMax = infoPtr->nItemWidth;
1705 else /* LVS_ICON, or LVS_SMALLICON */
1707 RECT rcView;
1709 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1712 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1713 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1714 dx = GetScrollPos(infoPtr->hwndSelf, SB_HORZ);
1715 dx -= SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1716 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1718 /* Setting the horizontal scroll can change the listview size
1719 * (and potentially everything else) so we need to recompute
1720 * everything again for the vertical scroll
1723 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1724 vertInfo.cbSize = sizeof(SCROLLINFO);
1725 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1727 if (uView == LVS_REPORT)
1729 vertInfo.nMax = infoPtr->nItemCount;
1731 /* scroll by at least one page */
1732 if(vertInfo.nPage < infoPtr->nItemHeight)
1733 vertInfo.nPage = infoPtr->nItemHeight;
1735 if (infoPtr->nItemHeight > 0)
1736 vertInfo.nPage /= infoPtr->nItemHeight;
1738 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1740 RECT rcView;
1742 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1745 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1746 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1747 dy = GetScrollPos(infoPtr->hwndSelf, SB_VERT);
1748 dy -= SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1749 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1751 /* Change of the range may have changed the scroll pos. If so move the content */
1752 if (dx != 0 || dy != 0)
1754 RECT listRect;
1755 listRect = infoPtr->rcList;
1756 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &listRect, &listRect, 0, 0,
1757 SW_ERASE | SW_INVALIDATE);
1760 /* Update the Header Control */
1761 if (uView == LVS_REPORT)
1763 horzInfo.fMask = SIF_POS;
1764 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1765 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1770 /***
1771 * DESCRIPTION:
1772 * Shows/hides the focus rectangle.
1774 * PARAMETER(S):
1775 * [I] infoPtr : valid pointer to the listview structure
1776 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1778 * RETURN:
1779 * None
1781 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO *infoPtr, BOOL fShow)
1783 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1784 HDC hdc;
1786 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1788 if (infoPtr->nFocusedItem < 0) return;
1790 /* we need some gymnastics in ICON mode to handle large items */
1791 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1793 RECT rcBox;
1795 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1796 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1798 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1799 return;
1803 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1805 /* for some reason, owner draw should work only in report mode */
1806 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1808 DRAWITEMSTRUCT dis;
1809 LVITEMW item;
1811 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1812 HFONT hOldFont = SelectObject(hdc, hFont);
1814 item.iItem = infoPtr->nFocusedItem;
1815 item.iSubItem = 0;
1816 item.mask = LVIF_PARAM;
1817 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1819 ZeroMemory(&dis, sizeof(dis));
1820 dis.CtlType = ODT_LISTVIEW;
1821 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1822 dis.itemID = item.iItem;
1823 dis.itemAction = ODA_FOCUS;
1824 if (fShow) dis.itemState |= ODS_FOCUS;
1825 dis.hwndItem = infoPtr->hwndSelf;
1826 dis.hDC = hdc;
1827 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1828 dis.itemData = item.lParam;
1830 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1832 SelectObject(hdc, hOldFont);
1834 else
1836 DrawFocusRect(hdc, &infoPtr->rcFocus);
1838 done:
1839 ReleaseDC(infoPtr->hwndSelf, hdc);
1842 /***
1843 * Invalidates all visible selected items.
1845 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO *infoPtr)
1847 ITERATOR i;
1849 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1850 while(iterator_next(&i))
1852 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1853 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1855 iterator_destroy(&i);
1859 /***
1860 * DESCRIPTION: [INTERNAL]
1861 * Computes an item's (left,top) corner, relative to rcView.
1862 * That is, the position has NOT been made relative to the Origin.
1863 * This is deliberate, to avoid computing the Origin over, and
1864 * over again, when this function is called in a loop. Instead,
1865 * one can factor the computation of the Origin before the loop,
1866 * and offset the value returned by this function, on every iteration.
1868 * PARAMETER(S):
1869 * [I] infoPtr : valid pointer to the listview structure
1870 * [I] nItem : item number
1871 * [O] lpptOrig : item top, left corner
1873 * RETURN:
1874 * None.
1876 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1878 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1880 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1882 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1884 lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1885 lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1887 else if (uView == LVS_LIST)
1889 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1890 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1891 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1893 else /* LVS_REPORT */
1895 lpptPosition->x = 0;
1896 lpptPosition->y = nItem * infoPtr->nItemHeight;
1900 /***
1901 * DESCRIPTION: [INTERNAL]
1902 * Compute the rectangles of an item. This is to localize all
1903 * the computations in one place. If you are not interested in some
1904 * of these values, simply pass in a NULL -- the function is smart
1905 * enough to compute only what's necessary. The function computes
1906 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1907 * one, the BOX rectangle. This rectangle is very cheap to compute,
1908 * and is guaranteed to contain all the other rectangles. Computing
1909 * the ICON rect is also cheap, but all the others are potentially
1910 * expensive. This gives an easy and effective optimization when
1911 * searching (like point inclusion, or rectangle intersection):
1912 * first test against the BOX, and if TRUE, test against the desired
1913 * rectangle.
1914 * If the function does not have all the necessary information
1915 * to computed the requested rectangles, will crash with a
1916 * failed assertion. This is done so we catch all programming
1917 * errors, given that the function is called only from our code.
1919 * We have the following 'special' meanings for a few fields:
1920 * * If LVIS_FOCUSED is set, we assume the item has the focus
1921 * This is important in ICON mode, where it might get a larger
1922 * then usual rectangle
1924 * Please note that subitem support works only in REPORT mode.
1926 * PARAMETER(S):
1927 * [I] infoPtr : valid pointer to the listview structure
1928 * [I] lpLVItem : item to compute the measures for
1929 * [O] lprcBox : ptr to Box rectangle
1930 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1931 * [0] lprcSelectBox : ptr to select box rectangle
1932 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
1933 * [O] lprcIcon : ptr to Icon rectangle
1934 * Same as LVM_GETITEMRECT with LVIR_ICON
1935 * [O] lprcStateIcon: ptr to State Icon rectangle
1936 * [O] lprcLabel : ptr to Label rectangle
1937 * Same as LVM_GETITEMRECT with LVIR_LABEL
1939 * RETURN:
1940 * None.
1942 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1943 LPRECT lprcBox, LPRECT lprcSelectBox,
1944 LPRECT lprcIcon, LPRECT lprcStateIcon, LPRECT lprcLabel)
1946 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1947 BOOL doSelectBox = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1948 RECT Box, SelectBox, Icon, Label;
1949 COLUMN_INFO *lpColumnInfo = NULL;
1950 SIZE labelSize = { 0, 0 };
1952 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1954 /* Be smart and try to figure out the minimum we have to do */
1955 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1956 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1958 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1959 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1961 if (lprcSelectBox) doSelectBox = TRUE;
1962 if (lprcLabel) doLabel = TRUE;
1963 if (doLabel || lprcIcon || lprcStateIcon) doIcon = TRUE;
1964 if (doSelectBox)
1966 doIcon = TRUE;
1967 doLabel = TRUE;
1970 /************************************************************/
1971 /* compute the box rectangle (it should be cheap to do) */
1972 /************************************************************/
1973 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1974 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1976 if (lpLVItem->iSubItem)
1978 Box = lpColumnInfo->rcHeader;
1980 else
1982 Box.left = 0;
1983 Box.right = infoPtr->nItemWidth;
1985 Box.top = 0;
1986 Box.bottom = infoPtr->nItemHeight;
1988 /******************************************************************/
1989 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
1990 /******************************************************************/
1991 if (doIcon)
1993 LONG state_width = 0;
1995 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1996 state_width = infoPtr->iconStateSize.cx;
1998 if (uView == LVS_ICON)
2000 Icon.left = Box.left + state_width;
2001 if (infoPtr->himlNormal)
2002 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx - state_width) / 2;
2003 Icon.top = Box.top + ICON_TOP_PADDING;
2004 Icon.right = Icon.left;
2005 Icon.bottom = Icon.top;
2006 if (infoPtr->himlNormal)
2008 Icon.right += infoPtr->iconSize.cx;
2009 Icon.bottom += infoPtr->iconSize.cy;
2012 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2014 Icon.left = Box.left + state_width;
2016 if (uView == LVS_REPORT)
2017 Icon.left += REPORT_MARGINX;
2019 Icon.top = Box.top;
2020 Icon.right = Icon.left;
2021 if (infoPtr->himlSmall &&
2022 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
2023 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
2024 Icon.right += infoPtr->iconSize.cx;
2025 Icon.bottom = Icon.top + infoPtr->iconSize.cy;
2027 if(lprcIcon) *lprcIcon = Icon;
2028 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon));
2030 /* TODO: is this correct? */
2031 if (lprcStateIcon)
2033 lprcStateIcon->left = Icon.left - state_width;
2034 lprcStateIcon->right = Icon.left;
2035 lprcStateIcon->top = Icon.top;
2036 lprcStateIcon->bottom = lprcStateIcon->top + infoPtr->iconSize.cy;
2037 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon));
2040 else Icon.right = 0;
2042 /************************************************************/
2043 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2044 /************************************************************/
2045 if (doLabel)
2047 /* calculate how far to the right can the label stretch */
2048 Label.right = Box.right;
2049 if (uView == LVS_REPORT)
2051 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
2054 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
2056 labelSize.cx = infoPtr->nItemWidth;
2057 labelSize.cy = infoPtr->nItemHeight;
2058 goto calc_label;
2061 /* we need the text in non owner draw mode */
2062 assert(lpLVItem->mask & LVIF_TEXT);
2063 if (is_textT(lpLVItem->pszText, TRUE))
2065 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2066 HDC hdc = GetDC(infoPtr->hwndSelf);
2067 HFONT hOldFont = SelectObject(hdc, hFont);
2068 UINT uFormat;
2069 RECT rcText;
2071 /* compute rough rectangle where the label will go */
2072 SetRectEmpty(&rcText);
2073 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2074 rcText.bottom = infoPtr->nItemHeight;
2075 if (uView == LVS_ICON)
2076 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2078 /* now figure out the flags */
2079 if (uView == LVS_ICON)
2080 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2081 else
2082 uFormat = LV_SL_DT_FLAGS;
2084 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2086 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2087 labelSize.cy = rcText.bottom - rcText.top;
2089 SelectObject(hdc, hOldFont);
2090 ReleaseDC(infoPtr->hwndSelf, hdc);
2093 calc_label:
2094 if (uView == LVS_ICON)
2096 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2097 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2098 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2099 Label.right = Label.left + labelSize.cx;
2100 Label.bottom = Label.top + infoPtr->nItemHeight;
2101 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2103 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2104 labelSize.cy /= infoPtr->ntmHeight;
2105 labelSize.cy = max(labelSize.cy, 1);
2106 labelSize.cy *= infoPtr->ntmHeight;
2108 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2110 else if (uView == LVS_REPORT)
2112 Label.left = Icon.right;
2113 Label.top = Box.top;
2114 Label.right = lpColumnInfo->rcHeader.right;
2115 Label.bottom = Label.top + infoPtr->nItemHeight;
2117 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2119 Label.left = Icon.right;
2120 Label.top = Box.top;
2121 Label.right = min(Label.left + labelSize.cx, Label.right);
2122 Label.bottom = Label.top + infoPtr->nItemHeight;
2125 if (lprcLabel) *lprcLabel = Label;
2126 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label));
2129 /************************************************************/
2130 /* compute STATEICON bounding box */
2131 /************************************************************/
2132 if (doSelectBox)
2134 if (uView == LVS_REPORT)
2136 SelectBox.left = Icon.right; /* FIXME: should be Icon.left */
2137 SelectBox.top = Box.top;
2138 SelectBox.bottom = Box.bottom;
2139 if (lpLVItem->iSubItem == 0)
2141 /* we need the indent in report mode */
2142 assert(lpLVItem->mask & LVIF_INDENT);
2143 SelectBox.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
2145 SelectBox.right = min(SelectBox.left + labelSize.cx, Label.right);
2147 else
2149 UnionRect(&SelectBox, &Icon, &Label);
2151 if (lprcSelectBox) *lprcSelectBox = SelectBox;
2152 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox));
2155 /* Fix the Box if necessary */
2156 if (lprcBox)
2158 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2159 else *lprcBox = Box;
2161 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box));
2164 /***
2165 * DESCRIPTION: [INTERNAL]
2167 * PARAMETER(S):
2168 * [I] infoPtr : valid pointer to the listview structure
2169 * [I] nItem : item number
2170 * [O] lprcBox : ptr to Box rectangle
2172 * RETURN:
2173 * None.
2175 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2177 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2178 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2179 POINT Position, Origin;
2180 LVITEMW lvItem;
2182 LISTVIEW_GetOrigin(infoPtr, &Origin);
2183 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2185 /* Be smart and try to figure out the minimum we have to do */
2186 lvItem.mask = 0;
2187 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2188 lvItem.mask |= LVIF_TEXT;
2189 lvItem.iItem = nItem;
2190 lvItem.iSubItem = 0;
2191 lvItem.pszText = szDispText;
2192 lvItem.cchTextMax = DISP_TEXT_SIZE;
2193 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2194 if (uView == LVS_ICON)
2196 lvItem.mask |= LVIF_STATE;
2197 lvItem.stateMask = LVIS_FOCUSED;
2198 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2200 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0, 0);
2202 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2206 /***
2207 * DESCRIPTION:
2208 * Returns the current icon position, and advances it along the top.
2209 * The returned position is not offset by Origin.
2211 * PARAMETER(S):
2212 * [I] infoPtr : valid pointer to the listview structure
2213 * [O] lpPos : will get the current icon position
2215 * RETURN:
2216 * None
2218 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2220 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2222 *lpPos = infoPtr->currIconPos;
2224 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2225 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2227 infoPtr->currIconPos.x = 0;
2228 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2232 /***
2233 * DESCRIPTION:
2234 * Returns the current icon position, and advances it down the left edge.
2235 * The returned position is not offset by Origin.
2237 * PARAMETER(S):
2238 * [I] infoPtr : valid pointer to the listview structure
2239 * [O] lpPos : will get the current icon position
2241 * RETURN:
2242 * None
2244 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2246 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2248 *lpPos = infoPtr->currIconPos;
2250 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2251 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2253 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2254 infoPtr->currIconPos.y = 0;
2258 /***
2259 * DESCRIPTION:
2260 * Moves an icon to the specified position.
2261 * It takes care of invalidating the item, etc.
2263 * PARAMETER(S):
2264 * [I] infoPtr : valid pointer to the listview structure
2265 * [I] nItem : the item to move
2266 * [I] lpPos : the new icon position
2267 * [I] isNew : flags the item as being new
2269 * RETURN:
2270 * Success: TRUE
2271 * Failure: FALSE
2273 static BOOL LISTVIEW_MoveIconTo(const LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2275 POINT old;
2277 if (!isNew)
2279 old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2280 old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2282 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2283 LISTVIEW_InvalidateItem(infoPtr, nItem);
2286 /* Allocating a POINTER for every item is too resource intensive,
2287 * so we'll keep the (x,y) in different arrays */
2288 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2289 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2291 LISTVIEW_InvalidateItem(infoPtr, nItem);
2293 return TRUE;
2296 /***
2297 * DESCRIPTION:
2298 * Arranges listview items in icon display mode.
2300 * PARAMETER(S):
2301 * [I] infoPtr : valid pointer to the listview structure
2302 * [I] nAlignCode : alignment code
2304 * RETURN:
2305 * SUCCESS : TRUE
2306 * FAILURE : FALSE
2308 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2310 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2311 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2312 POINT pos;
2313 INT i;
2315 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2317 TRACE("nAlignCode=%d\n", nAlignCode);
2319 if (nAlignCode == LVA_DEFAULT)
2321 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2322 else nAlignCode = LVA_ALIGNTOP;
2325 switch (nAlignCode)
2327 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2328 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2329 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2330 default: return FALSE;
2333 infoPtr->bAutoarrange = TRUE;
2334 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2335 for (i = 0; i < infoPtr->nItemCount; i++)
2337 next_pos(infoPtr, &pos);
2338 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2341 return TRUE;
2344 /***
2345 * DESCRIPTION:
2346 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2348 * PARAMETER(S):
2349 * [I] infoPtr : valid pointer to the listview structure
2350 * [O] lprcView : bounding rectangle
2352 * RETURN:
2353 * SUCCESS : TRUE
2354 * FAILURE : FALSE
2356 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2358 INT i, x, y;
2360 SetRectEmpty(lprcView);
2362 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2364 case LVS_ICON:
2365 case LVS_SMALLICON:
2366 for (i = 0; i < infoPtr->nItemCount; i++)
2368 x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2369 y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2370 lprcView->right = max(lprcView->right, x);
2371 lprcView->bottom = max(lprcView->bottom, y);
2373 if (infoPtr->nItemCount > 0)
2375 lprcView->right += infoPtr->nItemWidth;
2376 lprcView->bottom += infoPtr->nItemHeight;
2378 break;
2380 case LVS_LIST:
2381 y = LISTVIEW_GetCountPerColumn(infoPtr);
2382 x = infoPtr->nItemCount / y;
2383 if (infoPtr->nItemCount % y) x++;
2384 lprcView->right = x * infoPtr->nItemWidth;
2385 lprcView->bottom = y * infoPtr->nItemHeight;
2386 break;
2388 case LVS_REPORT:
2389 lprcView->right = infoPtr->nItemWidth;
2390 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2391 break;
2395 /***
2396 * DESCRIPTION:
2397 * Retrieves the bounding rectangle of all the items.
2399 * PARAMETER(S):
2400 * [I] infoPtr : valid pointer to the listview structure
2401 * [O] lprcView : bounding rectangle
2403 * RETURN:
2404 * SUCCESS : TRUE
2405 * FAILURE : FALSE
2407 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2409 POINT ptOrigin;
2411 TRACE("(lprcView=%p)\n", lprcView);
2413 if (!lprcView) return FALSE;
2415 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2416 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2417 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2419 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView));
2421 return TRUE;
2424 /***
2425 * DESCRIPTION:
2426 * Retrieves the subitem pointer associated with the subitem index.
2428 * PARAMETER(S):
2429 * [I] hdpaSubItems : DPA handle for a specific item
2430 * [I] nSubItem : index of subitem
2432 * RETURN:
2433 * SUCCESS : subitem pointer
2434 * FAILURE : NULL
2436 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2438 SUBITEM_INFO *lpSubItem;
2439 INT i;
2441 /* we should binary search here if need be */
2442 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2444 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
2445 if (lpSubItem->iSubItem == nSubItem)
2446 return lpSubItem;
2449 return NULL;
2453 /***
2454 * DESCRIPTION:
2455 * Calculates the desired item width.
2457 * PARAMETER(S):
2458 * [I] infoPtr : valid pointer to the listview structure
2460 * RETURN:
2461 * The desired item width.
2463 static INT LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO *infoPtr)
2465 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2466 INT nItemWidth = 0;
2468 TRACE("uView=%d\n", uView);
2470 if (uView == LVS_ICON)
2471 nItemWidth = infoPtr->iconSpacing.cx;
2472 else if (uView == LVS_REPORT)
2474 RECT rcHeader;
2476 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2478 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2479 nItemWidth = rcHeader.right;
2482 else /* LVS_SMALLICON, or LVS_LIST */
2484 INT i;
2486 for (i = 0; i < infoPtr->nItemCount; i++)
2487 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2489 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2490 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2492 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2495 return max(nItemWidth, 1);
2498 /***
2499 * DESCRIPTION:
2500 * Calculates the desired item height.
2502 * PARAMETER(S):
2503 * [I] infoPtr : valid pointer to the listview structure
2505 * RETURN:
2506 * The desired item height.
2508 static INT LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO *infoPtr)
2510 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2511 INT nItemHeight;
2513 TRACE("uView=%d\n", uView);
2515 if (uView == LVS_ICON)
2516 nItemHeight = infoPtr->iconSpacing.cy;
2517 else
2519 nItemHeight = infoPtr->ntmHeight;
2520 if (uView == LVS_REPORT && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
2521 nItemHeight++;
2522 if (infoPtr->himlState)
2523 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2524 if (infoPtr->himlSmall)
2525 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2526 if (infoPtr->himlState || infoPtr->himlSmall)
2527 nItemHeight += HEIGHT_PADDING;
2528 if (infoPtr->nMeasureItemHeight > 0)
2529 nItemHeight = infoPtr->nMeasureItemHeight;
2532 return max(nItemHeight, 1);
2535 /***
2536 * DESCRIPTION:
2537 * Updates the width, and height of an item.
2539 * PARAMETER(S):
2540 * [I] infoPtr : valid pointer to the listview structure
2542 * RETURN:
2543 * None.
2545 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2547 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2548 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2552 /***
2553 * DESCRIPTION:
2554 * Retrieves and saves important text metrics info for the current
2555 * Listview font.
2557 * PARAMETER(S):
2558 * [I] infoPtr : valid pointer to the listview structure
2561 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2563 HDC hdc = GetDC(infoPtr->hwndSelf);
2564 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2565 HFONT hOldFont = SelectObject(hdc, hFont);
2566 TEXTMETRICW tm;
2567 SIZE sz;
2569 if (GetTextMetricsW(hdc, &tm))
2571 infoPtr->ntmHeight = tm.tmHeight;
2572 infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
2575 if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
2576 infoPtr->nEllipsisWidth = sz.cx;
2578 SelectObject(hdc, hOldFont);
2579 ReleaseDC(infoPtr->hwndSelf, hdc);
2581 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2584 /***
2585 * DESCRIPTION:
2586 * A compare function for ranges
2588 * PARAMETER(S)
2589 * [I] range1 : pointer to range 1;
2590 * [I] range2 : pointer to range 2;
2591 * [I] flags : flags
2593 * RETURNS:
2594 * > 0 : if range 1 > range 2
2595 * < 0 : if range 2 > range 1
2596 * = 0 : if range intersects range 2
2598 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2600 INT cmp;
2602 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2603 cmp = -1;
2604 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2605 cmp = 1;
2606 else
2607 cmp = 0;
2609 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1), debugrange(range2), cmp);
2611 return cmp;
2614 #if DEBUG_RANGES
2615 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2616 #else
2617 #define ranges_check(ranges, desc) do { } while(0)
2618 #endif
2620 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2622 INT i;
2623 RANGE *prev, *curr;
2625 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2626 assert (ranges);
2627 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2628 ranges_dump(ranges);
2629 prev = DPA_GetPtr(ranges->hdpa, 0);
2630 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2631 assert (prev->lower >= 0 && prev->lower < prev->upper);
2632 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2634 curr = DPA_GetPtr(ranges->hdpa, i);
2635 assert (prev->upper <= curr->lower);
2636 assert (curr->lower < curr->upper);
2637 prev = curr;
2639 TRACE("--- Done checking---\n");
2642 static RANGES ranges_create(int count)
2644 RANGES ranges = Alloc(sizeof(struct tagRANGES));
2645 if (!ranges) return NULL;
2646 ranges->hdpa = DPA_Create(count);
2647 if (ranges->hdpa) return ranges;
2648 Free(ranges);
2649 return NULL;
2652 static void ranges_clear(RANGES ranges)
2654 INT i;
2656 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2657 Free(DPA_GetPtr(ranges->hdpa, i));
2658 DPA_DeleteAllPtrs(ranges->hdpa);
2662 static void ranges_destroy(RANGES ranges)
2664 if (!ranges) return;
2665 ranges_clear(ranges);
2666 DPA_Destroy(ranges->hdpa);
2667 Free(ranges);
2670 static RANGES ranges_clone(RANGES ranges)
2672 RANGES clone;
2673 INT i;
2675 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2677 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2679 RANGE *newrng = Alloc(sizeof(RANGE));
2680 if (!newrng) goto fail;
2681 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2682 DPA_SetPtr(clone->hdpa, i, newrng);
2684 return clone;
2686 fail:
2687 TRACE ("clone failed\n");
2688 ranges_destroy(clone);
2689 return NULL;
2692 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2694 INT i;
2696 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2697 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2699 return ranges;
2702 static void ranges_dump(RANGES ranges)
2704 INT i;
2706 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2707 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2710 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2712 RANGE srchrng = { nItem, nItem + 1 };
2714 TRACE("(nItem=%d)\n", nItem);
2715 ranges_check(ranges, "before contain");
2716 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2719 static INT ranges_itemcount(RANGES ranges)
2721 INT i, count = 0;
2723 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2725 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2726 count += sel->upper - sel->lower;
2729 return count;
2732 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2734 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2735 INT index;
2737 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2738 if (index == -1) return TRUE;
2740 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2742 chkrng = DPA_GetPtr(ranges->hdpa, index);
2743 if (chkrng->lower >= nItem)
2744 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2745 if (chkrng->upper > nItem)
2746 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2748 return TRUE;
2751 static BOOL ranges_add(RANGES ranges, RANGE range)
2753 RANGE srchrgn;
2754 INT index;
2756 TRACE("(%s)\n", debugrange(&range));
2757 ranges_check(ranges, "before add");
2759 /* try find overlapping regions first */
2760 srchrgn.lower = range.lower - 1;
2761 srchrgn.upper = range.upper + 1;
2762 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2764 if (index == -1)
2766 RANGE *newrgn;
2768 TRACE("Adding new range\n");
2770 /* create the brand new range to insert */
2771 newrgn = Alloc(sizeof(RANGE));
2772 if(!newrgn) goto fail;
2773 *newrgn = range;
2775 /* figure out where to insert it */
2776 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2777 TRACE("index=%d\n", index);
2778 if (index == -1) index = 0;
2780 /* and get it over with */
2781 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2783 Free(newrgn);
2784 goto fail;
2787 else
2789 RANGE *chkrgn, *mrgrgn;
2790 INT fromindex, mergeindex;
2792 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2793 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2795 chkrgn->lower = min(range.lower, chkrgn->lower);
2796 chkrgn->upper = max(range.upper, chkrgn->upper);
2798 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2800 /* merge now common ranges */
2801 fromindex = 0;
2802 srchrgn.lower = chkrgn->lower - 1;
2803 srchrgn.upper = chkrgn->upper + 1;
2807 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2808 if (mergeindex == -1) break;
2809 if (mergeindex == index)
2811 fromindex = index + 1;
2812 continue;
2815 TRACE("Merge with index %i\n", mergeindex);
2817 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2818 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2819 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2820 Free(mrgrgn);
2821 DPA_DeletePtr(ranges->hdpa, mergeindex);
2822 if (mergeindex < index) index --;
2823 } while(1);
2826 ranges_check(ranges, "after add");
2827 return TRUE;
2829 fail:
2830 ranges_check(ranges, "failed add");
2831 return FALSE;
2834 static BOOL ranges_del(RANGES ranges, RANGE range)
2836 RANGE *chkrgn;
2837 INT index;
2839 TRACE("(%s)\n", debugrange(&range));
2840 ranges_check(ranges, "before del");
2842 /* we don't use DPAS_SORTED here, since we need *
2843 * to find the first overlapping range */
2844 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2845 while(index != -1)
2847 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2849 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2851 /* case 1: Same range */
2852 if ( (chkrgn->upper == range.upper) &&
2853 (chkrgn->lower == range.lower) )
2855 DPA_DeletePtr(ranges->hdpa, index);
2856 break;
2858 /* case 2: engulf */
2859 else if ( (chkrgn->upper <= range.upper) &&
2860 (chkrgn->lower >= range.lower) )
2862 DPA_DeletePtr(ranges->hdpa, index);
2864 /* case 3: overlap upper */
2865 else if ( (chkrgn->upper <= range.upper) &&
2866 (chkrgn->lower < range.lower) )
2868 chkrgn->upper = range.lower;
2870 /* case 4: overlap lower */
2871 else if ( (chkrgn->upper > range.upper) &&
2872 (chkrgn->lower >= range.lower) )
2874 chkrgn->lower = range.upper;
2875 break;
2877 /* case 5: fully internal */
2878 else
2880 RANGE tmprgn = *chkrgn, *newrgn;
2882 if (!(newrgn = Alloc(sizeof(RANGE)))) goto fail;
2883 newrgn->lower = chkrgn->lower;
2884 newrgn->upper = range.lower;
2885 chkrgn->lower = range.upper;
2886 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2888 Free(newrgn);
2889 goto fail;
2891 chkrgn = &tmprgn;
2892 break;
2895 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2898 ranges_check(ranges, "after del");
2899 return TRUE;
2901 fail:
2902 ranges_check(ranges, "failed del");
2903 return FALSE;
2906 /***
2907 * DESCRIPTION:
2908 * Removes all selection ranges
2910 * Parameters(s):
2911 * [I] infoPtr : valid pointer to the listview structure
2912 * [I] toSkip : item range to skip removing the selection
2914 * RETURNS:
2915 * SUCCESS : TRUE
2916 * FAILURE : TRUE
2918 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2920 LVITEMW lvItem;
2921 ITERATOR i;
2922 RANGES clone;
2924 TRACE("()\n");
2926 lvItem.state = 0;
2927 lvItem.stateMask = LVIS_SELECTED;
2929 /* need to clone the DPA because callbacks can change it */
2930 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2931 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2932 while(iterator_next(&i))
2933 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2934 /* note that the iterator destructor will free the cloned range */
2935 iterator_destroy(&i);
2937 return TRUE;
2940 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2942 RANGES toSkip;
2944 if (!(toSkip = ranges_create(1))) return FALSE;
2945 if (nItem != -1) ranges_additem(toSkip, nItem);
2946 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2947 ranges_destroy(toSkip);
2948 return TRUE;
2951 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2953 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2956 /***
2957 * DESCRIPTION:
2958 * Retrieves the number of items that are marked as selected.
2960 * PARAMETER(S):
2961 * [I] infoPtr : valid pointer to the listview structure
2963 * RETURN:
2964 * Number of items selected.
2966 static INT LISTVIEW_GetSelectedCount(const LISTVIEW_INFO *infoPtr)
2968 INT nSelectedCount = 0;
2970 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2972 INT i;
2973 for (i = 0; i < infoPtr->nItemCount; i++)
2975 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2976 nSelectedCount++;
2979 else
2980 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2982 TRACE("nSelectedCount=%d\n", nSelectedCount);
2983 return nSelectedCount;
2986 /***
2987 * DESCRIPTION:
2988 * Manages the item focus.
2990 * PARAMETER(S):
2991 * [I] infoPtr : valid pointer to the listview structure
2992 * [I] nItem : item index
2994 * RETURN:
2995 * TRUE : focused item changed
2996 * FALSE : focused item has NOT changed
2998 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
3000 INT oldFocus = infoPtr->nFocusedItem;
3001 LVITEMW lvItem;
3003 if (nItem == infoPtr->nFocusedItem) return FALSE;
3005 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
3006 lvItem.stateMask = LVIS_FOCUSED;
3007 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
3009 return oldFocus != infoPtr->nFocusedItem;
3012 /* Helper function for LISTVIEW_ShiftIndices *only* */
3013 static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
3015 if (nShiftItem < nItem) return nShiftItem;
3017 if (nShiftItem > nItem) return nShiftItem + direction;
3019 if (direction > 0) return nShiftItem + direction;
3021 return min(nShiftItem, infoPtr->nItemCount - 1);
3025 * DESCRIPTION:
3026 * Updates the various indices after an item has been inserted or deleted.
3028 * PARAMETER(S):
3029 * [I] infoPtr : valid pointer to the listview structure
3030 * [I] nItem : item index
3031 * [I] direction : Direction of shift, +1 or -1.
3033 * RETURN:
3034 * None
3036 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
3038 INT nNewFocus;
3039 BOOL bOldChange;
3041 /* temporarily disable change notification while shifting items */
3042 bOldChange = infoPtr->bDoChangeNotify;
3043 infoPtr->bDoChangeNotify = FALSE;
3045 TRACE("Shifting %iu, %i steps\n", nItem, direction);
3047 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
3049 assert(abs(direction) == 1);
3051 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
3053 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
3054 if (nNewFocus != infoPtr->nFocusedItem)
3055 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
3057 /* But we are not supposed to modify nHotItem! */
3059 infoPtr->bDoChangeNotify = bOldChange;
3064 * DESCRIPTION:
3065 * Adds a block of selections.
3067 * PARAMETER(S):
3068 * [I] infoPtr : valid pointer to the listview structure
3069 * [I] nItem : item index
3071 * RETURN:
3072 * Whether the window is still valid.
3074 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3076 INT nFirst = min(infoPtr->nSelectionMark, nItem);
3077 INT nLast = max(infoPtr->nSelectionMark, nItem);
3078 HWND hwndSelf = infoPtr->hwndSelf;
3079 NMLVODSTATECHANGE nmlv;
3080 LVITEMW item;
3081 BOOL bOldChange;
3082 INT i;
3084 /* Temporarily disable change notification
3085 * If the control is LVS_OWNERDATA, we need to send
3086 * only one LVN_ODSTATECHANGED notification.
3087 * See MSDN documentation for LVN_ITEMCHANGED.
3089 bOldChange = infoPtr->bDoChangeNotify;
3090 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3092 if (nFirst == -1) nFirst = nItem;
3094 item.state = LVIS_SELECTED;
3095 item.stateMask = LVIS_SELECTED;
3097 for (i = nFirst; i <= nLast; i++)
3098 LISTVIEW_SetItemState(infoPtr,i,&item);
3100 ZeroMemory(&nmlv, sizeof(nmlv));
3101 nmlv.iFrom = nFirst;
3102 nmlv.iTo = nLast;
3103 nmlv.uNewState = 0;
3104 nmlv.uOldState = item.state;
3106 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3107 if (!IsWindow(hwndSelf))
3108 return FALSE;
3109 infoPtr->bDoChangeNotify = bOldChange;
3110 return TRUE;
3114 /***
3115 * DESCRIPTION:
3116 * Sets a single group selection.
3118 * PARAMETER(S):
3119 * [I] infoPtr : valid pointer to the listview structure
3120 * [I] nItem : item index
3122 * RETURN:
3123 * None
3125 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3127 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3128 RANGES selection;
3129 LVITEMW item;
3130 ITERATOR i;
3131 BOOL bOldChange;
3133 if (!(selection = ranges_create(100))) return;
3135 item.state = LVIS_SELECTED;
3136 item.stateMask = LVIS_SELECTED;
3138 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3140 if (infoPtr->nSelectionMark == -1)
3142 infoPtr->nSelectionMark = nItem;
3143 ranges_additem(selection, nItem);
3145 else
3147 RANGE sel;
3149 sel.lower = min(infoPtr->nSelectionMark, nItem);
3150 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3151 ranges_add(selection, sel);
3154 else
3156 RECT rcItem, rcSel, rcSelMark;
3157 POINT ptItem;
3159 rcItem.left = LVIR_BOUNDS;
3160 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3161 rcSelMark.left = LVIR_BOUNDS;
3162 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3163 UnionRect(&rcSel, &rcItem, &rcSelMark);
3164 iterator_frameditems(&i, infoPtr, &rcSel);
3165 while(iterator_next(&i))
3167 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3168 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3170 iterator_destroy(&i);
3173 /* disable per item notifications on LVS_OWNERDATA style
3174 FIXME: single LVN_ODSTATECHANGED should be used */
3175 bOldChange = infoPtr->bDoChangeNotify;
3176 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3178 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3181 iterator_rangesitems(&i, selection);
3182 while(iterator_next(&i))
3183 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3184 /* this will also destroy the selection */
3185 iterator_destroy(&i);
3187 infoPtr->bDoChangeNotify = bOldChange;
3189 LISTVIEW_SetItemFocus(infoPtr, nItem);
3192 /***
3193 * DESCRIPTION:
3194 * Sets a single selection.
3196 * PARAMETER(S):
3197 * [I] infoPtr : valid pointer to the listview structure
3198 * [I] nItem : item index
3200 * RETURN:
3201 * None
3203 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3205 LVITEMW lvItem;
3207 TRACE("nItem=%d\n", nItem);
3209 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3211 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3212 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3213 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3215 infoPtr->nSelectionMark = nItem;
3218 /***
3219 * DESCRIPTION:
3220 * Set selection(s) with keyboard.
3222 * PARAMETER(S):
3223 * [I] infoPtr : valid pointer to the listview structure
3224 * [I] nItem : item index
3225 * [I] space : VK_SPACE code sent
3227 * RETURN:
3228 * SUCCESS : TRUE (needs to be repainted)
3229 * FAILURE : FALSE (nothing has changed)
3231 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem, BOOL space)
3233 /* FIXME: pass in the state */
3234 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3235 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3236 BOOL bResult = FALSE;
3238 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3239 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3241 if (infoPtr->dwStyle & LVS_SINGLESEL)
3243 bResult = TRUE;
3244 LISTVIEW_SetSelection(infoPtr, nItem);
3246 else
3248 if (wShift)
3250 bResult = TRUE;
3251 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3253 else if (wCtrl)
3255 LVITEMW lvItem;
3256 lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3257 lvItem.stateMask = LVIS_SELECTED;
3258 if (space)
3260 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3261 if (lvItem.state & LVIS_SELECTED)
3262 infoPtr->nSelectionMark = nItem;
3264 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3266 else
3268 bResult = TRUE;
3269 LISTVIEW_SetSelection(infoPtr, nItem);
3272 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3275 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3276 return bResult;
3279 static BOOL LISTVIEW_GetItemAtPt(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3281 LVHITTESTINFO lvHitTestInfo;
3283 ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3284 lvHitTestInfo.pt.x = pt.x;
3285 lvHitTestInfo.pt.y = pt.y;
3287 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3289 lpLVItem->mask = LVIF_PARAM;
3290 lpLVItem->iItem = lvHitTestInfo.iItem;
3291 lpLVItem->iSubItem = 0;
3293 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3296 static inline BOOL LISTVIEW_isHotTracking(const LISTVIEW_INFO *infoPtr)
3298 return ((infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) ||
3299 (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE) ||
3300 (infoPtr->dwLvExStyle & LVS_EX_TWOCLICKACTIVATE));
3303 /***
3304 * DESCRIPTION:
3305 * Called when the mouse is being actively tracked and has hovered for a specified
3306 * amount of time
3308 * PARAMETER(S):
3309 * [I] infoPtr : valid pointer to the listview structure
3310 * [I] fwKeys : key indicator
3311 * [I] x,y : mouse position
3313 * RETURN:
3314 * 0 if the message was processed, non-zero if there was an error
3316 * INFO:
3317 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3318 * over the item for a certain period of time.
3321 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3323 if (LISTVIEW_isHotTracking(infoPtr))
3325 LVITEMW item;
3326 POINT pt;
3328 pt.x = x;
3329 pt.y = y;
3331 if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3332 LISTVIEW_SetSelection(infoPtr, item.iItem);
3335 return 0;
3338 /***
3339 * DESCRIPTION:
3340 * Called whenever WM_MOUSEMOVE is received.
3342 * PARAMETER(S):
3343 * [I] infoPtr : valid pointer to the listview structure
3344 * [I] fwKeys : key indicator
3345 * [I] x,y : mouse position
3347 * RETURN:
3348 * 0 if the message is processed, non-zero if there was an error
3350 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3352 TRACKMOUSEEVENT trackinfo;
3354 if (!(fwKeys & MK_LBUTTON))
3355 infoPtr->bLButtonDown = FALSE;
3357 if (infoPtr->bLButtonDown)
3359 POINT tmp;
3360 RECT rect;
3361 WORD wDragWidth = GetSystemMetrics(SM_CXDRAG);
3362 WORD wDragHeight= GetSystemMetrics(SM_CYDRAG);
3364 rect.left = infoPtr->ptClickPos.x - wDragWidth;
3365 rect.right = infoPtr->ptClickPos.x + wDragWidth;
3366 rect.top = infoPtr->ptClickPos.y - wDragHeight;
3367 rect.bottom = infoPtr->ptClickPos.y + wDragHeight;
3369 tmp.x = x;
3370 tmp.y = y;
3372 if (!PtInRect(&rect, tmp))
3374 LVHITTESTINFO lvHitTestInfo;
3375 NMLISTVIEW nmlv;
3377 lvHitTestInfo.pt = infoPtr->ptClickPos;
3378 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3380 ZeroMemory(&nmlv, sizeof(nmlv));
3381 nmlv.iItem = lvHitTestInfo.iItem;
3382 nmlv.ptAction = infoPtr->ptClickPos;
3384 if (!infoPtr->bDragging)
3386 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3387 infoPtr->bDragging = TRUE;
3390 return 0;
3393 else
3394 infoPtr->bLButtonDown = FALSE;
3396 /* see if we are supposed to be tracking mouse hovering */
3397 if (LISTVIEW_isHotTracking(infoPtr)) {
3398 /* fill in the trackinfo struct */
3399 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3400 trackinfo.dwFlags = TME_QUERY;
3401 trackinfo.hwndTrack = infoPtr->hwndSelf;
3402 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3404 /* see if we are already tracking this hwnd */
3405 _TrackMouseEvent(&trackinfo);
3407 if(!(trackinfo.dwFlags & TME_HOVER)) {
3408 trackinfo.dwFlags = TME_HOVER;
3410 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3411 _TrackMouseEvent(&trackinfo);
3415 return 0;
3419 /***
3420 * Tests whether the item is assignable to a list with style lStyle
3422 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3424 if ( (lpLVItem->mask & LVIF_TEXT) &&
3425 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3426 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3428 return TRUE;
3432 /***
3433 * DESCRIPTION:
3434 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3436 * PARAMETER(S):
3437 * [I] infoPtr : valid pointer to the listview structure
3438 * [I] lpLVItem : valid pointer to new item attributes
3439 * [I] isNew : the item being set is being inserted
3440 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3441 * [O] bChanged : will be set to TRUE if the item really changed
3443 * RETURN:
3444 * SUCCESS : TRUE
3445 * FAILURE : FALSE
3447 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3449 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3450 ITEM_INFO *lpItem;
3451 NMLISTVIEW nmlv;
3452 UINT uChanged = 0;
3453 LVITEMW item;
3455 TRACE("()\n");
3457 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3459 if (lpLVItem->mask == 0) return TRUE;
3461 if (infoPtr->dwStyle & LVS_OWNERDATA)
3463 /* a virtual listview only stores selection and focus */
3464 if (lpLVItem->mask & ~LVIF_STATE)
3465 return FALSE;
3466 lpItem = NULL;
3468 else
3470 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3471 lpItem = DPA_GetPtr(hdpaSubItems, 0);
3472 assert (lpItem);
3475 /* we need to get the lParam and state of the item */
3476 item.iItem = lpLVItem->iItem;
3477 item.iSubItem = lpLVItem->iSubItem;
3478 item.mask = LVIF_STATE | LVIF_PARAM;
3479 item.stateMask = ~0;
3480 item.state = 0;
3481 item.lParam = 0;
3482 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3484 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3485 /* determine what fields will change */
3486 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3487 uChanged |= LVIF_STATE;
3489 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3490 uChanged |= LVIF_IMAGE;
3492 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3493 uChanged |= LVIF_PARAM;
3495 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3496 uChanged |= LVIF_INDENT;
3498 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3499 uChanged |= LVIF_TEXT;
3501 TRACE("uChanged=0x%x\n", uChanged);
3502 if (!uChanged) return TRUE;
3503 *bChanged = TRUE;
3505 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3506 nmlv.iItem = lpLVItem->iItem;
3507 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3508 nmlv.uOldState = item.state;
3509 nmlv.uChanged = uChanged;
3510 nmlv.lParam = item.lParam;
3512 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3513 /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
3514 /* are enabled */
3515 if(lpItem && !isNew && infoPtr->bDoChangeNotify)
3517 HWND hwndSelf = infoPtr->hwndSelf;
3519 if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3520 return FALSE;
3521 if (!IsWindow(hwndSelf))
3522 return FALSE;
3525 /* copy information */
3526 if (lpLVItem->mask & LVIF_TEXT)
3527 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3529 if (lpLVItem->mask & LVIF_IMAGE)
3530 lpItem->hdr.iImage = lpLVItem->iImage;
3532 if (lpLVItem->mask & LVIF_PARAM)
3533 lpItem->lParam = lpLVItem->lParam;
3535 if (lpLVItem->mask & LVIF_INDENT)
3536 lpItem->iIndent = lpLVItem->iIndent;
3538 if (uChanged & LVIF_STATE)
3540 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3542 lpItem->state &= ~lpLVItem->stateMask;
3543 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3545 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3547 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3548 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3550 else if (lpLVItem->stateMask & LVIS_SELECTED)
3551 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3553 /* if we are asked to change focus, and we manage it, do it */
3554 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3556 if (lpLVItem->state & LVIS_FOCUSED)
3558 LISTVIEW_SetItemFocus(infoPtr, -1);
3559 infoPtr->nFocusedItem = lpLVItem->iItem;
3560 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3562 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3563 infoPtr->nFocusedItem = -1;
3567 /* if we're inserting the item, we're done */
3568 if (isNew) return TRUE;
3570 /* send LVN_ITEMCHANGED notification */
3571 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3572 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3574 return TRUE;
3577 /***
3578 * DESCRIPTION:
3579 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3581 * PARAMETER(S):
3582 * [I] infoPtr : valid pointer to the listview structure
3583 * [I] lpLVItem : valid pointer to new subitem attributes
3584 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3585 * [O] bChanged : will be set to TRUE if the item really changed
3587 * RETURN:
3588 * SUCCESS : TRUE
3589 * FAILURE : FALSE
3591 static BOOL set_sub_item(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3593 HDPA hdpaSubItems;
3594 SUBITEM_INFO *lpSubItem;
3596 /* we do not support subitems for virtual listviews */
3597 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3599 /* set subitem only if column is present */
3600 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3602 /* First do some sanity checks */
3603 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
3604 particularly useful. We currently do not actually do anything with
3605 the flag on subitems.
3607 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)) return FALSE;
3608 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))) return TRUE;
3610 /* get the subitem structure, and create it if not there */
3611 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3612 assert (hdpaSubItems);
3614 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3615 if (!lpSubItem)
3617 SUBITEM_INFO *tmpSubItem;
3618 INT i;
3620 lpSubItem = Alloc(sizeof(SUBITEM_INFO));
3621 if (!lpSubItem) return FALSE;
3622 /* we could binary search here, if need be...*/
3623 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3625 tmpSubItem = DPA_GetPtr(hdpaSubItems, i);
3626 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3628 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3630 Free(lpSubItem);
3631 return FALSE;
3633 lpSubItem->iSubItem = lpLVItem->iSubItem;
3634 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3635 *bChanged = TRUE;
3638 if (lpLVItem->mask & LVIF_IMAGE)
3639 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3641 lpSubItem->hdr.iImage = lpLVItem->iImage;
3642 *bChanged = TRUE;
3645 if (lpLVItem->mask & LVIF_TEXT)
3646 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3648 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3649 *bChanged = TRUE;
3652 return TRUE;
3655 /***
3656 * DESCRIPTION:
3657 * Sets item attributes.
3659 * PARAMETER(S):
3660 * [I] infoPtr : valid pointer to the listview structure
3661 * [I] lpLVItem : new item attributes
3662 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3664 * RETURN:
3665 * SUCCESS : TRUE
3666 * FAILURE : FALSE
3668 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem, BOOL isW)
3670 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3671 HWND hwndSelf = infoPtr->hwndSelf;
3672 LPWSTR pszText = NULL;
3673 BOOL bResult, bChanged = FALSE;
3675 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3677 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3678 return FALSE;
3680 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3681 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3683 pszText = lpLVItem->pszText;
3684 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
3687 /* actually set the fields */
3688 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3690 if (lpLVItem->iSubItem)
3691 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3692 else
3693 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3694 if (!IsWindow(hwndSelf))
3695 return FALSE;
3697 /* redraw item, if necessary */
3698 if (bChanged && !infoPtr->bIsDrawing)
3700 /* this little optimization eliminates some nasty flicker */
3701 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3702 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
3703 lpLVItem->iSubItem > 0 && lpLVItem->iSubItem <= DPA_GetPtrCount(infoPtr->hdpaColumns) )
3704 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3705 else
3706 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3708 /* restore text */
3709 if (pszText)
3711 textfreeT(lpLVItem->pszText, isW);
3712 lpLVItem->pszText = pszText;
3715 return bResult;
3718 /***
3719 * DESCRIPTION:
3720 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3722 * PARAMETER(S):
3723 * [I] infoPtr : valid pointer to the listview structure
3725 * RETURN:
3726 * item index
3728 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *infoPtr)
3730 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3731 INT nItem = 0;
3732 SCROLLINFO scrollInfo;
3734 scrollInfo.cbSize = sizeof(SCROLLINFO);
3735 scrollInfo.fMask = SIF_POS;
3737 if (uView == LVS_LIST)
3739 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3740 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3742 else if (uView == LVS_REPORT)
3744 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3745 nItem = scrollInfo.nPos;
3747 else
3749 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3750 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3753 TRACE("nItem=%d\n", nItem);
3755 return nItem;
3759 /***
3760 * DESCRIPTION:
3761 * Erases the background of the given rectangle
3763 * PARAMETER(S):
3764 * [I] infoPtr : valid pointer to the listview structure
3765 * [I] hdc : device context handle
3766 * [I] lprcBox : clipping rectangle
3768 * RETURN:
3769 * Success: TRUE
3770 * Failure: FALSE
3772 static inline BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3774 if (!infoPtr->hBkBrush) return FALSE;
3776 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
3778 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3781 /***
3782 * DESCRIPTION:
3783 * Draws an item.
3785 * PARAMETER(S):
3786 * [I] infoPtr : valid pointer to the listview structure
3787 * [I] hdc : device context handle
3788 * [I] nItem : item index
3789 * [I] nSubItem : subitem index
3790 * [I] pos : item position in client coordinates
3791 * [I] cdmode : custom draw mode
3793 * RETURN:
3794 * Success: TRUE
3795 * Failure: FALSE
3797 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3799 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3800 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3801 static WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3802 DWORD cdsubitemmode = CDRF_DODEFAULT;
3803 LPRECT lprcFocus;
3804 RECT rcSelect, rcBox, rcIcon, rcLabel, rcStateIcon;
3805 NMLVCUSTOMDRAW nmlvcd;
3806 HIMAGELIST himl;
3807 LVITEMW lvItem;
3808 HFONT hOldFont;
3810 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
3812 /* get information needed for drawing the item */
3813 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
3814 if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
3815 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3816 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3817 lvItem.iItem = nItem;
3818 lvItem.iSubItem = nSubItem;
3819 lvItem.state = 0;
3820 lvItem.lParam = 0;
3821 lvItem.cchTextMax = DISP_TEXT_SIZE;
3822 lvItem.pszText = szDispText;
3823 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3824 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3825 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3826 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
3827 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3829 /* now check if we need to update the focus rectangle */
3830 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3832 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3833 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
3834 OffsetRect(&rcBox, pos.x, pos.y);
3835 OffsetRect(&rcSelect, pos.x, pos.y);
3836 OffsetRect(&rcIcon, pos.x, pos.y);
3837 OffsetRect(&rcStateIcon, pos.x, pos.y);
3838 OffsetRect(&rcLabel, pos.x, pos.y);
3839 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
3840 wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect),
3841 wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
3843 /* fill in the custom draw structure */
3844 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3846 hOldFont = GetCurrentObject(hdc, OBJ_FONT);
3847 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3848 if (cdmode & CDRF_SKIPDEFAULT) goto postpaint;
3849 if (cdmode & CDRF_NOTIFYITEMDRAW)
3850 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3851 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3852 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3853 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3854 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3856 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3857 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3859 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3860 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
3861 else if ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) == FALSE)
3862 prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE);
3864 /* in full row select, subitems, will just use main item's colors */
3865 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3866 nmlvcd.clrTextBk = CLR_NONE;
3868 /* state icons */
3869 if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && (nSubItem == 0))
3871 UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
3872 if (uStateImage)
3874 TRACE("uStateImage=%d\n", uStateImage);
3875 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
3876 rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
3880 /* small icons */
3881 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3882 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3884 TRACE("iImage=%d\n", lvItem.iImage);
3885 ImageList_DrawEx(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3886 rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk, CLR_DEFAULT,
3887 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3890 /* Don't bother painting item being edited */
3891 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3893 /* FIXME: temporary hack */
3894 rcSelect.left = rcLabel.left;
3896 /* draw the selection background, if we're drawing the main item */
3897 if (nSubItem == 0)
3899 /* in icon mode, the label rect is really what we want to draw the
3900 * background for */
3901 if (uView == LVS_ICON)
3902 rcSelect = rcLabel;
3904 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3905 rcSelect.right = rcBox.right;
3907 if (nmlvcd.clrTextBk != CLR_NONE)
3908 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3909 if(lprcFocus) *lprcFocus = rcSelect;
3912 /* figure out the text drawing flags */
3913 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3914 if (uView == LVS_ICON)
3915 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3916 else if (nSubItem)
3918 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3920 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3921 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3922 default: uFormat |= DT_LEFT;
3925 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3927 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3928 else rcLabel.left += LABEL_HOR_PADDING;
3930 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3932 /* for GRIDLINES reduce the bottom so the text formats correctly */
3933 if (uView == LVS_REPORT && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
3934 rcLabel.bottom--;
3936 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3938 postpaint:
3939 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3940 notify_postpaint(infoPtr, &nmlvcd);
3941 if (cdsubitemmode & CDRF_NEWFONT)
3942 SelectObject(hdc, hOldFont);
3943 return TRUE;
3946 /***
3947 * DESCRIPTION:
3948 * Draws listview items when in owner draw mode.
3950 * PARAMETER(S):
3951 * [I] infoPtr : valid pointer to the listview structure
3952 * [I] hdc : device context handle
3954 * RETURN:
3955 * None
3957 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3959 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3960 DWORD cditemmode = CDRF_DODEFAULT;
3961 NMLVCUSTOMDRAW nmlvcd;
3962 POINT Origin, Position;
3963 DRAWITEMSTRUCT dis;
3964 LVITEMW item;
3966 TRACE("()\n");
3968 ZeroMemory(&dis, sizeof(dis));
3970 /* Get scroll info once before loop */
3971 LISTVIEW_GetOrigin(infoPtr, &Origin);
3973 /* iterate through the invalidated rows */
3974 while(iterator_next(i))
3976 item.iItem = i->nItem;
3977 item.iSubItem = 0;
3978 item.mask = LVIF_PARAM | LVIF_STATE;
3979 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3980 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3982 dis.CtlType = ODT_LISTVIEW;
3983 dis.CtlID = uID;
3984 dis.itemID = item.iItem;
3985 dis.itemAction = ODA_DRAWENTIRE;
3986 dis.itemState = 0;
3987 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3988 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3989 dis.hwndItem = infoPtr->hwndSelf;
3990 dis.hDC = hdc;
3991 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3992 dis.rcItem.left = Position.x + Origin.x;
3993 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3994 dis.rcItem.top = Position.y + Origin.y;
3995 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3996 dis.itemData = item.lParam;
3998 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
4001 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
4002 * structure for the rest. of the paint cycle
4004 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
4005 if (cdmode & CDRF_NOTIFYITEMDRAW)
4006 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4008 if (!(cditemmode & CDRF_SKIPDEFAULT))
4010 prepaint_setup (infoPtr, hdc, &nmlvcd, FALSE);
4011 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
4014 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
4015 notify_postpaint(infoPtr, &nmlvcd);
4019 /***
4020 * DESCRIPTION:
4021 * Draws listview items when in report display mode.
4023 * PARAMETER(S):
4024 * [I] infoPtr : valid pointer to the listview structure
4025 * [I] hdc : device context handle
4026 * [I] cdmode : custom draw mode
4028 * RETURN:
4029 * None
4031 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4033 INT rgntype;
4034 RECT rcClip, rcItem;
4035 POINT Origin, Position;
4036 RANGE colRange;
4037 ITERATOR j;
4039 TRACE("()\n");
4041 /* figure out what to draw */
4042 rgntype = GetClipBox(hdc, &rcClip);
4043 if (rgntype == NULLREGION) return;
4045 /* Get scroll info once before loop */
4046 LISTVIEW_GetOrigin(infoPtr, &Origin);
4048 /* narrow down the columns we need to paint */
4049 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
4051 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
4052 if (rcItem.right + Origin.x >= rcClip.left) break;
4054 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
4056 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
4057 if (rcItem.left + Origin.x < rcClip.right) break;
4059 iterator_rangeitems(&j, colRange);
4061 /* in full row select, we _have_ to draw the main item */
4062 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
4063 j.nSpecial = 0;
4065 /* iterate through the invalidated rows */
4066 while(iterator_next(i))
4068 /* iterate through the invalidated columns */
4069 while(iterator_next(&j))
4071 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4072 Position.x += Origin.x;
4073 Position.y += Origin.y;
4075 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
4077 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4078 rcItem.top = 0;
4079 rcItem.bottom = infoPtr->nItemHeight;
4080 OffsetRect(&rcItem, Position.x, Position.y);
4081 if (!RectVisible(hdc, &rcItem)) continue;
4084 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
4087 iterator_destroy(&j);
4090 /***
4091 * DESCRIPTION:
4092 * Draws the gridlines if necessary when in report display mode.
4094 * PARAMETER(S):
4095 * [I] infoPtr : valid pointer to the listview structure
4096 * [I] hdc : device context handle
4098 * RETURN:
4099 * None
4101 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO *infoPtr, HDC hdc)
4103 INT rgntype;
4104 INT y, itemheight;
4105 HPEN hPen, hOldPen;
4106 RECT rcClip, rcItem;
4107 POINT Origin;
4108 RANGE colRange;
4109 ITERATOR j;
4111 TRACE("()\n");
4113 /* figure out what to draw */
4114 rgntype = GetClipBox(hdc, &rcClip);
4115 if (rgntype == NULLREGION) return;
4117 /* Get scroll info once before loop */
4118 LISTVIEW_GetOrigin(infoPtr, &Origin);
4120 /* narrow down the columns we need to paint */
4121 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
4123 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
4124 if (rcItem.right + Origin.x >= rcClip.left) break;
4126 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
4128 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
4129 if (rcItem.left + Origin.x < rcClip.right) break;
4131 iterator_rangeitems(&j, colRange);
4133 if ((hPen = CreatePen( PS_SOLID, 1, comctl32_color.clr3dFace )))
4135 hOldPen = SelectObject ( hdc, hPen );
4137 /* draw the vertical lines for the columns */
4138 iterator_rangeitems(&j, colRange);
4139 while(iterator_next(&j))
4141 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4142 if (rcItem.left == 0) continue; /* skip first column */
4143 rcItem.left += Origin.x;
4144 rcItem.right += Origin.x;
4145 rcItem.top = infoPtr->rcList.top;
4146 rcItem.bottom = infoPtr->rcList.bottom;
4147 TRACE("vert col=%d, rcItem=%s\n", j.nItem, wine_dbgstr_rect(&rcItem));
4148 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4149 LineTo (hdc, rcItem.left, rcItem.bottom);
4151 iterator_destroy(&j);
4153 /* draw the horizontial lines for the rows */
4154 itemheight = LISTVIEW_CalculateItemHeight(infoPtr);
4155 rcItem.left = infoPtr->rcList.left + Origin.x;
4156 rcItem.right = infoPtr->rcList.right + Origin.x;
4157 rcItem.bottom = rcItem.top = Origin.y - 1;
4158 MoveToEx(hdc, rcItem.left, rcItem.top, NULL);
4159 LineTo(hdc, rcItem.right, rcItem.top);
4160 for(y=itemheight-1+Origin.y; y<=infoPtr->rcList.bottom; y+=itemheight)
4162 rcItem.bottom = rcItem.top = y;
4163 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem));
4164 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4165 LineTo (hdc, rcItem.right, rcItem.top);
4168 SelectObject( hdc, hOldPen );
4169 DeleteObject( hPen );
4173 /***
4174 * DESCRIPTION:
4175 * Draws listview items when in list display mode.
4177 * PARAMETER(S):
4178 * [I] infoPtr : valid pointer to the listview structure
4179 * [I] hdc : device context handle
4180 * [I] cdmode : custom draw mode
4182 * RETURN:
4183 * None
4185 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4187 POINT Origin, Position;
4189 /* Get scroll info once before loop */
4190 LISTVIEW_GetOrigin(infoPtr, &Origin);
4192 while(iterator_prev(i))
4194 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4195 Position.x += Origin.x;
4196 Position.y += Origin.y;
4198 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
4203 /***
4204 * DESCRIPTION:
4205 * Draws listview items.
4207 * PARAMETER(S):
4208 * [I] infoPtr : valid pointer to the listview structure
4209 * [I] hdc : device context handle
4210 * [I] prcErase : rect to be erased before refresh (may be NULL)
4212 * RETURN:
4213 * NoneX
4215 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcErase)
4217 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4218 COLORREF oldTextColor = 0, oldBkColor = 0, oldClrTextBk, oldClrText;
4219 NMLVCUSTOMDRAW nmlvcd;
4220 HFONT hOldFont = 0;
4221 DWORD cdmode;
4222 INT oldBkMode = 0;
4223 RECT rcClient;
4224 ITERATOR i;
4225 HDC hdcOrig = hdc;
4226 HBITMAP hbmp = NULL;
4228 LISTVIEW_DUMP(infoPtr);
4230 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4231 TRACE("double buffering\n");
4233 hdc = CreateCompatibleDC(hdcOrig);
4234 if (!hdc) {
4235 ERR("Failed to create DC for backbuffer\n");
4236 return;
4238 hbmp = CreateCompatibleBitmap(hdcOrig, infoPtr->rcList.right,
4239 infoPtr->rcList.bottom);
4240 if (!hbmp) {
4241 ERR("Failed to create bitmap for backbuffer\n");
4242 DeleteDC(hdc);
4243 return;
4246 SelectObject(hdc, hbmp);
4247 SelectObject(hdc, infoPtr->hFont);
4248 } else {
4249 /* Save dc values we're gonna trash while drawing
4250 * FIXME: Should be done in LISTVIEW_DrawItem() */
4251 hOldFont = SelectObject(hdc, infoPtr->hFont);
4252 oldBkMode = GetBkMode(hdc);
4253 oldBkColor = GetBkColor(hdc);
4254 oldTextColor = GetTextColor(hdc);
4257 infoPtr->bIsDrawing = TRUE;
4259 if (prcErase) {
4260 LISTVIEW_FillBkgnd(infoPtr, hdc, prcErase);
4261 } else if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4262 /* If no erasing was done (usually because RedrawWindow was called
4263 * with RDW_INVALIDATE only) we need to copy the old contents into
4264 * the backbuffer before continuing. */
4265 BitBlt(hdc, infoPtr->rcList.left, infoPtr->rcList.top,
4266 infoPtr->rcList.right - infoPtr->rcList.left,
4267 infoPtr->rcList.bottom - infoPtr->rcList.top,
4268 hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4271 /* FIXME: Shouldn't need to do this */
4272 oldClrTextBk = infoPtr->clrTextBk;
4273 oldClrText = infoPtr->clrText;
4275 infoPtr->cditemmode = CDRF_DODEFAULT;
4277 GetClientRect(infoPtr->hwndSelf, &rcClient);
4278 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4279 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4280 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4281 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4283 /* Use these colors to draw the items */
4284 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4285 infoPtr->clrText = nmlvcd.clrText;
4287 /* nothing to draw */
4288 if(infoPtr->nItemCount == 0) goto enddraw;
4290 /* figure out what we need to draw */
4291 iterator_visibleitems(&i, infoPtr, hdc);
4293 /* send cache hint notification */
4294 if (infoPtr->dwStyle & LVS_OWNERDATA)
4296 RANGE range = iterator_range(&i);
4297 NMLVCACHEHINT nmlv;
4299 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4300 nmlv.iFrom = range.lower;
4301 nmlv.iTo = range.upper - 1;
4302 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4305 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4306 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4307 else
4309 if (uView == LVS_REPORT)
4310 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4311 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4312 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4314 /* if we have a focus rect, draw it */
4315 if (infoPtr->bFocus)
4316 DrawFocusRect(hdc, &infoPtr->rcFocus);
4318 iterator_destroy(&i);
4320 enddraw:
4321 /* For LVS_EX_GRIDLINES go and draw lines */
4322 /* This includes the case where there were *no* items */
4323 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
4324 infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
4325 LISTVIEW_RefreshReportGrid(infoPtr, hdc);
4327 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4328 notify_postpaint(infoPtr, &nmlvcd);
4330 infoPtr->clrTextBk = oldClrTextBk;
4331 infoPtr->clrText = oldClrText;
4333 if(hbmp) {
4334 BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top,
4335 infoPtr->rcList.right - infoPtr->rcList.left,
4336 infoPtr->rcList.bottom - infoPtr->rcList.top,
4337 hdc, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4339 DeleteObject(hbmp);
4340 DeleteDC(hdc);
4341 } else {
4342 SelectObject(hdc, hOldFont);
4343 SetBkMode(hdc, oldBkMode);
4344 SetBkColor(hdc, oldBkColor);
4345 SetTextColor(hdc, oldTextColor);
4348 infoPtr->bIsDrawing = FALSE;
4352 /***
4353 * DESCRIPTION:
4354 * Calculates the approximate width and height of a given number of items.
4356 * PARAMETER(S):
4357 * [I] infoPtr : valid pointer to the listview structure
4358 * [I] nItemCount : number of items
4359 * [I] wWidth : width
4360 * [I] wHeight : height
4362 * RETURN:
4363 * Returns a DWORD. The width in the low word and the height in high word.
4365 static DWORD LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO *infoPtr, INT nItemCount,
4366 WORD wWidth, WORD wHeight)
4368 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4369 INT nItemCountPerColumn = 1;
4370 INT nColumnCount = 0;
4371 DWORD dwViewRect = 0;
4373 if (nItemCount == -1)
4374 nItemCount = infoPtr->nItemCount;
4376 if (uView == LVS_LIST)
4378 if (wHeight == 0xFFFF)
4380 /* use current height */
4381 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4384 if (wHeight < infoPtr->nItemHeight)
4385 wHeight = infoPtr->nItemHeight;
4387 if (nItemCount > 0)
4389 if (infoPtr->nItemHeight > 0)
4391 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4392 if (nItemCountPerColumn == 0)
4393 nItemCountPerColumn = 1;
4395 if (nItemCount % nItemCountPerColumn != 0)
4396 nColumnCount = nItemCount / nItemCountPerColumn;
4397 else
4398 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4402 /* Microsoft padding magic */
4403 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4404 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4406 dwViewRect = MAKELONG(wWidth, wHeight);
4408 else if (uView == LVS_REPORT)
4410 RECT rcBox;
4412 if (infoPtr->nItemCount > 0)
4414 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4415 wWidth = rcBox.right - rcBox.left;
4416 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4418 else
4420 /* use current height and width */
4421 if (wHeight == 0xffff)
4422 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4423 if (wWidth == 0xffff)
4424 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4427 dwViewRect = MAKELONG(wWidth, wHeight);
4429 else if (uView == LVS_SMALLICON)
4430 FIXME("uView == LVS_SMALLICON: not implemented\n");
4431 else if (uView == LVS_ICON)
4432 FIXME("uView == LVS_ICON: not implemented\n");
4434 return dwViewRect;
4438 /***
4439 * DESCRIPTION:
4440 * Create a drag image list for the specified item.
4442 * PARAMETER(S):
4443 * [I] infoPtr : valid pointer to the listview structure
4444 * [I] iItem : index of item
4445 * [O] lppt : Upperr-left corner of the image
4447 * RETURN:
4448 * Returns a handle to the image list if successful, NULL otherwise.
4450 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4452 RECT rcItem;
4453 SIZE size;
4454 POINT pos;
4455 HDC hdc, hdcOrig;
4456 HBITMAP hbmp, hOldbmp;
4457 HIMAGELIST dragList = 0;
4458 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
4460 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4461 return 0;
4463 rcItem.left = LVIR_BOUNDS;
4464 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4465 return 0;
4467 lppt->x = rcItem.left;
4468 lppt->y = rcItem.top;
4470 size.cx = rcItem.right - rcItem.left;
4471 size.cy = rcItem.bottom - rcItem.top;
4473 hdcOrig = GetDC(infoPtr->hwndSelf);
4474 hdc = CreateCompatibleDC(hdcOrig);
4475 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4476 hOldbmp = SelectObject(hdc, hbmp);
4478 rcItem.left = rcItem.top = 0;
4479 rcItem.right = size.cx;
4480 rcItem.bottom = size.cy;
4481 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4483 pos.x = pos.y = 0;
4484 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4486 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4487 SelectObject(hdc, hOldbmp);
4488 ImageList_Add(dragList, hbmp, 0);
4490 else
4491 SelectObject(hdc, hOldbmp);
4493 DeleteObject(hbmp);
4494 DeleteDC(hdc);
4495 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4497 TRACE("ret=%p\n", dragList);
4499 return dragList;
4503 /***
4504 * DESCRIPTION:
4505 * Removes all listview items and subitems.
4507 * PARAMETER(S):
4508 * [I] infoPtr : valid pointer to the listview structure
4510 * RETURN:
4511 * SUCCESS : TRUE
4512 * FAILURE : FALSE
4514 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr, BOOL destroy)
4516 NMLISTVIEW nmlv;
4517 HDPA hdpaSubItems = NULL;
4518 BOOL bSuppress;
4519 ITEMHDR *hdrItem;
4520 INT i, j;
4522 TRACE("()\n");
4524 /* we do it directly, to avoid notifications */
4525 ranges_clear(infoPtr->selectionRanges);
4526 infoPtr->nSelectionMark = -1;
4527 infoPtr->nFocusedItem = -1;
4528 SetRectEmpty(&infoPtr->rcFocus);
4529 /* But we are supposed to leave nHotItem as is! */
4532 /* send LVN_DELETEALLITEMS notification */
4533 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4534 nmlv.iItem = -1;
4535 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4537 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4539 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4541 /* send LVN_DELETEITEM notification, if not suppressed
4542 and if it is not a virtual listview */
4543 if (!bSuppress) notify_deleteitem(infoPtr, i);
4544 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
4545 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4547 hdrItem = DPA_GetPtr(hdpaSubItems, j);
4548 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4549 Free(hdrItem);
4551 DPA_Destroy(hdpaSubItems);
4552 DPA_DeletePtr(infoPtr->hdpaItems, i);
4554 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4555 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4556 infoPtr->nItemCount --;
4559 if (!destroy)
4561 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4562 LISTVIEW_UpdateScroll(infoPtr);
4564 LISTVIEW_InvalidateList(infoPtr);
4566 return TRUE;
4569 /***
4570 * DESCRIPTION:
4571 * Scrolls, and updates the columns, when a column is changing width.
4573 * PARAMETER(S):
4574 * [I] infoPtr : valid pointer to the listview structure
4575 * [I] nColumn : column to scroll
4576 * [I] dx : amount of scroll, in pixels
4578 * RETURN:
4579 * None.
4581 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4583 COLUMN_INFO *lpColumnInfo;
4584 RECT rcOld, rcCol;
4585 POINT ptOrigin;
4586 INT nCol;
4588 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4589 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4590 rcCol = lpColumnInfo->rcHeader;
4591 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4592 rcCol.left = rcCol.right;
4594 /* adjust the other columns */
4595 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4597 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4598 lpColumnInfo->rcHeader.left += dx;
4599 lpColumnInfo->rcHeader.right += dx;
4602 /* do not update screen if not in report mode */
4603 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4605 /* if we have a focus, we must first erase the focus rect */
4606 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4608 /* Need to reset the item width when inserting a new column */
4609 infoPtr->nItemWidth += dx;
4611 LISTVIEW_UpdateScroll(infoPtr);
4612 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4614 /* scroll to cover the deleted column, and invalidate for redraw */
4615 rcOld = infoPtr->rcList;
4616 rcOld.left = ptOrigin.x + rcCol.left + dx;
4617 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4619 /* we can restore focus now */
4620 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4623 /***
4624 * DESCRIPTION:
4625 * Removes a column from the listview control.
4627 * PARAMETER(S):
4628 * [I] infoPtr : valid pointer to the listview structure
4629 * [I] nColumn : column index
4631 * RETURN:
4632 * SUCCESS : TRUE
4633 * FAILURE : FALSE
4635 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4637 RECT rcCol;
4639 TRACE("nColumn=%d\n", nColumn);
4641 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4642 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4644 /* While the MSDN specifically says that column zero should not be deleted,
4645 what actually happens is that the column itself is deleted but no items or subitems
4646 are removed.
4649 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4651 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4652 return FALSE;
4654 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4655 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4657 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4659 SUBITEM_INFO *lpSubItem, *lpDelItem;
4660 HDPA hdpaSubItems;
4661 INT nItem, nSubItem, i;
4663 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4665 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
4666 nSubItem = 0;
4667 lpDelItem = 0;
4668 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4670 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
4671 if (lpSubItem->iSubItem == nColumn)
4673 nSubItem = i;
4674 lpDelItem = lpSubItem;
4676 else if (lpSubItem->iSubItem > nColumn)
4678 lpSubItem->iSubItem--;
4682 /* if we found our subitem, zapp it */
4683 if (nSubItem > 0)
4685 /* free string */
4686 if (is_textW(lpDelItem->hdr.pszText))
4687 Free(lpDelItem->hdr.pszText);
4689 /* free item */
4690 Free(lpDelItem);
4692 /* free dpa memory */
4693 DPA_DeletePtr(hdpaSubItems, nSubItem);
4698 /* update the other column info */
4699 LISTVIEW_UpdateItemSize(infoPtr);
4700 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4701 LISTVIEW_InvalidateList(infoPtr);
4702 else
4703 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4705 return TRUE;
4708 /***
4709 * DESCRIPTION:
4710 * Invalidates the listview after an item's insertion or deletion.
4712 * PARAMETER(S):
4713 * [I] infoPtr : valid pointer to the listview structure
4714 * [I] nItem : item index
4715 * [I] dir : -1 if deleting, 1 if inserting
4717 * RETURN:
4718 * None
4720 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4722 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4723 INT nPerCol, nItemCol, nItemRow;
4724 RECT rcScroll;
4725 POINT Origin;
4727 /* if we don't refresh, what's the point of scrolling? */
4728 if (!is_redrawing(infoPtr)) return;
4730 assert (abs(dir) == 1);
4732 /* arrange icons if autoarrange is on */
4733 if (is_autoarrange(infoPtr))
4735 BOOL arrange = TRUE;
4736 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4737 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4738 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4741 /* scrollbars need updating */
4742 LISTVIEW_UpdateScroll(infoPtr);
4744 /* figure out the item's position */
4745 if (uView == LVS_REPORT)
4746 nPerCol = infoPtr->nItemCount + 1;
4747 else if (uView == LVS_LIST)
4748 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4749 else /* LVS_ICON, or LVS_SMALLICON */
4750 return;
4752 nItemCol = nItem / nPerCol;
4753 nItemRow = nItem % nPerCol;
4754 LISTVIEW_GetOrigin(infoPtr, &Origin);
4756 /* move the items below up a slot */
4757 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4758 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4759 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4760 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4761 OffsetRect(&rcScroll, Origin.x, Origin.y);
4762 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
4763 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4765 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
4766 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4767 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4770 /* report has only that column, so we're done */
4771 if (uView == LVS_REPORT) return;
4773 /* now for LISTs, we have to deal with the columns to the right */
4774 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4775 rcScroll.top = 0;
4776 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4777 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4778 OffsetRect(&rcScroll, Origin.x, Origin.y);
4779 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4780 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4781 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4784 /***
4785 * DESCRIPTION:
4786 * Removes an item from the listview control.
4788 * PARAMETER(S):
4789 * [I] infoPtr : valid pointer to the listview structure
4790 * [I] nItem : item index
4792 * RETURN:
4793 * SUCCESS : TRUE
4794 * FAILURE : FALSE
4796 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4798 LVITEMW item;
4799 const UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4800 const BOOL is_icon = (uView == LVS_SMALLICON || uView == LVS_ICON);
4802 TRACE("(nItem=%d)\n", nItem);
4804 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4806 /* remove selection, and focus */
4807 item.state = 0;
4808 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4809 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4811 /* send LVN_DELETEITEM notification. */
4812 if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
4814 /* we need to do this here, because we'll be deleting stuff */
4815 if (is_icon)
4816 LISTVIEW_InvalidateItem(infoPtr, nItem);
4818 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4820 HDPA hdpaSubItems;
4821 ITEMHDR *hdrItem;
4822 INT i;
4824 hdpaSubItems = DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4825 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4827 hdrItem = DPA_GetPtr(hdpaSubItems, i);
4828 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4829 Free(hdrItem);
4831 DPA_Destroy(hdpaSubItems);
4834 if (is_icon)
4836 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4837 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4840 infoPtr->nItemCount--;
4841 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4843 /* now is the invalidation fun */
4844 if (!is_icon)
4845 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4846 return TRUE;
4850 /***
4851 * DESCRIPTION:
4852 * Callback implementation for editlabel control
4854 * PARAMETER(S):
4855 * [I] infoPtr : valid pointer to the listview structure
4856 * [I] pszText : modified text
4857 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4859 * RETURN:
4860 * SUCCESS : TRUE
4861 * FAILURE : FALSE
4863 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4865 HWND hwndSelf = infoPtr->hwndSelf;
4866 NMLVDISPINFOW dispInfo;
4867 INT editedItem = infoPtr->nEditLabelItem;
4868 BOOL bSame;
4870 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4872 infoPtr->nEditLabelItem = -1;
4874 ZeroMemory(&dispInfo, sizeof(dispInfo));
4875 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4876 dispInfo.item.iItem = editedItem;
4877 dispInfo.item.iSubItem = 0;
4878 dispInfo.item.stateMask = ~0;
4879 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4881 if (isW)
4882 bSame = (lstrcmpW(dispInfo.item.pszText, pszText) == 0);
4883 else
4885 LPWSTR tmp = textdupTtoW(pszText, FALSE);
4886 bSame = (lstrcmpW(dispInfo.item.pszText, tmp) == 0);
4887 textfreeT(tmp, FALSE);
4889 if (bSame) return TRUE;
4891 /* add the text from the edit in */
4892 dispInfo.item.mask |= LVIF_TEXT;
4893 dispInfo.item.pszText = pszText;
4894 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4896 /* Do we need to update the Item Text */
4897 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4898 if (!IsWindow(hwndSelf))
4899 return FALSE;
4900 if (!pszText) return TRUE;
4902 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4904 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, editedItem);
4905 ITEM_INFO* lpItem = DPA_GetPtr(hdpaSubItems, 0);
4906 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4908 LISTVIEW_InvalidateItem(infoPtr, editedItem);
4909 return TRUE;
4913 ZeroMemory(&dispInfo, sizeof(dispInfo));
4914 dispInfo.item.mask = LVIF_TEXT;
4915 dispInfo.item.iItem = editedItem;
4916 dispInfo.item.iSubItem = 0;
4917 dispInfo.item.pszText = pszText;
4918 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4919 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4922 /***
4923 * DESCRIPTION:
4924 * Begin in place editing of specified list view item
4926 * PARAMETER(S):
4927 * [I] infoPtr : valid pointer to the listview structure
4928 * [I] nItem : item index
4929 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4931 * RETURN:
4932 * SUCCESS : TRUE
4933 * FAILURE : FALSE
4935 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4937 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4938 NMLVDISPINFOW dispInfo;
4939 RECT rect;
4940 HWND hwndSelf = infoPtr->hwndSelf;
4942 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4944 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4945 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4947 infoPtr->nEditLabelItem = nItem;
4949 /* Is the EditBox still there, if so remove it */
4950 if(infoPtr->hwndEdit != 0)
4952 SetFocus(infoPtr->hwndSelf);
4953 infoPtr->hwndEdit = 0;
4956 LISTVIEW_SetSelection(infoPtr, nItem);
4957 LISTVIEW_SetItemFocus(infoPtr, nItem);
4958 LISTVIEW_InvalidateItem(infoPtr, nItem);
4960 rect.left = LVIR_LABEL;
4961 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4963 ZeroMemory(&dispInfo, sizeof(dispInfo));
4964 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4965 dispInfo.item.iItem = nItem;
4966 dispInfo.item.iSubItem = 0;
4967 dispInfo.item.stateMask = ~0;
4968 dispInfo.item.pszText = szDispText;
4969 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4970 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4972 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4973 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4974 if (!infoPtr->hwndEdit) return 0;
4976 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4978 if (!IsWindow(hwndSelf))
4979 return 0;
4980 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4981 infoPtr->hwndEdit = 0;
4982 return 0;
4985 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4986 SetFocus(infoPtr->hwndEdit);
4987 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4988 return infoPtr->hwndEdit;
4992 /***
4993 * DESCRIPTION:
4994 * Ensures the specified item is visible, scrolling into view if necessary.
4996 * PARAMETER(S):
4997 * [I] infoPtr : valid pointer to the listview structure
4998 * [I] nItem : item index
4999 * [I] bPartial : partially or entirely visible
5001 * RETURN:
5002 * SUCCESS : TRUE
5003 * FAILURE : FALSE
5005 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
5007 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5008 INT nScrollPosHeight = 0;
5009 INT nScrollPosWidth = 0;
5010 INT nHorzAdjust = 0;
5011 INT nVertAdjust = 0;
5012 INT nHorzDiff = 0;
5013 INT nVertDiff = 0;
5014 RECT rcItem, rcTemp;
5016 rcItem.left = LVIR_BOUNDS;
5017 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
5019 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
5021 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
5023 /* scroll left/right, but in LVS_REPORT mode */
5024 if (uView == LVS_LIST)
5025 nScrollPosWidth = infoPtr->nItemWidth;
5026 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5027 nScrollPosWidth = 1;
5029 if (rcItem.left < infoPtr->rcList.left)
5031 nHorzAdjust = -1;
5032 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
5034 else
5036 nHorzAdjust = 1;
5037 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
5041 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
5043 /* scroll up/down, but not in LVS_LIST mode */
5044 if (uView == LVS_REPORT)
5045 nScrollPosHeight = infoPtr->nItemHeight;
5046 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
5047 nScrollPosHeight = 1;
5049 if (rcItem.top < infoPtr->rcList.top)
5051 nVertAdjust = -1;
5052 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
5054 else
5056 nVertAdjust = 1;
5057 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
5061 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
5063 if (nScrollPosWidth)
5065 INT diff = nHorzDiff / nScrollPosWidth;
5066 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
5067 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
5070 if (nScrollPosHeight)
5072 INT diff = nVertDiff / nScrollPosHeight;
5073 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
5074 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
5077 return TRUE;
5080 /***
5081 * DESCRIPTION:
5082 * Searches for an item with specific characteristics.
5084 * PARAMETER(S):
5085 * [I] hwnd : window handle
5086 * [I] nStart : base item index
5087 * [I] lpFindInfo : item information to look for
5089 * RETURN:
5090 * SUCCESS : index of item
5091 * FAILURE : -1
5093 static INT LISTVIEW_FindItemW(const LISTVIEW_INFO *infoPtr, INT nStart,
5094 const LVFINDINFOW *lpFindInfo)
5096 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5097 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5098 BOOL bWrap = FALSE, bNearest = FALSE;
5099 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
5100 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
5101 POINT Position, Destination;
5102 LVITEMW lvItem;
5104 /* Search in virtual listviews should be done by application, not by
5105 listview control, so we just send LVN_ODFINDITEMW and return the result */
5106 if (infoPtr->dwStyle & LVS_OWNERDATA)
5108 NMLVFINDITEMW nmlv;
5110 nmlv.iStart = nStart;
5111 nmlv.lvfi = *lpFindInfo;
5112 return notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
5115 if (!lpFindInfo || nItem < 0) return -1;
5117 lvItem.mask = 0;
5118 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
5120 lvItem.mask |= LVIF_TEXT;
5121 lvItem.pszText = szDispText;
5122 lvItem.cchTextMax = DISP_TEXT_SIZE;
5125 if (lpFindInfo->flags & LVFI_WRAP)
5126 bWrap = TRUE;
5128 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
5129 (uView == LVS_ICON || uView ==LVS_SMALLICON))
5131 POINT Origin;
5132 RECT rcArea;
5134 LISTVIEW_GetOrigin(infoPtr, &Origin);
5135 Destination.x = lpFindInfo->pt.x - Origin.x;
5136 Destination.y = lpFindInfo->pt.y - Origin.y;
5137 switch(lpFindInfo->vkDirection)
5139 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
5140 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
5141 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
5142 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
5143 case VK_HOME: Destination.x = Destination.y = 0; break;
5144 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5145 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5146 case VK_END:
5147 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
5148 Destination.x = rcArea.right;
5149 Destination.y = rcArea.bottom;
5150 break;
5151 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
5153 bNearest = TRUE;
5155 else Destination.x = Destination.y = 0;
5157 /* if LVFI_PARAM is specified, all other flags are ignored */
5158 if (lpFindInfo->flags & LVFI_PARAM)
5160 lvItem.mask |= LVIF_PARAM;
5161 bNearest = FALSE;
5162 lvItem.mask &= ~LVIF_TEXT;
5165 again:
5166 for (; nItem < nLast; nItem++)
5168 lvItem.iItem = nItem;
5169 lvItem.iSubItem = 0;
5170 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
5172 if (lvItem.mask & LVIF_PARAM)
5174 if (lpFindInfo->lParam == lvItem.lParam)
5175 return nItem;
5176 else
5177 continue;
5180 if (lvItem.mask & LVIF_TEXT)
5182 if (lpFindInfo->flags & LVFI_PARTIAL)
5184 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
5186 else
5188 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
5192 if (!bNearest) return nItem;
5194 /* This is very inefficient. To do a good job here,
5195 * we need a sorted array of (x,y) item positions */
5196 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5198 /* compute the distance^2 to the destination */
5199 xdist = Destination.x - Position.x;
5200 ydist = Destination.y - Position.y;
5201 dist = xdist * xdist + ydist * ydist;
5203 /* remember the distance, and item if it's closer */
5204 if (dist < mindist)
5206 mindist = dist;
5207 nNearestItem = nItem;
5211 if (bWrap)
5213 nItem = 0;
5214 nLast = min(nStart + 1, infoPtr->nItemCount);
5215 bWrap = FALSE;
5216 goto again;
5219 return nNearestItem;
5222 /***
5223 * DESCRIPTION:
5224 * Searches for an item with specific characteristics.
5226 * PARAMETER(S):
5227 * [I] hwnd : window handle
5228 * [I] nStart : base item index
5229 * [I] lpFindInfo : item information to look for
5231 * RETURN:
5232 * SUCCESS : index of item
5233 * FAILURE : -1
5235 static INT LISTVIEW_FindItemA(const LISTVIEW_INFO *infoPtr, INT nStart,
5236 const LVFINDINFOA *lpFindInfo)
5238 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
5239 LVFINDINFOW fiw;
5240 INT res;
5241 LPWSTR strW = NULL;
5243 memcpy(&fiw, lpFindInfo, sizeof(fiw));
5244 if (hasText) fiw.psz = strW = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
5245 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
5246 textfreeT(strW, FALSE);
5247 return res;
5250 /***
5251 * DESCRIPTION:
5252 * Retrieves the background image of the listview control.
5254 * PARAMETER(S):
5255 * [I] infoPtr : valid pointer to the listview structure
5256 * [O] lpBkImage : background image attributes
5258 * RETURN:
5259 * SUCCESS : TRUE
5260 * FAILURE : FALSE
5262 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
5263 /* { */
5264 /* FIXME (listview, "empty stub!\n"); */
5265 /* return FALSE; */
5266 /* } */
5268 /***
5269 * DESCRIPTION:
5270 * Retrieves column attributes.
5272 * PARAMETER(S):
5273 * [I] infoPtr : valid pointer to the listview structure
5274 * [I] nColumn : column index
5275 * [IO] lpColumn : column information
5276 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5277 * otherwise it is in fact a LPLVCOLUMNA
5279 * RETURN:
5280 * SUCCESS : TRUE
5281 * FAILURE : FALSE
5283 static BOOL LISTVIEW_GetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
5285 COLUMN_INFO *lpColumnInfo;
5286 HDITEMW hdi;
5288 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5289 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
5291 /* initialize memory */
5292 ZeroMemory(&hdi, sizeof(hdi));
5294 if (lpColumn->mask & LVCF_TEXT)
5296 hdi.mask |= HDI_TEXT;
5297 hdi.pszText = lpColumn->pszText;
5298 hdi.cchTextMax = lpColumn->cchTextMax;
5301 if (lpColumn->mask & LVCF_IMAGE)
5302 hdi.mask |= HDI_IMAGE;
5304 if (lpColumn->mask & LVCF_ORDER)
5305 hdi.mask |= HDI_ORDER;
5307 if (lpColumn->mask & LVCF_SUBITEM)
5308 hdi.mask |= HDI_LPARAM;
5310 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
5312 if (lpColumn->mask & LVCF_FMT)
5313 lpColumn->fmt = lpColumnInfo->fmt;
5315 if (lpColumn->mask & LVCF_WIDTH)
5316 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
5318 if (lpColumn->mask & LVCF_IMAGE)
5319 lpColumn->iImage = hdi.iImage;
5321 if (lpColumn->mask & LVCF_ORDER)
5322 lpColumn->iOrder = hdi.iOrder;
5324 if (lpColumn->mask & LVCF_SUBITEM)
5325 lpColumn->iSubItem = hdi.lParam;
5327 return TRUE;
5331 static BOOL LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5333 INT i;
5335 if (!lpiArray)
5336 return FALSE;
5338 /* FIXME: little hack */
5339 for (i = 0; i < iCount; i++)
5340 lpiArray[i] = i;
5342 return TRUE;
5345 /***
5346 * DESCRIPTION:
5347 * Retrieves the column width.
5349 * PARAMETER(S):
5350 * [I] infoPtr : valid pointer to the listview structure
5351 * [I] int : column index
5353 * RETURN:
5354 * SUCCESS : column width
5355 * FAILURE : zero
5357 static INT LISTVIEW_GetColumnWidth(const LISTVIEW_INFO *infoPtr, INT nColumn)
5359 INT nColumnWidth = 0;
5360 HDITEMW hdItem;
5362 TRACE("nColumn=%d\n", nColumn);
5364 /* we have a 'column' in LIST and REPORT mode only */
5365 switch(infoPtr->dwStyle & LVS_TYPEMASK)
5367 case LVS_LIST:
5368 nColumnWidth = infoPtr->nItemWidth;
5369 break;
5370 case LVS_REPORT:
5371 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDM_ITEMCHANGED.
5372 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
5373 * HDM_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
5375 * TODO: should we do the same in LVM_GETCOLUMN?
5377 hdItem.mask = HDI_WIDTH;
5378 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdItem))
5380 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr->hwndSelf, nColumn);
5381 return 0;
5383 nColumnWidth = hdItem.cxy;
5384 break;
5387 TRACE("nColumnWidth=%d\n", nColumnWidth);
5388 return nColumnWidth;
5391 /***
5392 * DESCRIPTION:
5393 * In list or report display mode, retrieves the number of items that can fit
5394 * vertically in the visible area. In icon or small icon display mode,
5395 * retrieves the total number of visible items.
5397 * PARAMETER(S):
5398 * [I] infoPtr : valid pointer to the listview structure
5400 * RETURN:
5401 * Number of fully visible items.
5403 static INT LISTVIEW_GetCountPerPage(const LISTVIEW_INFO *infoPtr)
5405 switch (infoPtr->dwStyle & LVS_TYPEMASK)
5407 case LVS_ICON:
5408 case LVS_SMALLICON:
5409 return infoPtr->nItemCount;
5410 case LVS_REPORT:
5411 return LISTVIEW_GetCountPerColumn(infoPtr);
5412 case LVS_LIST:
5413 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5415 assert(FALSE);
5416 return 0;
5419 /***
5420 * DESCRIPTION:
5421 * Retrieves an image list handle.
5423 * PARAMETER(S):
5424 * [I] infoPtr : valid pointer to the listview structure
5425 * [I] nImageList : image list identifier
5427 * RETURN:
5428 * SUCCESS : image list handle
5429 * FAILURE : NULL
5431 static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImageList)
5433 switch (nImageList)
5435 case LVSIL_NORMAL: return infoPtr->himlNormal;
5436 case LVSIL_SMALL: return infoPtr->himlSmall;
5437 case LVSIL_STATE: return infoPtr->himlState;
5439 return NULL;
5442 /* LISTVIEW_GetISearchString */
5444 /***
5445 * DESCRIPTION:
5446 * Retrieves item attributes.
5448 * PARAMETER(S):
5449 * [I] hwnd : window handle
5450 * [IO] lpLVItem : item info
5451 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5452 * if FALSE, then lpLVItem is a LPLVITEMA.
5454 * NOTE:
5455 * This is the internal 'GetItem' interface -- it tries to
5456 * be smart and avoid text copies, if possible, by modifying
5457 * lpLVItem->pszText to point to the text string. Please note
5458 * that this is not always possible (e.g. OWNERDATA), so on
5459 * entry you *must* supply valid values for pszText, and cchTextMax.
5460 * The only difference to the documented interface is that upon
5461 * return, you should use *only* the lpLVItem->pszText, rather than
5462 * the buffer pointer you provided on input. Most code already does
5463 * that, so it's not a problem.
5464 * For the two cases when the text must be copied (that is,
5465 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5467 * RETURN:
5468 * SUCCESS : TRUE
5469 * FAILURE : FALSE
5471 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5473 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5474 NMLVDISPINFOW dispInfo;
5475 ITEM_INFO *lpItem;
5476 ITEMHDR* pItemHdr;
5477 HDPA hdpaSubItems;
5478 INT isubitem;
5480 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5482 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5483 return FALSE;
5485 if (lpLVItem->mask == 0) return TRUE;
5487 /* make a local copy */
5488 isubitem = lpLVItem->iSubItem;
5490 /* a quick optimization if all we're asked is the focus state
5491 * these queries are worth optimising since they are common,
5492 * and can be answered in constant time, without the heavy accesses */
5493 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5494 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5496 lpLVItem->state = 0;
5497 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5498 lpLVItem->state |= LVIS_FOCUSED;
5499 return TRUE;
5502 ZeroMemory(&dispInfo, sizeof(dispInfo));
5504 /* if the app stores all the data, handle it separately */
5505 if (infoPtr->dwStyle & LVS_OWNERDATA)
5507 dispInfo.item.state = 0;
5509 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5510 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5512 /* NOTE: copy only fields which we _know_ are initialized, some apps
5513 * depend on the uninitialized fields being 0 */
5514 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5515 dispInfo.item.iItem = lpLVItem->iItem;
5516 dispInfo.item.iSubItem = isubitem;
5517 if (lpLVItem->mask & LVIF_TEXT)
5519 dispInfo.item.pszText = lpLVItem->pszText;
5520 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5522 if (lpLVItem->mask & LVIF_STATE)
5523 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5524 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5525 dispInfo.item.stateMask = lpLVItem->stateMask;
5526 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5528 /* full size structure expected - _WIN32IE >= 0x560 */
5529 *lpLVItem = dispInfo.item;
5531 else if (lpLVItem->mask & LVIF_INDENT)
5533 /* indent member expected - _WIN32IE >= 0x300 */
5534 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5536 else
5538 /* minimal structure expected */
5539 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5541 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5544 /* make sure lParam is zeroed out */
5545 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5547 /* we store only a little state, so if we're not asked, we're done */
5548 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5550 /* if focus is handled by us, report it */
5551 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5553 lpLVItem->state &= ~LVIS_FOCUSED;
5554 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5555 lpLVItem->state |= LVIS_FOCUSED;
5558 /* and do the same for selection, if we handle it */
5559 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5561 lpLVItem->state &= ~LVIS_SELECTED;
5562 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5563 lpLVItem->state |= LVIS_SELECTED;
5566 return TRUE;
5569 /* find the item and subitem structures before we proceed */
5570 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5571 lpItem = DPA_GetPtr(hdpaSubItems, 0);
5572 assert (lpItem);
5574 if (isubitem)
5576 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5577 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5578 if (!lpSubItem)
5580 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5581 isubitem = 0;
5584 else
5585 pItemHdr = &lpItem->hdr;
5587 /* Do we need to query the state from the app? */
5588 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5590 dispInfo.item.mask |= LVIF_STATE;
5591 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5594 /* Do we need to enquire about the image? */
5595 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5596 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5598 dispInfo.item.mask |= LVIF_IMAGE;
5599 dispInfo.item.iImage = I_IMAGECALLBACK;
5602 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5603 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5605 dispInfo.item.mask |= LVIF_TEXT;
5606 dispInfo.item.pszText = lpLVItem->pszText;
5607 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5608 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5609 *dispInfo.item.pszText = '\0';
5612 /* If we don't have all the requested info, query the application */
5613 if (dispInfo.item.mask != 0)
5615 dispInfo.item.iItem = lpLVItem->iItem;
5616 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5617 dispInfo.item.lParam = lpItem->lParam;
5618 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5619 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5622 /* we should not store values for subitems */
5623 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5625 /* Now, handle the iImage field */
5626 if (dispInfo.item.mask & LVIF_IMAGE)
5628 lpLVItem->iImage = dispInfo.item.iImage;
5629 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5630 pItemHdr->iImage = dispInfo.item.iImage;
5632 else if (lpLVItem->mask & LVIF_IMAGE)
5634 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5635 lpLVItem->iImage = pItemHdr->iImage;
5636 else
5637 lpLVItem->iImage = 0;
5640 /* The pszText field */
5641 if (dispInfo.item.mask & LVIF_TEXT)
5643 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5644 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5646 lpLVItem->pszText = dispInfo.item.pszText;
5648 else if (lpLVItem->mask & LVIF_TEXT)
5650 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5651 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5654 /* Next is the lParam field */
5655 if (dispInfo.item.mask & LVIF_PARAM)
5657 lpLVItem->lParam = dispInfo.item.lParam;
5658 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5659 lpItem->lParam = dispInfo.item.lParam;
5661 else if (lpLVItem->mask & LVIF_PARAM)
5662 lpLVItem->lParam = lpItem->lParam;
5664 /* if this is a subitem, we're done */
5665 if (isubitem) return TRUE;
5667 /* ... the state field (this one is different due to uCallbackmask) */
5668 if (lpLVItem->mask & LVIF_STATE)
5670 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5671 if (dispInfo.item.mask & LVIF_STATE)
5673 lpLVItem->state &= ~dispInfo.item.stateMask;
5674 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5676 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5678 lpLVItem->state &= ~LVIS_FOCUSED;
5679 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5680 lpLVItem->state |= LVIS_FOCUSED;
5682 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5684 lpLVItem->state &= ~LVIS_SELECTED;
5685 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5686 lpLVItem->state |= LVIS_SELECTED;
5690 /* and last, but not least, the indent field */
5691 if (lpLVItem->mask & LVIF_INDENT)
5692 lpLVItem->iIndent = lpItem->iIndent;
5694 return TRUE;
5697 /***
5698 * DESCRIPTION:
5699 * Retrieves item attributes.
5701 * PARAMETER(S):
5702 * [I] hwnd : window handle
5703 * [IO] lpLVItem : item info
5704 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5705 * if FALSE, then lpLVItem is a LPLVITEMA.
5707 * NOTE:
5708 * This is the external 'GetItem' interface -- it properly copies
5709 * the text in the provided buffer.
5711 * RETURN:
5712 * SUCCESS : TRUE
5713 * FAILURE : FALSE
5715 static BOOL LISTVIEW_GetItemExtT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5717 LPWSTR pszText;
5718 BOOL bResult;
5720 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5721 return FALSE;
5723 pszText = lpLVItem->pszText;
5724 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5725 if (bResult && lpLVItem->pszText != pszText)
5726 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5727 lpLVItem->pszText = pszText;
5729 return bResult;
5733 /***
5734 * DESCRIPTION:
5735 * Retrieves the position (upper-left) of the listview control item.
5736 * Note that for LVS_ICON style, the upper-left is that of the icon
5737 * and not the bounding box.
5739 * PARAMETER(S):
5740 * [I] infoPtr : valid pointer to the listview structure
5741 * [I] nItem : item index
5742 * [O] lpptPosition : coordinate information
5744 * RETURN:
5745 * SUCCESS : TRUE
5746 * FAILURE : FALSE
5748 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5750 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5751 POINT Origin;
5753 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5755 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5757 LISTVIEW_GetOrigin(infoPtr, &Origin);
5758 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5760 if (uView == LVS_ICON)
5762 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5763 lpptPosition->y += ICON_TOP_PADDING;
5765 lpptPosition->x += Origin.x;
5766 lpptPosition->y += Origin.y;
5768 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
5769 return TRUE;
5773 /***
5774 * DESCRIPTION:
5775 * Retrieves the bounding rectangle for a listview control item.
5777 * PARAMETER(S):
5778 * [I] infoPtr : valid pointer to the listview structure
5779 * [I] nItem : item index
5780 * [IO] lprc : bounding rectangle coordinates
5781 * lprc->left specifies the portion of the item for which the bounding
5782 * rectangle will be retrieved.
5784 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5785 * including the icon and label.
5787 * * For LVS_ICON
5788 * * Experiment shows that native control returns:
5789 * * width = min (48, length of text line)
5790 * * .left = position.x - (width - iconsize.cx)/2
5791 * * .right = .left + width
5792 * * height = #lines of text * ntmHeight + icon height + 8
5793 * * .top = position.y - 2
5794 * * .bottom = .top + height
5795 * * separation between items .y = itemSpacing.cy - height
5796 * * .x = itemSpacing.cx - width
5797 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5799 * * For LVS_ICON
5800 * * Experiment shows that native control returns:
5801 * * width = iconSize.cx + 16
5802 * * .left = position.x - (width - iconsize.cx)/2
5803 * * .right = .left + width
5804 * * height = iconSize.cy + 4
5805 * * .top = position.y - 2
5806 * * .bottom = .top + height
5807 * * separation between items .y = itemSpacing.cy - height
5808 * * .x = itemSpacing.cx - width
5809 * LVIR_LABEL Returns the bounding rectangle of the item text.
5811 * * For LVS_ICON
5812 * * Experiment shows that native control returns:
5813 * * width = text length
5814 * * .left = position.x - width/2
5815 * * .right = .left + width
5816 * * height = ntmH * linecount + 2
5817 * * .top = position.y + iconSize.cy + 6
5818 * * .bottom = .top + height
5819 * * separation between items .y = itemSpacing.cy - height
5820 * * .x = itemSpacing.cx - width
5821 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5822 * rectangles, but excludes columns in report view.
5824 * RETURN:
5825 * SUCCESS : TRUE
5826 * FAILURE : FALSE
5828 * NOTES
5829 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5830 * upon whether the window has the focus currently and on whether the item
5831 * is the one with the focus. Ensure that the control's record of which
5832 * item has the focus agrees with the items' records.
5834 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5836 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5837 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5838 BOOL doLabel = TRUE, oversizedBox = FALSE;
5839 POINT Position, Origin;
5840 LVITEMW lvItem;
5842 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5844 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5846 LISTVIEW_GetOrigin(infoPtr, &Origin);
5847 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5849 /* Be smart and try to figure out the minimum we have to do */
5850 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5851 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5852 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5853 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5854 oversizedBox = TRUE;
5856 /* get what we need from the item before hand, so we make
5857 * only one request. This can speed up things, if data
5858 * is stored on the app side */
5859 lvItem.mask = 0;
5860 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5861 if (doLabel) lvItem.mask |= LVIF_TEXT;
5862 lvItem.iItem = nItem;
5863 lvItem.iSubItem = 0;
5864 lvItem.pszText = szDispText;
5865 lvItem.cchTextMax = DISP_TEXT_SIZE;
5866 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5867 /* we got the state already up, simulate it here, to avoid a reget */
5868 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5870 lvItem.mask |= LVIF_STATE;
5871 lvItem.stateMask = LVIS_FOCUSED;
5872 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5875 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5876 lprc->left = LVIR_BOUNDS;
5877 switch(lprc->left)
5879 case LVIR_ICON:
5880 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
5881 break;
5883 case LVIR_LABEL:
5884 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc);
5885 break;
5887 case LVIR_BOUNDS:
5888 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
5889 break;
5891 case LVIR_SELECTBOUNDS:
5892 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL);
5893 break;
5895 default:
5896 WARN("Unknown value: %d\n", lprc->left);
5897 return FALSE;
5900 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5902 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
5904 return TRUE;
5907 /***
5908 * DESCRIPTION:
5909 * Retrieves the spacing between listview control items.
5911 * PARAMETER(S):
5912 * [I] infoPtr : valid pointer to the listview structure
5913 * [IO] lprc : rectangle to receive the output
5914 * on input, lprc->top = nSubItem
5915 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5917 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5918 * not only those of the first column.
5919 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5921 * RETURN:
5922 * TRUE: success
5923 * FALSE: failure
5925 static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5927 POINT Position;
5928 LVITEMW lvItem;
5929 INT nColumn;
5931 if (!lprc) return FALSE;
5933 nColumn = lprc->top;
5935 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
5936 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5937 if (lprc->top == 0)
5938 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5940 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5942 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5944 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5946 lvItem.mask = 0;
5947 lvItem.iItem = nItem;
5948 lvItem.iSubItem = nColumn;
5950 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5951 switch(lprc->left)
5953 case LVIR_ICON:
5954 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
5955 break;
5957 case LVIR_LABEL:
5958 case LVIR_BOUNDS:
5959 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
5960 break;
5962 default:
5963 ERR("Unknown bounds=%d\n", lprc->left);
5964 return FALSE;
5967 OffsetRect(lprc, Position.x, Position.y);
5968 return TRUE;
5972 /***
5973 * DESCRIPTION:
5974 * Retrieves the width of a label.
5976 * PARAMETER(S):
5977 * [I] infoPtr : valid pointer to the listview structure
5979 * RETURN:
5980 * SUCCESS : string width (in pixels)
5981 * FAILURE : zero
5983 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *infoPtr, INT nItem)
5985 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5986 LVITEMW lvItem;
5988 TRACE("(nItem=%d)\n", nItem);
5990 lvItem.mask = LVIF_TEXT;
5991 lvItem.iItem = nItem;
5992 lvItem.iSubItem = 0;
5993 lvItem.pszText = szDispText;
5994 lvItem.cchTextMax = DISP_TEXT_SIZE;
5995 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5997 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6000 /***
6001 * DESCRIPTION:
6002 * Retrieves the spacing between listview control items.
6004 * PARAMETER(S):
6005 * [I] infoPtr : valid pointer to the listview structure
6006 * [I] bSmall : flag for small or large icon
6008 * RETURN:
6009 * Horizontal + vertical spacing
6011 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall)
6013 LONG lResult;
6015 if (!bSmall)
6017 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6019 else
6021 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
6022 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6023 else
6024 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6026 return lResult;
6029 /***
6030 * DESCRIPTION:
6031 * Retrieves the state of a listview control item.
6033 * PARAMETER(S):
6034 * [I] infoPtr : valid pointer to the listview structure
6035 * [I] nItem : item index
6036 * [I] uMask : state mask
6038 * RETURN:
6039 * State specified by the mask.
6041 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
6043 LVITEMW lvItem;
6045 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6047 lvItem.iItem = nItem;
6048 lvItem.iSubItem = 0;
6049 lvItem.mask = LVIF_STATE;
6050 lvItem.stateMask = uMask;
6051 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6053 return lvItem.state & uMask;
6056 /***
6057 * DESCRIPTION:
6058 * Retrieves the text of a listview control item or subitem.
6060 * PARAMETER(S):
6061 * [I] hwnd : window handle
6062 * [I] nItem : item index
6063 * [IO] lpLVItem : item information
6064 * [I] isW : TRUE if lpLVItem is Unicode
6066 * RETURN:
6067 * SUCCESS : string length
6068 * FAILURE : 0
6070 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6072 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6074 lpLVItem->mask = LVIF_TEXT;
6075 lpLVItem->iItem = nItem;
6076 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
6078 return textlenT(lpLVItem->pszText, isW);
6081 /***
6082 * DESCRIPTION:
6083 * Searches for an item based on properties + relationships.
6085 * PARAMETER(S):
6086 * [I] infoPtr : valid pointer to the listview structure
6087 * [I] nItem : item index
6088 * [I] uFlags : relationship flag
6090 * RETURN:
6091 * SUCCESS : item index
6092 * FAILURE : -1
6094 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
6096 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6097 UINT uMask = 0;
6098 LVFINDINFOW lvFindInfo;
6099 INT nCountPerColumn;
6100 INT nCountPerRow;
6101 INT i;
6103 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
6104 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
6106 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6108 if (uFlags & LVNI_CUT)
6109 uMask |= LVIS_CUT;
6111 if (uFlags & LVNI_DROPHILITED)
6112 uMask |= LVIS_DROPHILITED;
6114 if (uFlags & LVNI_FOCUSED)
6115 uMask |= LVIS_FOCUSED;
6117 if (uFlags & LVNI_SELECTED)
6118 uMask |= LVIS_SELECTED;
6120 /* if we're asked for the focused item, that's only one,
6121 * so it's worth optimizing */
6122 if (uFlags & LVNI_FOCUSED)
6124 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
6125 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
6128 if (uFlags & LVNI_ABOVE)
6130 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6132 while (nItem >= 0)
6134 nItem--;
6135 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6136 return nItem;
6139 else
6141 /* Special case for autoarrange - move 'til the top of a list */
6142 if (is_autoarrange(infoPtr))
6144 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6145 while (nItem - nCountPerRow >= 0)
6147 nItem -= nCountPerRow;
6148 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6149 return nItem;
6151 return -1;
6153 lvFindInfo.flags = LVFI_NEARESTXY;
6154 lvFindInfo.vkDirection = VK_UP;
6155 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6156 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6158 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6159 return nItem;
6163 else if (uFlags & LVNI_BELOW)
6165 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6167 while (nItem < infoPtr->nItemCount)
6169 nItem++;
6170 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6171 return nItem;
6174 else
6176 /* Special case for autoarrange - move 'til the bottom of a list */
6177 if (is_autoarrange(infoPtr))
6179 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6180 while (nItem + nCountPerRow < infoPtr->nItemCount )
6182 nItem += nCountPerRow;
6183 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6184 return nItem;
6186 return -1;
6188 lvFindInfo.flags = LVFI_NEARESTXY;
6189 lvFindInfo.vkDirection = VK_DOWN;
6190 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6191 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6193 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6194 return nItem;
6198 else if (uFlags & LVNI_TOLEFT)
6200 if (uView == LVS_LIST)
6202 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6203 while (nItem - nCountPerColumn >= 0)
6205 nItem -= nCountPerColumn;
6206 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6207 return nItem;
6210 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6212 /* Special case for autoarrange - move 'ti the beginning of a row */
6213 if (is_autoarrange(infoPtr))
6215 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6216 while (nItem % nCountPerRow > 0)
6218 nItem --;
6219 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6220 return nItem;
6222 return -1;
6224 lvFindInfo.flags = LVFI_NEARESTXY;
6225 lvFindInfo.vkDirection = VK_LEFT;
6226 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6227 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6229 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6230 return nItem;
6234 else if (uFlags & LVNI_TORIGHT)
6236 if (uView == LVS_LIST)
6238 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6239 while (nItem + nCountPerColumn < infoPtr->nItemCount)
6241 nItem += nCountPerColumn;
6242 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6243 return nItem;
6246 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6248 /* Special case for autoarrange - move 'til the end of a row */
6249 if (is_autoarrange(infoPtr))
6251 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6252 while (nItem % nCountPerRow < nCountPerRow - 1 )
6254 nItem ++;
6255 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6256 return nItem;
6258 return -1;
6260 lvFindInfo.flags = LVFI_NEARESTXY;
6261 lvFindInfo.vkDirection = VK_RIGHT;
6262 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6263 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6265 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6266 return nItem;
6270 else
6272 nItem++;
6274 /* search by index */
6275 for (i = nItem; i < infoPtr->nItemCount; i++)
6277 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
6278 return i;
6282 return -1;
6285 /* LISTVIEW_GetNumberOfWorkAreas */
6287 /***
6288 * DESCRIPTION:
6289 * Retrieves the origin coordinates when in icon or small icon display mode.
6291 * PARAMETER(S):
6292 * [I] infoPtr : valid pointer to the listview structure
6293 * [O] lpptOrigin : coordinate information
6295 * RETURN:
6296 * None.
6298 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
6300 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6301 INT nHorzPos = 0, nVertPos = 0;
6302 SCROLLINFO scrollInfo;
6304 scrollInfo.cbSize = sizeof(SCROLLINFO);
6305 scrollInfo.fMask = SIF_POS;
6307 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6308 nHorzPos = scrollInfo.nPos;
6309 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6310 nVertPos = scrollInfo.nPos;
6312 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6314 lpptOrigin->x = infoPtr->rcList.left;
6315 lpptOrigin->y = infoPtr->rcList.top;
6316 if (uView == LVS_LIST)
6317 nHorzPos *= infoPtr->nItemWidth;
6318 else if (uView == LVS_REPORT)
6319 nVertPos *= infoPtr->nItemHeight;
6321 lpptOrigin->x -= nHorzPos;
6322 lpptOrigin->y -= nVertPos;
6324 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
6327 /***
6328 * DESCRIPTION:
6329 * Retrieves the width of a string.
6331 * PARAMETER(S):
6332 * [I] hwnd : window handle
6333 * [I] lpszText : text string to process
6334 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6336 * RETURN:
6337 * SUCCESS : string width (in pixels)
6338 * FAILURE : zero
6340 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6342 SIZE stringSize;
6344 stringSize.cx = 0;
6345 if (is_textT(lpszText, isW))
6347 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6348 HDC hdc = GetDC(infoPtr->hwndSelf);
6349 HFONT hOldFont = SelectObject(hdc, hFont);
6351 if (isW)
6352 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6353 else
6354 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6355 SelectObject(hdc, hOldFont);
6356 ReleaseDC(infoPtr->hwndSelf, hdc);
6358 return stringSize.cx;
6361 /***
6362 * DESCRIPTION:
6363 * Determines which listview item is located at the specified position.
6365 * PARAMETER(S):
6366 * [I] infoPtr : valid pointer to the listview structure
6367 * [IO] lpht : hit test information
6368 * [I] subitem : fill out iSubItem.
6369 * [I] select : return the index only if the hit selects the item
6371 * NOTE:
6372 * (mm 20001022): We must not allow iSubItem to be touched, for
6373 * an app might pass only a structure with space up to iItem!
6374 * (MS Office 97 does that for instance in the file open dialog)
6376 * RETURN:
6377 * SUCCESS : item index
6378 * FAILURE : -1
6380 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6382 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6383 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6384 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6385 POINT Origin, Position, opt;
6386 LVITEMW lvItem;
6387 ITERATOR i;
6388 INT iItem;
6390 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6392 lpht->flags = 0;
6393 lpht->iItem = -1;
6394 if (subitem) lpht->iSubItem = 0;
6396 if (infoPtr->rcList.left > lpht->pt.x)
6397 lpht->flags |= LVHT_TOLEFT;
6398 else if (infoPtr->rcList.right < lpht->pt.x)
6399 lpht->flags |= LVHT_TORIGHT;
6401 if (infoPtr->rcList.top > lpht->pt.y)
6402 lpht->flags |= LVHT_ABOVE;
6403 else if (infoPtr->rcList.bottom < lpht->pt.y)
6404 lpht->flags |= LVHT_BELOW;
6406 TRACE("lpht->flags=0x%x\n", lpht->flags);
6407 if (lpht->flags) return -1;
6409 lpht->flags |= LVHT_NOWHERE;
6411 LISTVIEW_GetOrigin(infoPtr, &Origin);
6413 /* first deal with the large items */
6414 rcSearch.left = lpht->pt.x;
6415 rcSearch.top = lpht->pt.y;
6416 rcSearch.right = rcSearch.left + 1;
6417 rcSearch.bottom = rcSearch.top + 1;
6419 iterator_frameditems(&i, infoPtr, &rcSearch);
6420 iterator_next(&i); /* go to first item in the sequence */
6421 iItem = i.nItem;
6422 iterator_destroy(&i);
6424 TRACE("lpht->iItem=%d\n", iItem);
6425 if (iItem == -1) return -1;
6427 lvItem.mask = LVIF_STATE | LVIF_TEXT;
6428 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6429 lvItem.stateMask = LVIS_STATEIMAGEMASK;
6430 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6431 lvItem.iItem = iItem;
6432 lvItem.iSubItem = 0;
6433 lvItem.pszText = szDispText;
6434 lvItem.cchTextMax = DISP_TEXT_SIZE;
6435 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6436 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6438 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
6439 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6440 opt.x = lpht->pt.x - Position.x - Origin.x;
6441 opt.y = lpht->pt.y - Position.y - Origin.y;
6443 if (uView == LVS_REPORT)
6444 rcBounds = rcBox;
6445 else
6447 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6448 UnionRect(&rcBounds, &rcBounds, &rcState);
6450 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6451 if (!PtInRect(&rcBounds, opt)) return -1;
6453 if (PtInRect(&rcIcon, opt))
6454 lpht->flags |= LVHT_ONITEMICON;
6455 else if (PtInRect(&rcLabel, opt))
6456 lpht->flags |= LVHT_ONITEMLABEL;
6457 else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6458 lpht->flags |= LVHT_ONITEMSTATEICON;
6459 if (lpht->flags & LVHT_ONITEM)
6460 lpht->flags &= ~LVHT_NOWHERE;
6462 TRACE("lpht->flags=0x%x\n", lpht->flags);
6463 if (uView == LVS_REPORT && subitem)
6465 INT j;
6467 rcBounds.right = rcBounds.left;
6468 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6470 rcBounds.left = rcBounds.right;
6471 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6472 if (PtInRect(&rcBounds, opt))
6474 lpht->iSubItem = j;
6475 break;
6480 if (select && !(uView == LVS_REPORT &&
6481 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6482 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6484 if (uView == LVS_REPORT)
6486 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6487 UnionRect(&rcBounds, &rcBounds, &rcState);
6489 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6491 return lpht->iItem = iItem;
6495 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6496 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6497 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6498 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6499 their own sort proc. when sending LVM_SORTITEMS.
6501 /* Platform SDK:
6502 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6504 LVS_SORTXXX must be specified,
6505 LVS_OWNERDRAW is not set,
6506 <item>.pszText is not LPSTR_TEXTCALLBACK.
6508 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6509 are sorted based on item text..."
6511 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6513 ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
6514 ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
6515 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6517 /* if we're sorting descending, negate the return value */
6518 return (((const LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6521 /***
6522 * DESCRIPTION:
6523 * Inserts a new item in the listview control.
6525 * PARAMETER(S):
6526 * [I] infoPtr : valid pointer to the listview structure
6527 * [I] lpLVItem : item information
6528 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6530 * RETURN:
6531 * SUCCESS : new item index
6532 * FAILURE : -1
6534 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6536 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6537 INT nItem;
6538 HDPA hdpaSubItems;
6539 NMLISTVIEW nmlv;
6540 ITEM_INFO *lpItem;
6541 BOOL is_sorted, has_changed;
6542 LVITEMW item;
6543 HWND hwndSelf = infoPtr->hwndSelf;
6545 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6547 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6549 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6550 if (!lpLVItem || lpLVItem->iSubItem) return -1;
6552 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6554 if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
6556 /* insert item in listview control data structure */
6557 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6558 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6560 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6561 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6563 if (lpLVItem->iItem < 0 && !is_sorted) return -1;
6565 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6566 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6567 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6568 if (nItem == -1) goto fail;
6569 infoPtr->nItemCount++;
6571 /* shift indices first so they don't get tangled */
6572 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6574 /* set the item attributes */
6575 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6577 /* full size structure expected - _WIN32IE >= 0x560 */
6578 item = *lpLVItem;
6580 else if (lpLVItem->mask & LVIF_INDENT)
6582 /* indent member expected - _WIN32IE >= 0x300 */
6583 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6585 else
6587 /* minimal structure expected */
6588 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6590 item.iItem = nItem;
6591 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6593 item.mask |= LVIF_STATE;
6594 item.stateMask |= LVIS_STATEIMAGEMASK;
6595 item.state &= ~LVIS_STATEIMAGEMASK;
6596 item.state |= INDEXTOSTATEIMAGEMASK(1);
6598 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6600 /* if we're sorted, sort the list, and update the index */
6601 if (is_sorted)
6603 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6604 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6605 assert(nItem != -1);
6608 /* make room for the position, if we are in the right mode */
6609 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6611 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6612 goto undo;
6613 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6615 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6616 goto undo;
6620 /* send LVN_INSERTITEM notification */
6621 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6622 nmlv.iItem = nItem;
6623 nmlv.lParam = lpItem->lParam;
6624 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6625 if (!IsWindow(hwndSelf))
6626 return -1;
6628 /* align items (set position of each item) */
6629 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6631 POINT pt;
6633 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6634 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6635 else
6636 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6638 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6641 /* now is the invalidation fun */
6642 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6643 return nItem;
6645 undo:
6646 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6647 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6648 infoPtr->nItemCount--;
6649 fail:
6650 DPA_DeletePtr(hdpaSubItems, 0);
6651 DPA_Destroy (hdpaSubItems);
6652 Free (lpItem);
6653 return -1;
6656 /***
6657 * DESCRIPTION:
6658 * Redraws a range of items.
6660 * PARAMETER(S):
6661 * [I] infoPtr : valid pointer to the listview structure
6662 * [I] nFirst : first item
6663 * [I] nLast : last item
6665 * RETURN:
6666 * SUCCESS : TRUE
6667 * FAILURE : FALSE
6669 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6671 INT i;
6673 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6674 max(nFirst, nLast) >= infoPtr->nItemCount)
6675 return FALSE;
6677 for (i = nFirst; i <= nLast; i++)
6678 LISTVIEW_InvalidateItem(infoPtr, i);
6680 return TRUE;
6683 /***
6684 * DESCRIPTION:
6685 * Scroll the content of a listview.
6687 * PARAMETER(S):
6688 * [I] infoPtr : valid pointer to the listview structure
6689 * [I] dx : horizontal scroll amount in pixels
6690 * [I] dy : vertical scroll amount in pixels
6692 * RETURN:
6693 * SUCCESS : TRUE
6694 * FAILURE : FALSE
6696 * COMMENTS:
6697 * If the control is in report mode (LVS_REPORT) the control can
6698 * be scrolled only in line increments. "dy" will be rounded to the
6699 * nearest number of pixels that are a whole line. Ex: if line height
6700 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6701 * is passed, then the scroll will be 0. (per MSDN 7/2002)
6703 * For: (per experimentation with native control and CSpy ListView)
6704 * LVS_ICON dy=1 = 1 pixel (vertical only)
6705 * dx ignored
6706 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6707 * dx ignored
6708 * LVS_LIST dx=1 = 1 column (horizontal only)
6709 * but will only scroll 1 column per message
6710 * no matter what the value.
6711 * dy must be 0 or FALSE returned.
6712 * LVS_REPORT dx=1 = 1 pixel
6713 * dy= see above
6716 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6718 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6719 case LVS_REPORT:
6720 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6721 dy /= infoPtr->nItemHeight;
6722 break;
6723 case LVS_LIST:
6724 if (dy != 0) return FALSE;
6725 break;
6726 default: /* icon */
6727 dx = 0;
6728 break;
6731 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6732 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6734 return TRUE;
6737 /***
6738 * DESCRIPTION:
6739 * Sets the background color.
6741 * PARAMETER(S):
6742 * [I] infoPtr : valid pointer to the listview structure
6743 * [I] clrBk : background color
6745 * RETURN:
6746 * SUCCESS : TRUE
6747 * FAILURE : FALSE
6749 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6751 TRACE("(clrBk=%x)\n", clrBk);
6753 if(infoPtr->clrBk != clrBk) {
6754 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6755 infoPtr->clrBk = clrBk;
6756 if (clrBk == CLR_NONE)
6757 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6758 else
6759 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6760 LISTVIEW_InvalidateList(infoPtr);
6763 return TRUE;
6766 /* LISTVIEW_SetBkImage */
6768 /*** Helper for {Insert,Set}ColumnT *only* */
6769 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
6770 const LVCOLUMNW *lpColumn, BOOL isW)
6772 if (lpColumn->mask & LVCF_FMT)
6774 /* format member is valid */
6775 lphdi->mask |= HDI_FORMAT;
6777 /* set text alignment (leftmost column must be left-aligned) */
6778 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6779 lphdi->fmt |= HDF_LEFT;
6780 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6781 lphdi->fmt |= HDF_RIGHT;
6782 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6783 lphdi->fmt |= HDF_CENTER;
6785 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6786 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6788 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6790 lphdi->fmt |= HDF_IMAGE;
6791 lphdi->iImage = I_IMAGECALLBACK;
6795 if (lpColumn->mask & LVCF_WIDTH)
6797 lphdi->mask |= HDI_WIDTH;
6798 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6800 /* make it fill the remainder of the controls width */
6801 RECT rcHeader;
6802 INT item_index;
6804 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6806 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6807 lphdi->cxy += rcHeader.right - rcHeader.left;
6810 /* retrieve the layout of the header */
6811 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6812 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
6814 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6816 else
6817 lphdi->cxy = lpColumn->cx;
6820 if (lpColumn->mask & LVCF_TEXT)
6822 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6823 lphdi->fmt |= HDF_STRING;
6824 lphdi->pszText = lpColumn->pszText;
6825 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6828 if (lpColumn->mask & LVCF_IMAGE)
6830 lphdi->mask |= HDI_IMAGE;
6831 lphdi->iImage = lpColumn->iImage;
6834 if (lpColumn->mask & LVCF_ORDER)
6836 lphdi->mask |= HDI_ORDER;
6837 lphdi->iOrder = lpColumn->iOrder;
6842 /***
6843 * DESCRIPTION:
6844 * Inserts a new column.
6846 * PARAMETER(S):
6847 * [I] infoPtr : valid pointer to the listview structure
6848 * [I] nColumn : column index
6849 * [I] lpColumn : column information
6850 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6852 * RETURN:
6853 * SUCCESS : new column index
6854 * FAILURE : -1
6856 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6857 const LVCOLUMNW *lpColumn, BOOL isW)
6859 COLUMN_INFO *lpColumnInfo;
6860 INT nNewColumn;
6861 HDITEMW hdi;
6863 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6865 if (!lpColumn || nColumn < 0) return -1;
6866 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6868 ZeroMemory(&hdi, sizeof(HDITEMW));
6869 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6872 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
6873 * (can be seen in SPY) otherwise column never gets added.
6875 if (!(lpColumn->mask & LVCF_WIDTH)) {
6876 hdi.mask |= HDI_WIDTH;
6877 hdi.cxy = 10;
6881 * when the iSubItem is available Windows copies it to the header lParam. It seems
6882 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
6884 if (lpColumn->mask & LVCF_SUBITEM)
6886 hdi.mask |= HDI_LPARAM;
6887 hdi.lParam = lpColumn->iSubItem;
6890 /* insert item in header control */
6891 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6892 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6893 (WPARAM)nColumn, (LPARAM)&hdi);
6894 if (nNewColumn == -1) return -1;
6895 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6897 /* create our own column info */
6898 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6899 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6901 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6902 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6904 /* now we have to actually adjust the data */
6905 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6907 SUBITEM_INFO *lpSubItem;
6908 HDPA hdpaSubItems;
6909 INT nItem, i;
6911 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6913 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
6914 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6916 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
6917 if (lpSubItem->iSubItem >= nNewColumn)
6918 lpSubItem->iSubItem++;
6923 /* make space for the new column */
6924 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6925 LISTVIEW_UpdateItemSize(infoPtr);
6927 return nNewColumn;
6929 fail:
6930 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6931 if (lpColumnInfo)
6933 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6934 Free(lpColumnInfo);
6936 return -1;
6939 /***
6940 * DESCRIPTION:
6941 * Sets the attributes of a header item.
6943 * PARAMETER(S):
6944 * [I] infoPtr : valid pointer to the listview structure
6945 * [I] nColumn : column index
6946 * [I] lpColumn : column attributes
6947 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6949 * RETURN:
6950 * SUCCESS : TRUE
6951 * FAILURE : FALSE
6953 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
6954 const LVCOLUMNW *lpColumn, BOOL isW)
6956 HDITEMW hdi, hdiget;
6957 BOOL bResult;
6959 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6961 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6963 ZeroMemory(&hdi, sizeof(HDITEMW));
6964 if (lpColumn->mask & LVCF_FMT)
6966 hdi.mask |= HDI_FORMAT;
6967 hdiget.mask = HDI_FORMAT;
6968 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6969 hdi.fmt = hdiget.fmt & HDF_STRING;
6971 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6973 /* set header item attributes */
6974 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6975 if (!bResult) return FALSE;
6977 if (lpColumn->mask & LVCF_FMT)
6979 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6980 int oldFmt = lpColumnInfo->fmt;
6982 lpColumnInfo->fmt = lpColumn->fmt;
6983 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6985 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6986 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6990 return TRUE;
6993 /***
6994 * DESCRIPTION:
6995 * Sets the column order array
6997 * PARAMETERS:
6998 * [I] infoPtr : valid pointer to the listview structure
6999 * [I] iCount : number of elements in column order array
7000 * [I] lpiArray : pointer to column order array
7002 * RETURN:
7003 * SUCCESS : TRUE
7004 * FAILURE : FALSE
7006 static BOOL LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
7008 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7010 if (!lpiArray)
7011 return FALSE;
7013 return TRUE;
7017 /***
7018 * DESCRIPTION:
7019 * Sets the width of a column
7021 * PARAMETERS:
7022 * [I] infoPtr : valid pointer to the listview structure
7023 * [I] nColumn : column index
7024 * [I] cx : column width
7026 * RETURN:
7027 * SUCCESS : TRUE
7028 * FAILURE : FALSE
7030 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
7032 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7033 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
7034 INT max_cx = 0;
7035 HDITEMW hdi;
7037 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
7039 /* set column width only if in report or list mode */
7040 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
7042 /* take care of invalid cx values */
7043 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
7044 else if (uView == LVS_LIST && cx < 1) return FALSE;
7046 /* resize all columns if in LVS_LIST mode */
7047 if(uView == LVS_LIST)
7049 infoPtr->nItemWidth = cx;
7050 LISTVIEW_InvalidateList(infoPtr);
7051 return TRUE;
7054 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7056 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
7058 INT nLabelWidth;
7059 LVITEMW lvItem;
7061 lvItem.mask = LVIF_TEXT;
7062 lvItem.iItem = 0;
7063 lvItem.iSubItem = nColumn;
7064 lvItem.pszText = szDispText;
7065 lvItem.cchTextMax = DISP_TEXT_SIZE;
7066 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7068 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
7069 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
7070 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
7072 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
7073 max_cx += infoPtr->iconSize.cx;
7074 max_cx += TRAILING_LABEL_PADDING;
7077 /* autosize based on listview items width */
7078 if(cx == LVSCW_AUTOSIZE)
7079 cx = max_cx;
7080 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7082 /* if iCol is the last column make it fill the remainder of the controls width */
7083 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
7085 RECT rcHeader;
7086 POINT Origin;
7088 LISTVIEW_GetOrigin(infoPtr, &Origin);
7089 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
7091 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
7093 else
7095 /* Despite what the MS docs say, if this is not the last
7096 column, then MS resizes the column to the width of the
7097 largest text string in the column, including headers
7098 and items. This is different from LVSCW_AUTOSIZE in that
7099 LVSCW_AUTOSIZE ignores the header string length. */
7100 cx = 0;
7102 /* retrieve header text */
7103 hdi.mask = HDI_TEXT;
7104 hdi.cchTextMax = DISP_TEXT_SIZE;
7105 hdi.pszText = szDispText;
7106 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
7108 HDC hdc = GetDC(infoPtr->hwndSelf);
7109 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
7110 SIZE size;
7112 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
7113 cx = size.cx + TRAILING_HEADER_PADDING;
7114 /* FIXME: Take into account the header image, if one is present */
7115 SelectObject(hdc, old_font);
7116 ReleaseDC(infoPtr->hwndSelf, hdc);
7118 cx = max (cx, max_cx);
7122 if (cx < 0) return FALSE;
7124 /* call header to update the column change */
7125 hdi.mask = HDI_WIDTH;
7126 hdi.cxy = cx;
7127 TRACE("hdi.cxy=%d\n", hdi.cxy);
7128 return Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7131 /***
7132 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
7135 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
7137 HDC hdc_wnd, hdc;
7138 HBITMAP hbm_im, hbm_mask, hbm_orig;
7139 RECT rc;
7140 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
7141 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
7142 HIMAGELIST himl;
7144 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
7145 ILC_COLOR | ILC_MASK, 2, 2);
7146 hdc_wnd = GetDC(infoPtr->hwndSelf);
7147 hdc = CreateCompatibleDC(hdc_wnd);
7148 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
7149 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
7150 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
7152 rc.left = rc.top = 0;
7153 rc.right = GetSystemMetrics(SM_CXSMICON);
7154 rc.bottom = GetSystemMetrics(SM_CYSMICON);
7156 hbm_orig = SelectObject(hdc, hbm_mask);
7157 FillRect(hdc, &rc, hbr_white);
7158 InflateRect(&rc, -3, -3);
7159 FillRect(hdc, &rc, hbr_black);
7161 SelectObject(hdc, hbm_im);
7162 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
7163 SelectObject(hdc, hbm_orig);
7164 ImageList_Add(himl, hbm_im, hbm_mask);
7166 SelectObject(hdc, hbm_im);
7167 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
7168 SelectObject(hdc, hbm_orig);
7169 ImageList_Add(himl, hbm_im, hbm_mask);
7171 DeleteObject(hbm_mask);
7172 DeleteObject(hbm_im);
7173 DeleteDC(hdc);
7175 return himl;
7178 /***
7179 * DESCRIPTION:
7180 * Sets the extended listview style.
7182 * PARAMETERS:
7183 * [I] infoPtr : valid pointer to the listview structure
7184 * [I] dwMask : mask
7185 * [I] dwStyle : style
7187 * RETURN:
7188 * SUCCESS : previous style
7189 * FAILURE : 0
7191 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwExStyle)
7193 DWORD dwOldExStyle = infoPtr->dwLvExStyle;
7195 /* set new style */
7196 if (dwMask)
7197 infoPtr->dwLvExStyle = (dwOldExStyle & ~dwMask) | (dwExStyle & dwMask);
7198 else
7199 infoPtr->dwLvExStyle = dwExStyle;
7201 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_CHECKBOXES)
7203 HIMAGELIST himl = 0;
7204 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7206 LVITEMW item;
7207 item.mask = LVIF_STATE;
7208 item.stateMask = LVIS_STATEIMAGEMASK;
7209 item.state = INDEXTOSTATEIMAGEMASK(1);
7210 LISTVIEW_SetItemState(infoPtr, -1, &item);
7212 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
7214 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
7217 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_HEADERDRAGDROP)
7219 DWORD dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
7220 if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
7221 dwStyle |= HDS_DRAGDROP;
7222 else
7223 dwStyle &= ~HDS_DRAGDROP;
7224 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
7227 /* GRIDLINES adds decoration at top so changes sizes */
7228 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_GRIDLINES)
7230 LISTVIEW_UpdateSize(infoPtr);
7234 LISTVIEW_InvalidateList(infoPtr);
7235 return dwOldExStyle;
7238 /***
7239 * DESCRIPTION:
7240 * Sets the new hot cursor used during hot tracking and hover selection.
7242 * PARAMETER(S):
7243 * [I] infoPtr : valid pointer to the listview structure
7244 * [I] hCursor : the new hot cursor handle
7246 * RETURN:
7247 * Returns the previous hot cursor
7249 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
7251 HCURSOR oldCursor = infoPtr->hHotCursor;
7253 infoPtr->hHotCursor = hCursor;
7255 return oldCursor;
7259 /***
7260 * DESCRIPTION:
7261 * Sets the hot item index.
7263 * PARAMETERS:
7264 * [I] infoPtr : valid pointer to the listview structure
7265 * [I] iIndex : index
7267 * RETURN:
7268 * SUCCESS : previous hot item index
7269 * FAILURE : -1 (no hot item)
7271 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
7273 INT iOldIndex = infoPtr->nHotItem;
7275 infoPtr->nHotItem = iIndex;
7277 return iOldIndex;
7281 /***
7282 * DESCRIPTION:
7283 * Sets the amount of time the cursor must hover over an item before it is selected.
7285 * PARAMETER(S):
7286 * [I] infoPtr : valid pointer to the listview structure
7287 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7289 * RETURN:
7290 * Returns the previous hover time
7292 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
7294 DWORD oldHoverTime = infoPtr->dwHoverTime;
7296 infoPtr->dwHoverTime = dwHoverTime;
7298 return oldHoverTime;
7301 /***
7302 * DESCRIPTION:
7303 * Sets spacing for icons of LVS_ICON style.
7305 * PARAMETER(S):
7306 * [I] infoPtr : valid pointer to the listview structure
7307 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7308 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7310 * RETURN:
7311 * MAKELONG(oldcx, oldcy)
7313 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
7315 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7316 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7318 TRACE("requested=(%d,%d)\n", cx, cy);
7320 /* this is supported only for LVS_ICON style */
7321 if (uView != LVS_ICON) return oldspacing;
7323 /* set to defaults, if instructed to */
7324 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
7325 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
7327 /* if 0 then compute width
7328 * FIXME: Should scan each item and determine max width of
7329 * icon or label, then make that the width */
7330 if (cx == 0)
7331 cx = infoPtr->iconSpacing.cx;
7333 /* if 0 then compute height */
7334 if (cy == 0)
7335 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
7336 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
7339 infoPtr->iconSpacing.cx = cx;
7340 infoPtr->iconSpacing.cy = cy;
7342 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7343 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
7344 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7345 infoPtr->ntmHeight);
7347 /* these depend on the iconSpacing */
7348 LISTVIEW_UpdateItemSize(infoPtr);
7350 return oldspacing;
7353 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
7355 INT cx, cy;
7357 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
7359 size->cx = cx;
7360 size->cy = cy;
7362 else
7364 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
7365 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7369 /***
7370 * DESCRIPTION:
7371 * Sets image lists.
7373 * PARAMETER(S):
7374 * [I] infoPtr : valid pointer to the listview structure
7375 * [I] nType : image list type
7376 * [I] himl : image list handle
7378 * RETURN:
7379 * SUCCESS : old image list
7380 * FAILURE : NULL
7382 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7384 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7385 INT oldHeight = infoPtr->nItemHeight;
7386 HIMAGELIST himlOld = 0;
7388 TRACE("(nType=%d, himl=%p\n", nType, himl);
7390 switch (nType)
7392 case LVSIL_NORMAL:
7393 himlOld = infoPtr->himlNormal;
7394 infoPtr->himlNormal = himl;
7395 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7396 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7397 break;
7399 case LVSIL_SMALL:
7400 himlOld = infoPtr->himlSmall;
7401 infoPtr->himlSmall = himl;
7402 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7403 break;
7405 case LVSIL_STATE:
7406 himlOld = infoPtr->himlState;
7407 infoPtr->himlState = himl;
7408 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7409 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7410 break;
7412 default:
7413 ERR("Unknown icon type=%d\n", nType);
7414 return NULL;
7417 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7418 if (infoPtr->nItemHeight != oldHeight)
7419 LISTVIEW_UpdateScroll(infoPtr);
7421 return himlOld;
7424 /***
7425 * DESCRIPTION:
7426 * Preallocates memory (does *not* set the actual count of items !)
7428 * PARAMETER(S):
7429 * [I] infoPtr : valid pointer to the listview structure
7430 * [I] nItems : item count (projected number of items to allocate)
7431 * [I] dwFlags : update flags
7433 * RETURN:
7434 * SUCCESS : TRUE
7435 * FAILURE : FALSE
7437 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7439 TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
7441 if (infoPtr->dwStyle & LVS_OWNERDATA)
7443 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7444 INT nOldCount = infoPtr->nItemCount;
7446 if (nItems < nOldCount)
7448 RANGE range = { nItems, nOldCount };
7449 ranges_del(infoPtr->selectionRanges, range);
7450 if (infoPtr->nFocusedItem >= nItems)
7452 infoPtr->nFocusedItem = -1;
7453 SetRectEmpty(&infoPtr->rcFocus);
7457 infoPtr->nItemCount = nItems;
7458 LISTVIEW_UpdateScroll(infoPtr);
7460 /* the flags are valid only in ownerdata report and list modes */
7461 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7463 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7464 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7466 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7467 LISTVIEW_InvalidateList(infoPtr);
7468 else
7470 INT nFrom, nTo;
7471 POINT Origin;
7472 RECT rcErase;
7474 LISTVIEW_GetOrigin(infoPtr, &Origin);
7475 nFrom = min(nOldCount, nItems);
7476 nTo = max(nOldCount, nItems);
7478 if (uView == LVS_REPORT)
7480 rcErase.left = 0;
7481 rcErase.top = nFrom * infoPtr->nItemHeight;
7482 rcErase.right = infoPtr->nItemWidth;
7483 rcErase.bottom = nTo * infoPtr->nItemHeight;
7484 OffsetRect(&rcErase, Origin.x, Origin.y);
7485 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7486 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7488 else /* LVS_LIST */
7490 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7492 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7493 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7494 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7495 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7496 OffsetRect(&rcErase, Origin.x, Origin.y);
7497 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7498 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7500 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7501 rcErase.top = 0;
7502 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7503 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7504 OffsetRect(&rcErase, Origin.x, Origin.y);
7505 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7506 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7510 else
7512 /* According to MSDN for non-LVS_OWNERDATA this is just
7513 * a performance issue. The control allocates its internal
7514 * data structures for the number of items specified. It
7515 * cuts down on the number of memory allocations. Therefore
7516 * we will just issue a WARN here
7518 WARN("for non-ownerdata performance option not implemented.\n");
7521 return TRUE;
7524 /***
7525 * DESCRIPTION:
7526 * Sets the position of an item.
7528 * PARAMETER(S):
7529 * [I] infoPtr : valid pointer to the listview structure
7530 * [I] nItem : item index
7531 * [I] pt : coordinate
7533 * RETURN:
7534 * SUCCESS : TRUE
7535 * FAILURE : FALSE
7537 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7539 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7540 POINT Origin;
7542 TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7544 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7545 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7547 LISTVIEW_GetOrigin(infoPtr, &Origin);
7549 /* This point value seems to be an undocumented feature.
7550 * The best guess is that it means either at the origin,
7551 * or at true beginning of the list. I will assume the origin. */
7552 if ((pt.x == -1) && (pt.y == -1))
7553 pt = Origin;
7555 if (uView == LVS_ICON)
7557 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7558 pt.y -= ICON_TOP_PADDING;
7560 pt.x -= Origin.x;
7561 pt.y -= Origin.y;
7563 infoPtr->bAutoarrange = FALSE;
7565 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7568 /***
7569 * DESCRIPTION:
7570 * Sets the state of one or many items.
7572 * PARAMETER(S):
7573 * [I] infoPtr : valid pointer to the listview structure
7574 * [I] nItem : item index
7575 * [I] lpLVItem : item or subitem info
7577 * RETURN:
7578 * SUCCESS : TRUE
7579 * FAILURE : FALSE
7581 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7583 BOOL bResult = TRUE;
7584 LVITEMW lvItem;
7586 lvItem.iItem = nItem;
7587 lvItem.iSubItem = 0;
7588 lvItem.mask = LVIF_STATE;
7589 lvItem.state = lpLVItem->state;
7590 lvItem.stateMask = lpLVItem->stateMask;
7591 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7593 if (nItem == -1)
7595 /* apply to all items */
7596 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7597 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7599 else
7600 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7603 * Update selection mark
7605 * Investigation on windows 2k showed that selection mark was updated
7606 * whenever a new selection was made, but if the selected item was
7607 * unselected it was not updated.
7609 * we are probably still not 100% accurate, but this at least sets the
7610 * proper selection mark when it is needed
7613 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7614 (infoPtr->nSelectionMark == -1))
7616 int i;
7617 for (i = 0; i < infoPtr->nItemCount; i++)
7619 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7621 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7623 infoPtr->nSelectionMark = i;
7624 break;
7627 else if (ranges_contain(infoPtr->selectionRanges, i))
7629 infoPtr->nSelectionMark = i;
7630 break;
7635 return bResult;
7638 /***
7639 * DESCRIPTION:
7640 * Sets the text of an item or subitem.
7642 * PARAMETER(S):
7643 * [I] hwnd : window handle
7644 * [I] nItem : item index
7645 * [I] lpLVItem : item or subitem info
7646 * [I] isW : TRUE if input is Unicode
7648 * RETURN:
7649 * SUCCESS : TRUE
7650 * FAILURE : FALSE
7652 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7654 LVITEMW lvItem;
7656 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7658 lvItem.iItem = nItem;
7659 lvItem.iSubItem = lpLVItem->iSubItem;
7660 lvItem.mask = LVIF_TEXT;
7661 lvItem.pszText = lpLVItem->pszText;
7662 lvItem.cchTextMax = lpLVItem->cchTextMax;
7664 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7666 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7669 /***
7670 * DESCRIPTION:
7671 * Set item index that marks the start of a multiple selection.
7673 * PARAMETER(S):
7674 * [I] infoPtr : valid pointer to the listview structure
7675 * [I] nIndex : index
7677 * RETURN:
7678 * Index number or -1 if there is no selection mark.
7680 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7682 INT nOldIndex = infoPtr->nSelectionMark;
7684 TRACE("(nIndex=%d)\n", nIndex);
7686 infoPtr->nSelectionMark = nIndex;
7688 return nOldIndex;
7691 /***
7692 * DESCRIPTION:
7693 * Sets the text background color.
7695 * PARAMETER(S):
7696 * [I] infoPtr : valid pointer to the listview structure
7697 * [I] clrTextBk : text background color
7699 * RETURN:
7700 * SUCCESS : TRUE
7701 * FAILURE : FALSE
7703 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7705 TRACE("(clrTextBk=%x)\n", clrTextBk);
7707 if (infoPtr->clrTextBk != clrTextBk)
7709 infoPtr->clrTextBk = clrTextBk;
7710 LISTVIEW_InvalidateList(infoPtr);
7713 return TRUE;
7716 /***
7717 * DESCRIPTION:
7718 * Sets the text foreground color.
7720 * PARAMETER(S):
7721 * [I] infoPtr : valid pointer to the listview structure
7722 * [I] clrText : text color
7724 * RETURN:
7725 * SUCCESS : TRUE
7726 * FAILURE : FALSE
7728 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7730 TRACE("(clrText=%x)\n", clrText);
7732 if (infoPtr->clrText != clrText)
7734 infoPtr->clrText = clrText;
7735 LISTVIEW_InvalidateList(infoPtr);
7738 return TRUE;
7741 /***
7742 * DESCRIPTION:
7743 * Determines which listview item is located at the specified position.
7745 * PARAMETER(S):
7746 * [I] infoPtr : valid pointer to the listview structure
7747 * [I] hwndNewToolTip : handle to new ToolTip
7749 * RETURN:
7750 * old tool tip
7752 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7754 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7755 infoPtr->hwndToolTip = hwndNewToolTip;
7756 return hwndOldToolTip;
7760 * DESCRIPTION:
7761 * sets the Unicode character format flag for the control
7762 * PARAMETER(S):
7763 * [I] infoPtr :valid pointer to the listview structure
7764 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
7766 * RETURN:
7767 * Old Unicode Format
7769 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL fUnicode)
7771 BOOL rc = infoPtr->notifyFormat;
7772 infoPtr->notifyFormat = (fUnicode)?NFR_UNICODE:NFR_ANSI;
7773 return rc;
7776 /* LISTVIEW_SetWorkAreas */
7778 /***
7779 * DESCRIPTION:
7780 * Callback internally used by LISTVIEW_SortItems()
7782 * PARAMETER(S):
7783 * [I] first : pointer to first ITEM_INFO to compare
7784 * [I] second : pointer to second ITEM_INFO to compare
7785 * [I] lParam : HWND of control
7787 * RETURN:
7788 * if first comes before second : negative
7789 * if first comes after second : positive
7790 * if first and second are equivalent : zero
7792 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7794 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7795 ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
7796 ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
7798 /* Forward the call to the client defined callback */
7799 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7802 /***
7803 * DESCRIPTION:
7804 * Sorts the listview items.
7806 * PARAMETER(S):
7807 * [I] infoPtr : valid pointer to the listview structure
7808 * [I] pfnCompare : application-defined value
7809 * [I] lParamSort : pointer to comparison callback
7811 * RETURN:
7812 * SUCCESS : TRUE
7813 * FAILURE : FALSE
7815 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7817 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7818 HDPA hdpaSubItems;
7819 ITEM_INFO *lpItem;
7820 LPVOID selectionMarkItem;
7821 LVITEMW item;
7822 int i;
7824 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7826 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7828 if (!pfnCompare) return FALSE;
7829 if (!infoPtr->hdpaItems) return FALSE;
7831 /* if there are 0 or 1 items, there is no need to sort */
7832 if (infoPtr->nItemCount < 2) return TRUE;
7834 if (infoPtr->nFocusedItem >= 0)
7836 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7837 lpItem = DPA_GetPtr(hdpaSubItems, 0);
7838 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7840 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7841 /* clear the lpItem->state for non-selected ones */
7842 /* remove the selection ranges */
7844 infoPtr->pfnCompare = pfnCompare;
7845 infoPtr->lParamSort = lParamSort;
7846 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7848 /* Adjust selections and indices so that they are the way they should
7849 * be after the sort (otherwise, the list items move around, but
7850 * whatever is at the item's previous original position will be
7851 * selected instead)
7853 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7854 for (i=0; i < infoPtr->nItemCount; i++)
7856 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
7857 lpItem = DPA_GetPtr(hdpaSubItems, 0);
7859 if (lpItem->state & LVIS_SELECTED)
7861 item.state = LVIS_SELECTED;
7862 item.stateMask = LVIS_SELECTED;
7863 LISTVIEW_SetItemState(infoPtr, i, &item);
7865 if (lpItem->state & LVIS_FOCUSED)
7867 infoPtr->nFocusedItem = i;
7868 lpItem->state &= ~LVIS_FOCUSED;
7871 if (selectionMarkItem != NULL)
7872 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7873 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7875 /* refresh the display */
7876 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7877 LISTVIEW_InvalidateList(infoPtr);
7879 return TRUE;
7882 /***
7883 * DESCRIPTION:
7884 * Update theme handle after a theme change.
7886 * PARAMETER(S):
7887 * [I] infoPtr : valid pointer to the listview structure
7889 * RETURN:
7890 * SUCCESS : 0
7891 * FAILURE : something else
7893 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr)
7895 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7896 CloseThemeData(theme);
7897 OpenThemeData(infoPtr->hwndSelf, themeClass);
7898 return 0;
7901 /***
7902 * DESCRIPTION:
7903 * Updates an items or rearranges the listview control.
7905 * PARAMETER(S):
7906 * [I] infoPtr : valid pointer to the listview structure
7907 * [I] nItem : item index
7909 * RETURN:
7910 * SUCCESS : TRUE
7911 * FAILURE : FALSE
7913 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7915 TRACE("(nItem=%d)\n", nItem);
7917 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7919 /* rearrange with default alignment style */
7920 if (is_autoarrange(infoPtr))
7921 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7922 else
7923 LISTVIEW_InvalidateItem(infoPtr, nItem);
7925 return TRUE;
7928 /***
7929 * DESCRIPTION:
7930 * Draw the track line at the place defined in the infoPtr structure.
7931 * The line is drawn with a XOR pen so drawing the line for the second time
7932 * in the same place erases the line.
7934 * PARAMETER(S):
7935 * [I] infoPtr : valid pointer to the listview structure
7937 * RETURN:
7938 * SUCCESS : TRUE
7939 * FAILURE : FALSE
7941 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
7943 HPEN hOldPen;
7944 HDC hdc;
7945 INT oldROP;
7947 if (infoPtr->xTrackLine == -1)
7948 return FALSE;
7950 if (!(hdc = GetDC(infoPtr->hwndSelf)))
7951 return FALSE;
7952 hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
7953 oldROP = SetROP2(hdc, R2_XORPEN);
7954 MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
7955 LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
7956 SetROP2(hdc, oldROP);
7957 SelectObject(hdc, hOldPen);
7958 ReleaseDC(infoPtr->hwndSelf, hdc);
7959 return TRUE;
7962 /***
7963 * DESCRIPTION:
7964 * Called when an edit control should be displayed. This function is called after
7965 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
7967 * PARAMETER(S):
7968 * [I] hwnd : Handle to the listview
7969 * [I] uMsg : WM_TIMER (ignored)
7970 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
7971 * [I] dwTimer : The elapsed time (ignored)
7973 * RETURN:
7974 * None.
7976 static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
7978 DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
7979 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
7981 KillTimer(hwnd, idEvent);
7982 editItem->fEnabled = FALSE;
7983 /* check if the item is still selected */
7984 if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
7985 LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
7988 /***
7989 * DESCRIPTION:
7990 * Creates the listview control - the WM_NCCREATE phase.
7992 * PARAMETER(S):
7993 * [I] hwnd : window handle
7994 * [I] lpcs : the create parameters
7996 * RETURN:
7997 * Success: TRUE
7998 * Failure: FALSE
8000 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
8002 LISTVIEW_INFO *infoPtr;
8003 LOGFONTW logFont;
8005 TRACE("(lpcs=%p)\n", lpcs);
8007 /* initialize info pointer */
8008 infoPtr = Alloc(sizeof(LISTVIEW_INFO));
8009 if (!infoPtr) return FALSE;
8011 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
8013 infoPtr->hwndSelf = hwnd;
8014 infoPtr->dwStyle = lpcs->style; /* Note: may be changed in WM_CREATE */
8015 /* determine the type of structures to use */
8016 infoPtr->hwndNotify = lpcs->hwndParent;
8017 /* infoPtr->notifyFormat will be filled in WM_CREATE */
8019 /* initialize color information */
8020 infoPtr->clrBk = CLR_NONE;
8021 infoPtr->clrText = CLR_DEFAULT;
8022 infoPtr->clrTextBk = CLR_DEFAULT;
8023 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
8025 /* set default values */
8026 infoPtr->nFocusedItem = -1;
8027 infoPtr->nSelectionMark = -1;
8028 infoPtr->nHotItem = -1;
8029 infoPtr->bRedraw = TRUE;
8030 infoPtr->bNoItemMetrics = TRUE;
8031 infoPtr->bDoChangeNotify = TRUE;
8032 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
8033 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
8034 infoPtr->nEditLabelItem = -1;
8035 infoPtr->dwHoverTime = -1; /* default system hover time */
8036 infoPtr->nMeasureItemHeight = 0;
8037 infoPtr->xTrackLine = -1; /* no track line */
8038 infoPtr->itemEdit.fEnabled = FALSE;
8040 /* get default font (icon title) */
8041 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
8042 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
8043 infoPtr->hFont = infoPtr->hDefaultFont;
8044 LISTVIEW_SaveTextMetrics(infoPtr);
8046 /* allocate memory for the data structure */
8047 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
8048 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
8049 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
8050 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
8051 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
8052 return TRUE;
8054 fail:
8055 DestroyWindow(infoPtr->hwndHeader);
8056 ranges_destroy(infoPtr->selectionRanges);
8057 DPA_Destroy(infoPtr->hdpaItems);
8058 DPA_Destroy(infoPtr->hdpaPosX);
8059 DPA_Destroy(infoPtr->hdpaPosY);
8060 DPA_Destroy(infoPtr->hdpaColumns);
8061 Free(infoPtr);
8062 return FALSE;
8065 /***
8066 * DESCRIPTION:
8067 * Creates the listview control - the WM_CREATE phase. Most of the data is
8068 * already set up in LISTVIEW_NCCreate
8070 * PARAMETER(S):
8071 * [I] hwnd : window handle
8072 * [I] lpcs : the create parameters
8074 * RETURN:
8075 * Success: 0
8076 * Failure: -1
8078 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
8080 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8081 UINT uView = lpcs->style & LVS_TYPEMASK;
8082 DWORD dFlags = WS_CHILD | HDS_HORZ | HDS_FULLDRAG | HDS_DRAGDROP;
8084 TRACE("(lpcs=%p)\n", lpcs);
8086 infoPtr->dwStyle = lpcs->style;
8087 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
8088 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
8090 /* setup creation flags */
8091 dFlags |= (LVS_NOSORTHEADER & lpcs->style) ? 0 : HDS_BUTTONS;
8092 dFlags |= (LVS_NOCOLUMNHEADER & lpcs->style) ? HDS_HIDDEN : 0;
8094 /* create header */
8095 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL, dFlags,
8096 0, 0, 0, 0, hwnd, NULL,
8097 lpcs->hInstance, NULL);
8098 if (!infoPtr->hwndHeader) return -1;
8100 /* set header unicode format */
8101 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, TRUE, 0);
8103 /* set header font */
8104 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
8106 /* init item size to avoid division by 0 */
8107 LISTVIEW_UpdateItemSize (infoPtr);
8109 if (uView == LVS_REPORT)
8111 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
8113 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8115 LISTVIEW_UpdateSize(infoPtr);
8116 LISTVIEW_UpdateScroll(infoPtr);
8119 OpenThemeData(hwnd, themeClass);
8121 /* initialize the icon sizes */
8122 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
8123 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
8124 return 0;
8127 /***
8128 * DESCRIPTION:
8129 * Destroys the listview control.
8131 * PARAMETER(S):
8132 * [I] infoPtr : valid pointer to the listview structure
8134 * RETURN:
8135 * Success: 0
8136 * Failure: -1
8138 static LRESULT LISTVIEW_Destroy(const LISTVIEW_INFO *infoPtr)
8140 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8141 CloseThemeData(theme);
8142 return 0;
8145 /***
8146 * DESCRIPTION:
8147 * Enables the listview control.
8149 * PARAMETER(S):
8150 * [I] infoPtr : valid pointer to the listview structure
8151 * [I] bEnable : specifies whether to enable or disable the window
8153 * RETURN:
8154 * SUCCESS : TRUE
8155 * FAILURE : FALSE
8157 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr, BOOL bEnable)
8159 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
8160 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8161 return TRUE;
8164 /***
8165 * DESCRIPTION:
8166 * Erases the background of the listview control.
8168 * PARAMETER(S):
8169 * [I] infoPtr : valid pointer to the listview structure
8170 * [I] hdc : device context handle
8172 * RETURN:
8173 * SUCCESS : TRUE
8174 * FAILURE : FALSE
8176 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
8178 RECT rc;
8180 TRACE("(hdc=%p)\n", hdc);
8182 if (!GetClipBox(hdc, &rc)) return FALSE;
8184 /* for double buffered controls we need to do this during refresh */
8185 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
8187 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
8191 /***
8192 * DESCRIPTION:
8193 * Helper function for LISTVIEW_[HV]Scroll *only*.
8194 * Performs vertical/horizontal scrolling by a give amount.
8196 * PARAMETER(S):
8197 * [I] infoPtr : valid pointer to the listview structure
8198 * [I] dx : amount of horizontal scroll
8199 * [I] dy : amount of vertical scroll
8201 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
8203 /* now we can scroll the list */
8204 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
8205 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
8206 /* if we have focus, adjust rect */
8207 OffsetRect(&infoPtr->rcFocus, dx, dy);
8208 UpdateWindow(infoPtr->hwndSelf);
8211 /***
8212 * DESCRIPTION:
8213 * Performs vertical scrolling.
8215 * PARAMETER(S):
8216 * [I] infoPtr : valid pointer to the listview structure
8217 * [I] nScrollCode : scroll code
8218 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8219 * [I] hScrollWnd : scrollbar control window handle
8221 * RETURN:
8222 * Zero
8224 * NOTES:
8225 * SB_LINEUP/SB_LINEDOWN:
8226 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8227 * for LVS_REPORT is 1 line
8228 * for LVS_LIST cannot occur
8231 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8232 INT nScrollDiff, HWND hScrollWnd)
8234 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8235 INT nOldScrollPos, nNewScrollPos;
8236 SCROLLINFO scrollInfo;
8237 BOOL is_an_icon;
8239 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8240 debugscrollcode(nScrollCode), nScrollDiff);
8242 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8244 scrollInfo.cbSize = sizeof(SCROLLINFO);
8245 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8247 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
8249 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
8251 nOldScrollPos = scrollInfo.nPos;
8252 switch (nScrollCode)
8254 case SB_INTERNAL:
8255 break;
8257 case SB_LINEUP:
8258 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
8259 break;
8261 case SB_LINEDOWN:
8262 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
8263 break;
8265 case SB_PAGEUP:
8266 nScrollDiff = -scrollInfo.nPage;
8267 break;
8269 case SB_PAGEDOWN:
8270 nScrollDiff = scrollInfo.nPage;
8271 break;
8273 case SB_THUMBPOSITION:
8274 case SB_THUMBTRACK:
8275 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8276 break;
8278 default:
8279 nScrollDiff = 0;
8282 /* quit right away if pos isn't changing */
8283 if (nScrollDiff == 0) return 0;
8285 /* calculate new position, and handle overflows */
8286 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8287 if (nScrollDiff > 0) {
8288 if (nNewScrollPos < nOldScrollPos ||
8289 nNewScrollPos > scrollInfo.nMax)
8290 nNewScrollPos = scrollInfo.nMax;
8291 } else {
8292 if (nNewScrollPos > nOldScrollPos ||
8293 nNewScrollPos < scrollInfo.nMin)
8294 nNewScrollPos = scrollInfo.nMin;
8297 /* set the new position, and reread in case it changed */
8298 scrollInfo.fMask = SIF_POS;
8299 scrollInfo.nPos = nNewScrollPos;
8300 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
8302 /* carry on only if it really changed */
8303 if (nNewScrollPos == nOldScrollPos) return 0;
8305 /* now adjust to client coordinates */
8306 nScrollDiff = nOldScrollPos - nNewScrollPos;
8307 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
8309 /* and scroll the window */
8310 scroll_list(infoPtr, 0, nScrollDiff);
8312 return 0;
8315 /***
8316 * DESCRIPTION:
8317 * Performs horizontal scrolling.
8319 * PARAMETER(S):
8320 * [I] infoPtr : valid pointer to the listview structure
8321 * [I] nScrollCode : scroll code
8322 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8323 * [I] hScrollWnd : scrollbar control window handle
8325 * RETURN:
8326 * Zero
8328 * NOTES:
8329 * SB_LINELEFT/SB_LINERIGHT:
8330 * for LVS_ICON, LVS_SMALLICON 1 pixel
8331 * for LVS_REPORT is 1 pixel
8332 * for LVS_LIST is 1 column --> which is a 1 because the
8333 * scroll is based on columns not pixels
8336 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8337 INT nScrollDiff, HWND hScrollWnd)
8339 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8340 INT nOldScrollPos, nNewScrollPos;
8341 SCROLLINFO scrollInfo;
8343 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8344 debugscrollcode(nScrollCode), nScrollDiff);
8346 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8348 scrollInfo.cbSize = sizeof(SCROLLINFO);
8349 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8351 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
8353 nOldScrollPos = scrollInfo.nPos;
8355 switch (nScrollCode)
8357 case SB_INTERNAL:
8358 break;
8360 case SB_LINELEFT:
8361 nScrollDiff = -1;
8362 break;
8364 case SB_LINERIGHT:
8365 nScrollDiff = 1;
8366 break;
8368 case SB_PAGELEFT:
8369 nScrollDiff = -scrollInfo.nPage;
8370 break;
8372 case SB_PAGERIGHT:
8373 nScrollDiff = scrollInfo.nPage;
8374 break;
8376 case SB_THUMBPOSITION:
8377 case SB_THUMBTRACK:
8378 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8379 break;
8381 default:
8382 nScrollDiff = 0;
8385 /* quit right away if pos isn't changing */
8386 if (nScrollDiff == 0) return 0;
8388 /* calculate new position, and handle overflows */
8389 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8390 if (nScrollDiff > 0) {
8391 if (nNewScrollPos < nOldScrollPos ||
8392 nNewScrollPos > scrollInfo.nMax)
8393 nNewScrollPos = scrollInfo.nMax;
8394 } else {
8395 if (nNewScrollPos > nOldScrollPos ||
8396 nNewScrollPos < scrollInfo.nMin)
8397 nNewScrollPos = scrollInfo.nMin;
8400 /* set the new position, and reread in case it changed */
8401 scrollInfo.fMask = SIF_POS;
8402 scrollInfo.nPos = nNewScrollPos;
8403 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
8405 /* carry on only if it really changed */
8406 if (nNewScrollPos == nOldScrollPos) return 0;
8408 if(uView == LVS_REPORT)
8409 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
8411 /* now adjust to client coordinates */
8412 nScrollDiff = nOldScrollPos - nNewScrollPos;
8413 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
8415 /* and scroll the window */
8416 scroll_list(infoPtr, nScrollDiff, 0);
8418 return 0;
8421 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
8423 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8424 INT gcWheelDelta = 0;
8425 INT pulScrollLines = 3;
8426 SCROLLINFO scrollInfo;
8428 TRACE("(wheelDelta=%d)\n", wheelDelta);
8430 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8431 gcWheelDelta -= wheelDelta;
8433 scrollInfo.cbSize = sizeof(SCROLLINFO);
8434 scrollInfo.fMask = SIF_POS;
8436 switch(uView)
8438 case LVS_ICON:
8439 case LVS_SMALLICON:
8441 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8442 * should be fixed in the future.
8444 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
8445 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8446 break;
8448 case LVS_REPORT:
8449 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8451 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
8452 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8453 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
8455 break;
8457 case LVS_LIST:
8458 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8459 break;
8461 return 0;
8464 /***
8465 * DESCRIPTION:
8466 * ???
8468 * PARAMETER(S):
8469 * [I] infoPtr : valid pointer to the listview structure
8470 * [I] nVirtualKey : virtual key
8471 * [I] lKeyData : key data
8473 * RETURN:
8474 * Zero
8476 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8478 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8479 HWND hwndSelf = infoPtr->hwndSelf;
8480 INT nItem = -1;
8481 NMLVKEYDOWN nmKeyDown;
8483 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
8485 /* send LVN_KEYDOWN notification */
8486 nmKeyDown.wVKey = nVirtualKey;
8487 nmKeyDown.flags = 0;
8488 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8489 if (!IsWindow(hwndSelf))
8490 return 0;
8492 switch (nVirtualKey)
8494 case VK_SPACE:
8495 nItem = infoPtr->nFocusedItem;
8496 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
8497 toggle_checkbox_state(infoPtr, infoPtr->nFocusedItem);
8498 break;
8500 case VK_RETURN:
8501 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8503 if (!notify(infoPtr, NM_RETURN)) return 0;
8504 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8506 break;
8508 case VK_HOME:
8509 if (infoPtr->nItemCount > 0)
8510 nItem = 0;
8511 break;
8513 case VK_END:
8514 if (infoPtr->nItemCount > 0)
8515 nItem = infoPtr->nItemCount - 1;
8516 break;
8518 case VK_LEFT:
8519 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8520 break;
8522 case VK_UP:
8523 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8524 break;
8526 case VK_RIGHT:
8527 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8528 break;
8530 case VK_DOWN:
8531 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8532 break;
8534 case VK_PRIOR:
8535 if (uView == LVS_REPORT)
8537 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8538 if (infoPtr->nFocusedItem == topidx)
8539 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8540 else
8541 nItem = topidx;
8543 else
8544 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8545 * LISTVIEW_GetCountPerRow(infoPtr);
8546 if(nItem < 0) nItem = 0;
8547 break;
8549 case VK_NEXT:
8550 if (uView == LVS_REPORT)
8552 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8553 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8554 if (infoPtr->nFocusedItem == topidx + cnt - 1)
8555 nItem = infoPtr->nFocusedItem + cnt - 1;
8556 else
8557 nItem = topidx + cnt - 1;
8559 else
8560 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8561 * LISTVIEW_GetCountPerRow(infoPtr);
8562 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8563 break;
8566 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8567 LISTVIEW_KeySelection(infoPtr, nItem, nVirtualKey == VK_SPACE);
8569 return 0;
8572 /***
8573 * DESCRIPTION:
8574 * Kills the focus.
8576 * PARAMETER(S):
8577 * [I] infoPtr : valid pointer to the listview structure
8579 * RETURN:
8580 * Zero
8582 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8584 TRACE("()\n");
8586 /* if we did not have the focus, there's nothing to do */
8587 if (!infoPtr->bFocus) return 0;
8589 /* send NM_KILLFOCUS notification */
8590 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8592 /* if we have a focus rectagle, get rid of it */
8593 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8595 /* set window focus flag */
8596 infoPtr->bFocus = FALSE;
8598 /* invalidate the selected items before resetting focus flag */
8599 LISTVIEW_InvalidateSelectedItems(infoPtr);
8601 return 0;
8604 /***
8605 * DESCRIPTION:
8606 * Processes double click messages (left mouse button).
8608 * PARAMETER(S):
8609 * [I] infoPtr : valid pointer to the listview structure
8610 * [I] wKey : key flag
8611 * [I] x,y : mouse coordinate
8613 * RETURN:
8614 * Zero
8616 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8618 LVHITTESTINFO htInfo;
8620 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8622 /* Cancel the item edition if any */
8623 if (infoPtr->itemEdit.fEnabled)
8625 KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
8626 infoPtr->itemEdit.fEnabled = FALSE;
8629 /* send NM_RELEASEDCAPTURE notification */
8630 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8632 htInfo.pt.x = x;
8633 htInfo.pt.y = y;
8635 /* send NM_DBLCLK notification */
8636 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8637 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8639 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8640 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8642 return 0;
8645 /***
8646 * DESCRIPTION:
8647 * Processes mouse down messages (left mouse button).
8649 * PARAMETERS:
8650 * infoPtr [I ] valid pointer to the listview structure
8651 * wKey [I ] key flag
8652 * x,y [I ] mouse coordinate
8654 * RETURN:
8655 * Zero
8657 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8659 LVHITTESTINFO lvHitTestInfo;
8660 static BOOL bGroupSelect = TRUE;
8661 POINT pt = { x, y };
8662 INT nItem;
8664 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8666 /* send NM_RELEASEDCAPTURE notification */
8667 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8669 /* set left button down flag and record the click position */
8670 infoPtr->bLButtonDown = TRUE;
8671 infoPtr->ptClickPos = pt;
8672 infoPtr->bDragging = FALSE;
8674 lvHitTestInfo.pt.x = x;
8675 lvHitTestInfo.pt.y = y;
8677 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8678 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
8679 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8681 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8683 toggle_checkbox_state(infoPtr, nItem);
8684 return 0;
8687 if (infoPtr->dwStyle & LVS_SINGLESEL)
8689 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8690 infoPtr->nEditLabelItem = nItem;
8691 else
8692 LISTVIEW_SetSelection(infoPtr, nItem);
8694 else
8696 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8698 if (bGroupSelect)
8700 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8701 LISTVIEW_SetItemFocus(infoPtr, nItem);
8702 infoPtr->nSelectionMark = nItem;
8704 else
8706 LVITEMW item;
8708 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8709 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8711 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8712 infoPtr->nSelectionMark = nItem;
8715 else if (wKey & MK_CONTROL)
8717 LVITEMW item;
8719 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8721 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8722 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8723 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8724 infoPtr->nSelectionMark = nItem;
8726 else if (wKey & MK_SHIFT)
8728 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8730 else
8732 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8733 infoPtr->nEditLabelItem = nItem;
8735 /* set selection (clears other pre-existing selections) */
8736 LISTVIEW_SetSelection(infoPtr, nItem);
8740 if (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE)
8741 if(lvHitTestInfo.iItem != -1) notify_itemactivate(infoPtr,&lvHitTestInfo);
8743 else
8745 /* remove all selections */
8746 LISTVIEW_DeselectAll(infoPtr);
8747 ReleaseCapture();
8750 return 0;
8753 /***
8754 * DESCRIPTION:
8755 * Processes mouse up messages (left mouse button).
8757 * PARAMETERS:
8758 * infoPtr [I ] valid pointer to the listview structure
8759 * wKey [I ] key flag
8760 * x,y [I ] mouse coordinate
8762 * RETURN:
8763 * Zero
8765 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8767 LVHITTESTINFO lvHitTestInfo;
8769 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8771 if (!infoPtr->bLButtonDown) return 0;
8773 lvHitTestInfo.pt.x = x;
8774 lvHitTestInfo.pt.y = y;
8776 /* send NM_CLICK notification */
8777 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8778 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8780 /* set left button flag */
8781 infoPtr->bLButtonDown = FALSE;
8783 if (infoPtr->bDragging)
8785 infoPtr->bDragging = FALSE;
8786 return 0;
8789 /* if we clicked on a selected item, edit the label */
8790 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8792 /* we want to make sure the user doesn't want to do a double click. So we will
8793 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
8795 infoPtr->itemEdit.fEnabled = TRUE;
8796 infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
8797 SetTimer(infoPtr->hwndSelf,
8798 (UINT_PTR)&infoPtr->itemEdit,
8799 GetDoubleClickTime(),
8800 LISTVIEW_DelayedEditItem);
8803 if (!infoPtr->bFocus)
8804 SetFocus(infoPtr->hwndSelf);
8806 return 0;
8809 /***
8810 * DESCRIPTION:
8811 * Destroys the listview control (called after WM_DESTROY).
8813 * PARAMETER(S):
8814 * [I] infoPtr : valid pointer to the listview structure
8816 * RETURN:
8817 * Zero
8819 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8821 TRACE("()\n");
8823 /* delete all items */
8824 LISTVIEW_DeleteAllItems(infoPtr, TRUE);
8826 /* destroy data structure */
8827 DPA_Destroy(infoPtr->hdpaItems);
8828 DPA_Destroy(infoPtr->hdpaPosX);
8829 DPA_Destroy(infoPtr->hdpaPosY);
8830 DPA_Destroy(infoPtr->hdpaColumns);
8831 ranges_destroy(infoPtr->selectionRanges);
8833 /* destroy image lists */
8834 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8836 if (infoPtr->himlNormal)
8837 ImageList_Destroy(infoPtr->himlNormal);
8838 if (infoPtr->himlSmall)
8839 ImageList_Destroy(infoPtr->himlSmall);
8840 if (infoPtr->himlState)
8841 ImageList_Destroy(infoPtr->himlState);
8844 /* destroy font, bkgnd brush */
8845 infoPtr->hFont = 0;
8846 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8847 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8849 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8851 /* free listview info pointer*/
8852 Free(infoPtr);
8854 return 0;
8857 /***
8858 * DESCRIPTION:
8859 * Handles notifications from header.
8861 * PARAMETER(S):
8862 * [I] infoPtr : valid pointer to the listview structure
8863 * [I] nCtrlId : control identifier
8864 * [I] lpnmh : notification information
8866 * RETURN:
8867 * Zero
8869 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8871 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8872 HWND hwndSelf = infoPtr->hwndSelf;
8874 TRACE("(lpnmh=%p)\n", lpnmh);
8876 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8878 switch (lpnmh->hdr.code)
8880 case HDN_TRACKW:
8881 case HDN_TRACKA:
8883 COLUMN_INFO *lpColumnInfo;
8884 POINT ptOrigin;
8885 INT x;
8887 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8888 break;
8890 /* remove the old line (if any) */
8891 LISTVIEW_DrawTrackLine(infoPtr);
8893 /* compute & draw the new line */
8894 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8895 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
8896 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8897 infoPtr->xTrackLine = x + ptOrigin.x;
8898 LISTVIEW_DrawTrackLine(infoPtr);
8899 break;
8902 case HDN_ENDTRACKA:
8903 case HDN_ENDTRACKW:
8904 /* remove the track line (if any) */
8905 LISTVIEW_DrawTrackLine(infoPtr);
8906 infoPtr->xTrackLine = -1;
8907 break;
8909 case HDN_ENDDRAG:
8910 FIXME("Changing column order not implemented\n");
8911 return TRUE;
8913 case HDN_ITEMCHANGINGW:
8914 case HDN_ITEMCHANGINGA:
8915 return notify_forward_header(infoPtr, lpnmh);
8917 case HDN_ITEMCHANGEDW:
8918 case HDN_ITEMCHANGEDA:
8920 COLUMN_INFO *lpColumnInfo;
8921 INT dx, cxy;
8923 notify_forward_header(infoPtr, lpnmh);
8924 if (!IsWindow(hwndSelf))
8925 break;
8927 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8929 HDITEMW hdi;
8931 hdi.mask = HDI_WIDTH;
8932 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, &hdi)) return 0;
8933 cxy = hdi.cxy;
8935 else
8936 cxy = lpnmh->pitem->cxy;
8938 /* determine how much we change since the last know position */
8939 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8940 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8941 if (dx != 0)
8943 lpColumnInfo->rcHeader.right += dx;
8944 if (lpnmh->iItem + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
8945 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8946 else
8948 /* only needs to update the scrolls */
8949 infoPtr->nItemWidth += dx;
8950 LISTVIEW_UpdateScroll(infoPtr);
8952 LISTVIEW_UpdateItemSize(infoPtr);
8953 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8955 POINT ptOrigin;
8956 RECT rcCol = lpColumnInfo->rcHeader;
8958 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8959 OffsetRect(&rcCol, ptOrigin.x, 0);
8961 rcCol.top = infoPtr->rcList.top;
8962 rcCol.bottom = infoPtr->rcList.bottom;
8964 /* resizing left-aligned columns leaves most of the left side untouched */
8965 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8967 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
8968 if (dx > 0)
8969 nMaxDirty += dx;
8970 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
8973 /* when shrinking the last column clear the now unused field */
8974 if (lpnmh->iItem == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1 && dx < 0)
8975 rcCol.right -= dx;
8977 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8981 break;
8983 case HDN_ITEMCLICKW:
8984 case HDN_ITEMCLICKA:
8986 /* Handle sorting by Header Column */
8987 NMLISTVIEW nmlv;
8989 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8990 nmlv.iItem = -1;
8991 nmlv.iSubItem = lpnmh->iItem;
8992 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8993 notify_forward_header(infoPtr, lpnmh);
8995 break;
8997 case HDN_DIVIDERDBLCLICKW:
8998 case HDN_DIVIDERDBLCLICKA:
8999 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
9000 break;
9003 return 0;
9006 /***
9007 * DESCRIPTION:
9008 * Paint non-client area of control.
9010 * PARAMETER(S):
9011 * [I] infoPtr : valid pointer to the listview structureof the sender
9012 * [I] region : update region
9014 * RETURN:
9015 * TRUE - frame was painted
9016 * FALSE - call default window proc
9018 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
9020 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
9021 HDC dc;
9022 RECT r;
9023 HRGN cliprgn;
9024 int cxEdge = GetSystemMetrics (SM_CXEDGE),
9025 cyEdge = GetSystemMetrics (SM_CYEDGE);
9027 if (!theme) return FALSE;
9029 GetWindowRect(infoPtr->hwndSelf, &r);
9031 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
9032 r.right - cxEdge, r.bottom - cyEdge);
9033 if (region != (HRGN)1)
9034 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
9035 OffsetRect(&r, -r.left, -r.top);
9037 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
9038 OffsetRect(&r, -r.left, -r.top);
9040 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
9041 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
9042 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
9043 ReleaseDC(infoPtr->hwndSelf, dc);
9045 /* Call default proc to get the scrollbars etc. painted */
9046 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
9048 return TRUE;
9051 /***
9052 * DESCRIPTION:
9053 * Determines the type of structure to use.
9055 * PARAMETER(S):
9056 * [I] infoPtr : valid pointer to the listview structureof the sender
9057 * [I] hwndFrom : listview window handle
9058 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
9060 * RETURN:
9061 * Zero
9063 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
9065 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
9067 if (nCommand == NF_REQUERY)
9068 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
9070 return infoPtr->notifyFormat;
9073 /***
9074 * DESCRIPTION:
9075 * Paints/Repaints the listview control.
9077 * PARAMETER(S):
9078 * [I] infoPtr : valid pointer to the listview structure
9079 * [I] hdc : device context handle
9081 * RETURN:
9082 * Zero
9084 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
9086 TRACE("(hdc=%p)\n", hdc);
9088 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
9090 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9092 infoPtr->bNoItemMetrics = FALSE;
9093 LISTVIEW_UpdateItemSize(infoPtr);
9094 if (uView == LVS_ICON || uView == LVS_SMALLICON)
9095 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9096 LISTVIEW_UpdateScroll(infoPtr);
9099 UpdateWindow(infoPtr->hwndHeader);
9101 if (hdc)
9102 LISTVIEW_Refresh(infoPtr, hdc, NULL);
9103 else
9105 PAINTSTRUCT ps;
9107 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
9108 if (!hdc) return 1;
9109 LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
9110 EndPaint(infoPtr->hwndSelf, &ps);
9113 return 0;
9117 /***
9118 * DESCRIPTION:
9119 * Paints/Repaints the listview control.
9121 * PARAMETER(S):
9122 * [I] infoPtr : valid pointer to the listview structure
9123 * [I] hdc : device context handle
9124 * [I] options : drawing options
9126 * RETURN:
9127 * Zero
9129 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
9131 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
9133 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
9134 return 0;
9136 if (options & PRF_ERASEBKGND)
9137 LISTVIEW_EraseBkgnd(infoPtr, hdc);
9139 if (options & PRF_CLIENT)
9140 LISTVIEW_Paint(infoPtr, hdc);
9142 return 0;
9146 /***
9147 * DESCRIPTION:
9148 * Processes double click messages (right mouse button).
9150 * PARAMETER(S):
9151 * [I] infoPtr : valid pointer to the listview structure
9152 * [I] wKey : key flag
9153 * [I] x,y : mouse coordinate
9155 * RETURN:
9156 * Zero
9158 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9160 LVHITTESTINFO lvHitTestInfo;
9162 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9164 /* send NM_RELEASEDCAPTURE notification */
9165 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9167 /* send NM_RDBLCLK notification */
9168 lvHitTestInfo.pt.x = x;
9169 lvHitTestInfo.pt.y = y;
9170 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9171 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
9173 return 0;
9176 /***
9177 * DESCRIPTION:
9178 * Processes mouse down messages (right mouse button).
9180 * PARAMETER(S):
9181 * [I] infoPtr : valid pointer to the listview structure
9182 * [I] wKey : key flag
9183 * [I] x,y : mouse coordinate
9185 * RETURN:
9186 * Zero
9188 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9190 LVHITTESTINFO lvHitTestInfo;
9191 INT nItem;
9193 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9195 /* send NM_RELEASEDCAPTURE notification */
9196 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9198 /* make sure the listview control window has the focus */
9199 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
9201 /* set right button down flag */
9202 infoPtr->bRButtonDown = TRUE;
9204 /* determine the index of the selected item */
9205 lvHitTestInfo.pt.x = x;
9206 lvHitTestInfo.pt.y = y;
9207 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
9209 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9211 LISTVIEW_SetItemFocus(infoPtr, nItem);
9212 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9213 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9214 LISTVIEW_SetSelection(infoPtr, nItem);
9216 else
9218 LISTVIEW_DeselectAll(infoPtr);
9221 return 0;
9224 /***
9225 * DESCRIPTION:
9226 * Processes mouse up messages (right mouse button).
9228 * PARAMETER(S):
9229 * [I] infoPtr : valid pointer to the listview structure
9230 * [I] wKey : key flag
9231 * [I] x,y : mouse coordinate
9233 * RETURN:
9234 * Zero
9236 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9238 LVHITTESTINFO lvHitTestInfo;
9239 POINT pt;
9241 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9243 if (!infoPtr->bRButtonDown) return 0;
9245 /* set button flag */
9246 infoPtr->bRButtonDown = FALSE;
9248 /* Send NM_RClICK notification */
9249 lvHitTestInfo.pt.x = x;
9250 lvHitTestInfo.pt.y = y;
9251 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9252 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
9254 /* Change to screen coordinate for WM_CONTEXTMENU */
9255 pt = lvHitTestInfo.pt;
9256 ClientToScreen(infoPtr->hwndSelf, &pt);
9258 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9259 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
9260 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
9262 return 0;
9266 /***
9267 * DESCRIPTION:
9268 * Sets the cursor.
9270 * PARAMETER(S):
9271 * [I] infoPtr : valid pointer to the listview structure
9272 * [I] hwnd : window handle of window containing the cursor
9273 * [I] nHittest : hit-test code
9274 * [I] wMouseMsg : ideintifier of the mouse message
9276 * RETURN:
9277 * TRUE if cursor is set
9278 * FALSE otherwise
9280 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
9282 LVHITTESTINFO lvHitTestInfo;
9284 if(!(LISTVIEW_isHotTracking(infoPtr))) return FALSE;
9286 if(!infoPtr->hHotCursor) return FALSE;
9288 GetCursorPos(&lvHitTestInfo.pt);
9289 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
9291 SetCursor(infoPtr->hHotCursor);
9293 return TRUE;
9296 /***
9297 * DESCRIPTION:
9298 * Sets the focus.
9300 * PARAMETER(S):
9301 * [I] infoPtr : valid pointer to the listview structure
9302 * [I] hwndLoseFocus : handle of previously focused window
9304 * RETURN:
9305 * Zero
9307 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
9309 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
9311 /* if we have the focus already, there's nothing to do */
9312 if (infoPtr->bFocus) return 0;
9314 /* send NM_SETFOCUS notification */
9315 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
9317 /* set window focus flag */
9318 infoPtr->bFocus = TRUE;
9320 /* put the focus rect back on */
9321 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
9323 /* redraw all visible selected items */
9324 LISTVIEW_InvalidateSelectedItems(infoPtr);
9326 return 0;
9329 /***
9330 * DESCRIPTION:
9331 * Sets the font.
9333 * PARAMETER(S):
9334 * [I] infoPtr : valid pointer to the listview structure
9335 * [I] fRedraw : font handle
9336 * [I] fRedraw : redraw flag
9338 * RETURN:
9339 * Zero
9341 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
9343 HFONT oldFont = infoPtr->hFont;
9345 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
9347 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9348 if (infoPtr->hFont == oldFont) return 0;
9350 LISTVIEW_SaveTextMetrics(infoPtr);
9352 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
9354 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
9355 LISTVIEW_UpdateSize(infoPtr);
9356 LISTVIEW_UpdateScroll(infoPtr);
9359 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
9361 return 0;
9364 /***
9365 * DESCRIPTION:
9366 * Message handling for WM_SETREDRAW.
9367 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9369 * PARAMETER(S):
9370 * [I] infoPtr : valid pointer to the listview structure
9371 * [I] bRedraw: state of redraw flag
9373 * RETURN:
9374 * DefWinProc return value
9376 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
9378 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
9380 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9381 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
9383 infoPtr->bRedraw = bRedraw;
9385 if(!bRedraw) return 0;
9387 if (is_autoarrange(infoPtr))
9388 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9389 LISTVIEW_UpdateScroll(infoPtr);
9391 /* despite what the WM_SETREDRAW docs says, apps expect us
9392 * to invalidate the listview here... stupid! */
9393 LISTVIEW_InvalidateList(infoPtr);
9395 return 0;
9398 /***
9399 * DESCRIPTION:
9400 * Resizes the listview control. This function processes WM_SIZE
9401 * messages. At this time, the width and height are not used.
9403 * PARAMETER(S):
9404 * [I] infoPtr : valid pointer to the listview structure
9405 * [I] Width : new width
9406 * [I] Height : new height
9408 * RETURN:
9409 * Zero
9411 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
9413 RECT rcOld = infoPtr->rcList;
9415 TRACE("(width=%d, height=%d)\n", Width, Height);
9417 LISTVIEW_UpdateSize(infoPtr);
9418 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
9420 /* do not bother with display related stuff if we're not redrawing */
9421 if (!is_redrawing(infoPtr)) return 0;
9423 if (is_autoarrange(infoPtr))
9424 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9426 LISTVIEW_UpdateScroll(infoPtr);
9428 /* refresh all only for lists whose height changed significantly */
9429 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
9430 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
9431 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
9432 LISTVIEW_InvalidateList(infoPtr);
9434 return 0;
9437 /***
9438 * DESCRIPTION:
9439 * Sets the size information.
9441 * PARAMETER(S):
9442 * [I] infoPtr : valid pointer to the listview structure
9444 * RETURN:
9445 * None
9447 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
9449 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9451 TRACE("uView=%d, rcList(old)=%s\n", uView, wine_dbgstr_rect(&infoPtr->rcList));
9453 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
9455 if (uView == LVS_LIST)
9457 /* Apparently the "LIST" style is supposed to have the same
9458 * number of items in a column even if there is no scroll bar.
9459 * Since if a scroll bar already exists then the bottom is already
9460 * reduced, only reduce if the scroll bar does not currently exist.
9461 * The "2" is there to mimic the native control. I think it may be
9462 * related to either padding or edges. (GLA 7/2002)
9464 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
9465 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
9466 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
9468 else if (uView == LVS_REPORT)
9470 HDLAYOUT hl;
9471 WINDOWPOS wp;
9473 hl.prc = &infoPtr->rcList;
9474 hl.pwpos = &wp;
9475 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9476 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp.flags, wp.x, wp.y, wp.cx, wp.cy);
9477 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy,
9478 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9479 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9480 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy);
9482 infoPtr->rcList.top = max(wp.cy, 0);
9483 infoPtr->rcList.top += (infoPtr->dwLvExStyle & LVS_EX_GRIDLINES) ? 2 : 0;
9486 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
9489 /***
9490 * DESCRIPTION:
9491 * Processes WM_STYLECHANGED messages.
9493 * PARAMETER(S):
9494 * [I] infoPtr : valid pointer to the listview structure
9495 * [I] wStyleType : window style type (normal or extended)
9496 * [I] lpss : window style information
9498 * RETURN:
9499 * Zero
9501 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9502 const STYLESTRUCT *lpss)
9504 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9505 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9506 UINT style;
9508 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9509 wStyleType, lpss->styleOld, lpss->styleNew);
9511 if (wStyleType != GWL_STYLE) return 0;
9513 /* FIXME: if LVS_NOSORTHEADER changed, update header */
9514 /* what if LVS_OWNERDATA changed? */
9515 /* or LVS_SINGLESEL */
9516 /* or LVS_SORT{AS,DES}CENDING */
9518 infoPtr->dwStyle = lpss->styleNew;
9520 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9521 ((lpss->styleNew & WS_HSCROLL) == 0))
9522 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9524 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9525 ((lpss->styleNew & WS_VSCROLL) == 0))
9526 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9528 if (uNewView != uOldView)
9530 SIZE oldIconSize = infoPtr->iconSize;
9531 HIMAGELIST himl;
9533 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9534 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9536 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9537 SetRectEmpty(&infoPtr->rcFocus);
9539 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9540 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
9542 if (uNewView == LVS_ICON)
9544 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
9546 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9547 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9548 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
9551 else if (uNewView == LVS_REPORT)
9553 HDLAYOUT hl;
9554 WINDOWPOS wp;
9556 hl.prc = &infoPtr->rcList;
9557 hl.pwpos = &wp;
9558 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9559 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
9560 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9561 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9564 LISTVIEW_UpdateItemSize(infoPtr);
9567 if (uNewView == LVS_REPORT)
9569 if ((lpss->styleOld ^ lpss->styleNew) & LVS_NOCOLUMNHEADER)
9571 if (lpss->styleNew & LVS_NOCOLUMNHEADER)
9573 /* Turn off the header control */
9574 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
9575 TRACE("Hide header control, was 0x%08x\n", style);
9576 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style | HDS_HIDDEN);
9577 } else {
9578 /* Turn on the header control */
9579 if ((style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE)) & HDS_HIDDEN)
9581 TRACE("Show header control, was 0x%08x\n", style);
9582 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, (style & ~HDS_HIDDEN) | WS_VISIBLE);
9588 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9589 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9590 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9592 /* update the size of the client area */
9593 LISTVIEW_UpdateSize(infoPtr);
9595 /* add scrollbars if needed */
9596 LISTVIEW_UpdateScroll(infoPtr);
9598 /* invalidate client area + erase background */
9599 LISTVIEW_InvalidateList(infoPtr);
9601 return 0;
9604 /***
9605 * DESCRIPTION:
9606 * Window procedure of the listview control.
9609 static LRESULT WINAPI
9610 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9612 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9614 TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg, wParam, lParam);
9616 if (!infoPtr && (uMsg != WM_NCCREATE))
9617 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9619 switch (uMsg)
9621 case LVM_APPROXIMATEVIEWRECT:
9622 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9623 LOWORD(lParam), HIWORD(lParam));
9624 case LVM_ARRANGE:
9625 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9627 /* case LVM_CANCELEDITLABEL: */
9629 case LVM_CREATEDRAGIMAGE:
9630 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9632 case LVM_DELETEALLITEMS:
9633 return LISTVIEW_DeleteAllItems(infoPtr, FALSE);
9635 case LVM_DELETECOLUMN:
9636 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9638 case LVM_DELETEITEM:
9639 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9641 case LVM_EDITLABELW:
9642 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9644 case LVM_EDITLABELA:
9645 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9647 /* case LVM_ENABLEGROUPVIEW: */
9649 case LVM_ENSUREVISIBLE:
9650 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9652 case LVM_FINDITEMW:
9653 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9655 case LVM_FINDITEMA:
9656 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9658 case LVM_GETBKCOLOR:
9659 return infoPtr->clrBk;
9661 /* case LVM_GETBKIMAGE: */
9663 case LVM_GETCALLBACKMASK:
9664 return infoPtr->uCallbackMask;
9666 case LVM_GETCOLUMNA:
9667 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9669 case LVM_GETCOLUMNW:
9670 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9672 case LVM_GETCOLUMNORDERARRAY:
9673 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9675 case LVM_GETCOLUMNWIDTH:
9676 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9678 case LVM_GETCOUNTPERPAGE:
9679 return LISTVIEW_GetCountPerPage(infoPtr);
9681 case LVM_GETEDITCONTROL:
9682 return (LRESULT)infoPtr->hwndEdit;
9684 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9685 return infoPtr->dwLvExStyle;
9687 /* case LVM_GETGROUPINFO: */
9689 /* case LVM_GETGROUPMETRICS: */
9691 case LVM_GETHEADER:
9692 return (LRESULT)infoPtr->hwndHeader;
9694 case LVM_GETHOTCURSOR:
9695 return (LRESULT)infoPtr->hHotCursor;
9697 case LVM_GETHOTITEM:
9698 return infoPtr->nHotItem;
9700 case LVM_GETHOVERTIME:
9701 return infoPtr->dwHoverTime;
9703 case LVM_GETIMAGELIST:
9704 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9706 /* case LVM_GETINSERTMARK: */
9708 /* case LVM_GETINSERTMARKCOLOR: */
9710 /* case LVM_GETINSERTMARKRECT: */
9712 case LVM_GETISEARCHSTRINGA:
9713 case LVM_GETISEARCHSTRINGW:
9714 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9715 return FALSE;
9717 case LVM_GETITEMA:
9718 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9720 case LVM_GETITEMW:
9721 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9723 case LVM_GETITEMCOUNT:
9724 return infoPtr->nItemCount;
9726 case LVM_GETITEMPOSITION:
9727 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9729 case LVM_GETITEMRECT:
9730 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9732 case LVM_GETITEMSPACING:
9733 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9735 case LVM_GETITEMSTATE:
9736 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9738 case LVM_GETITEMTEXTA:
9739 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9741 case LVM_GETITEMTEXTW:
9742 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9744 case LVM_GETNEXTITEM:
9745 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9747 case LVM_GETNUMBEROFWORKAREAS:
9748 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9749 return 1;
9751 case LVM_GETORIGIN:
9752 if (!lParam) return FALSE;
9753 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT ||
9754 (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST) return FALSE;
9755 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9756 return TRUE;
9758 /* case LVM_GETOUTLINECOLOR: */
9760 /* case LVM_GETSELECTEDCOLUMN: */
9762 case LVM_GETSELECTEDCOUNT:
9763 return LISTVIEW_GetSelectedCount(infoPtr);
9765 case LVM_GETSELECTIONMARK:
9766 return infoPtr->nSelectionMark;
9768 case LVM_GETSTRINGWIDTHA:
9769 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9771 case LVM_GETSTRINGWIDTHW:
9772 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9774 case LVM_GETSUBITEMRECT:
9775 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
9777 case LVM_GETTEXTBKCOLOR:
9778 return infoPtr->clrTextBk;
9780 case LVM_GETTEXTCOLOR:
9781 return infoPtr->clrText;
9783 /* case LVM_GETTILEINFO: */
9785 /* case LVM_GETTILEVIEWINFO: */
9787 case LVM_GETTOOLTIPS:
9788 if( !infoPtr->hwndToolTip )
9789 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9790 return (LRESULT)infoPtr->hwndToolTip;
9792 case LVM_GETTOPINDEX:
9793 return LISTVIEW_GetTopIndex(infoPtr);
9795 case LVM_GETUNICODEFORMAT:
9796 return (infoPtr->notifyFormat == NFR_UNICODE);
9798 /* case LVM_GETVIEW: */
9800 case LVM_GETVIEWRECT:
9801 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9803 case LVM_GETWORKAREAS:
9804 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9805 return FALSE;
9807 /* case LVM_HASGROUP: */
9809 case LVM_HITTEST:
9810 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9812 case LVM_INSERTCOLUMNA:
9813 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9815 case LVM_INSERTCOLUMNW:
9816 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9818 /* case LVM_INSERTGROUP: */
9820 /* case LVM_INSERTGROUPSORTED: */
9822 case LVM_INSERTITEMA:
9823 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9825 case LVM_INSERTITEMW:
9826 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9828 /* case LVM_INSERTMARKHITTEST: */
9830 /* case LVM_ISGROUPVIEWENABLED: */
9832 /* case LVM_MAPIDTOINDEX: */
9834 /* case LVM_MAPINDEXTOID: */
9836 /* case LVM_MOVEGROUP: */
9838 /* case LVM_MOVEITEMTOGROUP: */
9840 case LVM_REDRAWITEMS:
9841 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9843 /* case LVM_REMOVEALLGROUPS: */
9845 /* case LVM_REMOVEGROUP: */
9847 case LVM_SCROLL:
9848 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9850 case LVM_SETBKCOLOR:
9851 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9853 /* case LVM_SETBKIMAGE: */
9855 case LVM_SETCALLBACKMASK:
9856 infoPtr->uCallbackMask = (UINT)wParam;
9857 return TRUE;
9859 case LVM_SETCOLUMNA:
9860 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9862 case LVM_SETCOLUMNW:
9863 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9865 case LVM_SETCOLUMNORDERARRAY:
9866 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9868 case LVM_SETCOLUMNWIDTH:
9869 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9871 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9872 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9874 /* case LVM_SETGROUPINFO: */
9876 /* case LVM_SETGROUPMETRICS: */
9878 case LVM_SETHOTCURSOR:
9879 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9881 case LVM_SETHOTITEM:
9882 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9884 case LVM_SETHOVERTIME:
9885 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9887 case LVM_SETICONSPACING:
9888 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9890 case LVM_SETIMAGELIST:
9891 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9893 /* case LVM_SETINFOTIP: */
9895 /* case LVM_SETINSERTMARK: */
9897 /* case LVM_SETINSERTMARKCOLOR: */
9899 case LVM_SETITEMA:
9900 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9902 case LVM_SETITEMW:
9903 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9905 case LVM_SETITEMCOUNT:
9906 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9908 case LVM_SETITEMPOSITION:
9910 POINT pt;
9911 pt.x = (short)LOWORD(lParam);
9912 pt.y = (short)HIWORD(lParam);
9913 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9916 case LVM_SETITEMPOSITION32:
9917 if (lParam == 0) return FALSE;
9918 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9920 case LVM_SETITEMSTATE:
9921 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9923 case LVM_SETITEMTEXTA:
9924 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9926 case LVM_SETITEMTEXTW:
9927 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9929 /* case LVM_SETOUTLINECOLOR: */
9931 /* case LVM_SETSELECTEDCOLUMN: */
9933 case LVM_SETSELECTIONMARK:
9934 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9936 case LVM_SETTEXTBKCOLOR:
9937 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9939 case LVM_SETTEXTCOLOR:
9940 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9942 /* case LVM_SETTILEINFO: */
9944 /* case LVM_SETTILEVIEWINFO: */
9946 /* case LVM_SETTILEWIDTH: */
9948 case LVM_SETTOOLTIPS:
9949 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9951 case LVM_SETUNICODEFORMAT:
9952 return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
9954 /* case LVM_SETVIEW: */
9956 /* case LVM_SETWORKAREAS: */
9958 /* case LVM_SORTGROUPS: */
9960 case LVM_SORTITEMS:
9961 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9963 /* LVM_SORTITEMSEX: */
9965 case LVM_SUBITEMHITTEST:
9966 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9968 case LVM_UPDATE:
9969 return LISTVIEW_Update(infoPtr, (INT)wParam);
9971 case WM_CHAR:
9972 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9974 case WM_COMMAND:
9975 return LISTVIEW_Command(infoPtr, wParam, lParam);
9977 case WM_NCCREATE:
9978 return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
9980 case WM_CREATE:
9981 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9983 case WM_DESTROY:
9984 return LISTVIEW_Destroy(infoPtr);
9986 case WM_ENABLE:
9987 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
9989 case WM_ERASEBKGND:
9990 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9992 case WM_GETDLGCODE:
9993 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9995 case WM_GETFONT:
9996 return (LRESULT)infoPtr->hFont;
9998 case WM_HSCROLL:
9999 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10001 case WM_KEYDOWN:
10002 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
10004 case WM_KILLFOCUS:
10005 return LISTVIEW_KillFocus(infoPtr);
10007 case WM_LBUTTONDBLCLK:
10008 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10010 case WM_LBUTTONDOWN:
10011 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10013 case WM_LBUTTONUP:
10014 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10016 case WM_MOUSEMOVE:
10017 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10019 case WM_MOUSEHOVER:
10020 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10022 case WM_NCDESTROY:
10023 return LISTVIEW_NCDestroy(infoPtr);
10025 case WM_NCPAINT:
10026 if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
10027 return 0;
10028 goto fwd_msg;
10030 case WM_NOTIFY:
10031 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
10032 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
10033 else return 0;
10035 case WM_NOTIFYFORMAT:
10036 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
10038 case WM_PRINTCLIENT:
10039 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
10041 case WM_PAINT:
10042 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
10044 case WM_RBUTTONDBLCLK:
10045 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10047 case WM_RBUTTONDOWN:
10048 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10050 case WM_RBUTTONUP:
10051 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10053 case WM_SETCURSOR:
10054 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
10055 return TRUE;
10056 goto fwd_msg;
10058 case WM_SETFOCUS:
10059 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
10061 case WM_SETFONT:
10062 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
10064 case WM_SETREDRAW:
10065 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
10067 case WM_SIZE:
10068 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10070 case WM_STYLECHANGED:
10071 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10073 case WM_SYSCOLORCHANGE:
10074 COMCTL32_RefreshSysColors();
10075 return 0;
10077 /* case WM_TIMER: */
10078 case WM_THEMECHANGED:
10079 return LISTVIEW_ThemeChanged(infoPtr);
10081 case WM_VSCROLL:
10082 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10084 case WM_MOUSEWHEEL:
10085 if (wParam & (MK_SHIFT | MK_CONTROL))
10086 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10087 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
10089 case WM_WINDOWPOSCHANGED:
10090 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
10092 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
10093 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
10094 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
10096 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
10098 MEASUREITEMSTRUCT mis;
10099 mis.CtlType = ODT_LISTVIEW;
10100 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
10101 mis.itemID = -1;
10102 mis.itemWidth = 0;
10103 mis.itemData = 0;
10104 mis.itemHeight= infoPtr->nItemHeight;
10105 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
10106 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
10107 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
10110 LISTVIEW_UpdateSize(infoPtr);
10111 LISTVIEW_UpdateScroll(infoPtr);
10113 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10115 /* case WM_WININICHANGE: */
10117 default:
10118 if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
10119 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
10121 fwd_msg:
10122 /* call default window procedure */
10123 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10128 /***
10129 * DESCRIPTION:
10130 * Registers the window class.
10132 * PARAMETER(S):
10133 * None
10135 * RETURN:
10136 * None
10138 void LISTVIEW_Register(void)
10140 WNDCLASSW wndClass;
10142 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
10143 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
10144 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
10145 wndClass.cbClsExtra = 0;
10146 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
10147 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
10148 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
10149 wndClass.lpszClassName = WC_LISTVIEWW;
10150 RegisterClassW(&wndClass);
10153 /***
10154 * DESCRIPTION:
10155 * Unregisters the window class.
10157 * PARAMETER(S):
10158 * None
10160 * RETURN:
10161 * None
10163 void LISTVIEW_Unregister(void)
10165 UnregisterClassW(WC_LISTVIEWW, NULL);
10168 /***
10169 * DESCRIPTION:
10170 * Handle any WM_COMMAND messages
10172 * PARAMETER(S):
10173 * [I] infoPtr : valid pointer to the listview structure
10174 * [I] wParam : the first message parameter
10175 * [I] lParam : the second message parameter
10177 * RETURN:
10178 * Zero.
10180 static LRESULT LISTVIEW_Command(const LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
10182 switch (HIWORD(wParam))
10184 case EN_UPDATE:
10187 * Adjust the edit window size
10189 WCHAR buffer[1024];
10190 HDC hdc = GetDC(infoPtr->hwndEdit);
10191 HFONT hFont, hOldFont = 0;
10192 RECT rect;
10193 SIZE sz;
10195 if (!infoPtr->hwndEdit || !hdc) return 0;
10196 GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
10197 GetWindowRect(infoPtr->hwndEdit, &rect);
10199 /* Select font to get the right dimension of the string */
10200 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
10201 if(hFont != 0)
10203 hOldFont = SelectObject(hdc, hFont);
10206 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
10208 TEXTMETRICW textMetric;
10210 /* Add Extra spacing for the next character */
10211 GetTextMetricsW(hdc, &textMetric);
10212 sz.cx += (textMetric.tmMaxCharWidth * 2);
10214 SetWindowPos (
10215 infoPtr->hwndEdit,
10216 HWND_TOP,
10219 sz.cx,
10220 rect.bottom - rect.top,
10221 SWP_DRAWFRAME|SWP_NOMOVE);
10223 if(hFont != 0)
10224 SelectObject(hdc, hOldFont);
10226 ReleaseDC(infoPtr->hwndEdit, hdc);
10228 break;
10231 default:
10232 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
10235 return 0;
10239 /***
10240 * DESCRIPTION:
10241 * Subclassed edit control windproc function
10243 * PARAMETER(S):
10244 * [I] hwnd : the edit window handle
10245 * [I] uMsg : the message that is to be processed
10246 * [I] wParam : first message parameter
10247 * [I] lParam : second message parameter
10248 * [I] isW : TRUE if input is Unicode
10250 * RETURN:
10251 * Zero.
10253 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
10255 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
10256 BOOL cancel = FALSE;
10258 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10259 hwnd, uMsg, wParam, lParam, isW);
10261 switch (uMsg)
10263 case WM_GETDLGCODE:
10264 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
10266 case WM_KILLFOCUS:
10267 break;
10269 case WM_DESTROY:
10271 WNDPROC editProc = infoPtr->EditWndProc;
10272 infoPtr->EditWndProc = 0;
10273 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
10274 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
10277 case WM_KEYDOWN:
10278 if (VK_ESCAPE == (INT)wParam)
10280 cancel = TRUE;
10281 break;
10283 else if (VK_RETURN == (INT)wParam)
10284 break;
10286 default:
10287 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
10290 /* kill the edit */
10291 if (infoPtr->hwndEdit)
10293 LPWSTR buffer = NULL;
10295 infoPtr->hwndEdit = 0;
10296 if (!cancel)
10298 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
10300 if (len)
10302 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
10304 if (isW) GetWindowTextW(hwnd, buffer, len+1);
10305 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
10309 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
10311 Free(buffer);
10314 SendMessageW(hwnd, WM_CLOSE, 0, 0);
10315 return 0;
10318 /***
10319 * DESCRIPTION:
10320 * Subclassed edit control Unicode windproc function
10322 * PARAMETER(S):
10323 * [I] hwnd : the edit window handle
10324 * [I] uMsg : the message that is to be processed
10325 * [I] wParam : first message parameter
10326 * [I] lParam : second message parameter
10328 * RETURN:
10330 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10332 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
10335 /***
10336 * DESCRIPTION:
10337 * Subclassed edit control ANSI windproc function
10339 * PARAMETER(S):
10340 * [I] hwnd : the edit window handle
10341 * [I] uMsg : the message that is to be processed
10342 * [I] wParam : first message parameter
10343 * [I] lParam : second message parameter
10345 * RETURN:
10347 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10349 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
10352 /***
10353 * DESCRIPTION:
10354 * Creates a subclassed edit control
10356 * PARAMETER(S):
10357 * [I] infoPtr : valid pointer to the listview structure
10358 * [I] text : initial text for the edit
10359 * [I] style : the window style
10360 * [I] isW : TRUE if input is Unicode
10362 * RETURN:
10364 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
10365 INT x, INT y, INT width, INT height, BOOL isW)
10367 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
10368 HWND hedit;
10369 SIZE sz;
10370 HDC hdc;
10371 HDC hOldFont=0;
10372 TEXTMETRICW textMetric;
10373 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
10375 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
10377 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
10378 hdc = GetDC(infoPtr->hwndSelf);
10380 /* Select the font to get appropriate metric dimensions */
10381 if(infoPtr->hFont != 0)
10382 hOldFont = SelectObject(hdc, infoPtr->hFont);
10384 /*Get String Length in pixels */
10385 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
10387 /*Add Extra spacing for the next character */
10388 GetTextMetricsW(hdc, &textMetric);
10389 sz.cx += (textMetric.tmMaxCharWidth * 2);
10391 if(infoPtr->hFont != 0)
10392 SelectObject(hdc, hOldFont);
10394 ReleaseDC(infoPtr->hwndSelf, hdc);
10395 if (isW)
10396 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10397 else
10398 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10400 if (!hedit) return 0;
10402 infoPtr->EditWndProc = (WNDPROC)
10403 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
10404 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
10406 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
10408 return hedit;