push 20d539bd3127e997ce79233e3988ba5740356ce5
[wine/hacks.git] / dlls / comctl32 / listview.c
blob169c242f41e5c82b99f0fe4bfa7f0ad636ccc931
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_StyleChanged doesn't handle some changes too well
62 * Speedups
63 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
64 * linear in the number of items in the list, and this is
65 * unacceptable for large lists.
66 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
67 * instead of inserting in the right spot
68 * -- we should keep an ordered array of coordinates in iconic mode
69 * this would allow to frame items (iterator_frameditems),
70 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
72 * Flags
73 * -- LVIF_COLUMNS
74 * -- LVIF_GROUPID
76 * States
77 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
78 * -- LVIS_CUT
79 * -- LVIS_DROPHILITED
80 * -- LVIS_OVERLAYMASK
82 * Styles
83 * -- LVS_NOLABELWRAP
84 * -- LVS_NOSCROLL (see Q137520)
85 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
86 * -- LVS_ALIGNTOP
87 * -- LVS_TYPESTYLEMASK
89 * Extended Styles
90 * -- LVS_EX_BORDERSELECT
91 * -- LVS_EX_FLATSB
92 * -- LVS_EX_HEADERDRAGDROP
93 * -- LVS_EX_INFOTIP
94 * -- LVS_EX_LABELTIP
95 * -- LVS_EX_MULTIWORKAREAS
96 * -- LVS_EX_REGIONAL
97 * -- LVS_EX_SIMPLESELECT
98 * -- LVS_EX_TWOCLICKACTIVATE
99 * -- LVS_EX_UNDERLINECOLD
100 * -- LVS_EX_UNDERLINEHOT
102 * Notifications:
103 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
104 * -- LVN_GETINFOTIP
105 * -- LVN_HOTTRACK
106 * -- LVN_MARQUEEBEGIN
107 * -- LVN_SETDISPINFO
108 * -- NM_HOVER
109 * -- LVN_BEGINRDRAG
111 * Messages:
112 * -- LVM_CANCELEDITLABEL
113 * -- LVM_ENABLEGROUPVIEW
114 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
115 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
116 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
117 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
118 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
119 * -- LVM_GETINSERTMARKRECT
120 * -- LVM_GETNUMBEROFWORKAREAS
121 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
122 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
123 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
124 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
125 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
126 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
127 * -- LVM_GETVIEW, LVM_SETVIEW
128 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
129 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
130 * -- LVM_INSERTGROUPSORTED
131 * -- LVM_INSERTMARKHITTEST
132 * -- LVM_ISGROUPVIEWENABLED
133 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
134 * -- LVM_MOVEGROUP
135 * -- LVM_MOVEITEMTOGROUP
136 * -- LVM_SETINFOTIP
137 * -- LVM_SETTILEWIDTH
138 * -- LVM_SORTGROUPS
140 * Macros:
141 * -- ListView_GetCheckSate, ListView_SetCheckState
142 * -- ListView_GetHoverTime, ListView_SetHoverTime
143 * -- ListView_GetISearchString
144 * -- ListView_GetNumberOfWorkAreas
145 * -- ListView_GetOrigin
146 * -- ListView_GetUnicodeFormat, ListView_SetUnicodeFormat
147 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
149 * Functions:
150 * -- LVGroupComparE
152 * Known differences in message stream from native control (not known if
153 * these differences cause problems):
154 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
155 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
156 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
157 * processing for "USEDOUBLECLICKTIME".
160 #include "config.h"
161 #include "wine/port.h"
163 #include <assert.h>
164 #include <ctype.h>
165 #include <string.h>
166 #include <stdlib.h>
167 #include <stdarg.h>
168 #include <stdio.h>
170 #include "windef.h"
171 #include "winbase.h"
172 #include "winnt.h"
173 #include "wingdi.h"
174 #include "winuser.h"
175 #include "winnls.h"
176 #include "commctrl.h"
177 #include "comctl32.h"
178 #include "uxtheme.h"
180 #include "wine/debug.h"
181 #include "wine/unicode.h"
183 WINE_DEFAULT_DEBUG_CHANNEL(listview);
185 /* make sure you set this to 0 for production use! */
186 #define DEBUG_RANGES 1
188 typedef struct tagCOLUMN_INFO
190 RECT rcHeader; /* tracks the header's rectangle */
191 int fmt; /* same as LVCOLUMN.fmt */
192 } COLUMN_INFO;
194 typedef struct tagITEMHDR
196 LPWSTR pszText;
197 INT iImage;
198 } ITEMHDR, *LPITEMHDR;
200 typedef struct tagSUBITEM_INFO
202 ITEMHDR hdr;
203 INT iSubItem;
204 } SUBITEM_INFO;
206 typedef struct tagITEM_INFO
208 ITEMHDR hdr;
209 UINT state;
210 LPARAM lParam;
211 INT iIndent;
212 } ITEM_INFO;
214 typedef struct tagRANGE
216 INT lower;
217 INT upper;
218 } RANGE;
220 typedef struct tagRANGES
222 HDPA hdpa;
223 } *RANGES;
225 typedef struct tagITERATOR
227 INT nItem;
228 INT nSpecial;
229 RANGE range;
230 RANGES ranges;
231 INT index;
232 } ITERATOR;
234 typedef struct tagDELAYED_ITEM_EDIT
236 BOOL fEnabled;
237 INT iItem;
238 } DELAYED_ITEM_EDIT;
240 typedef struct tagLISTVIEW_INFO
242 HWND hwndSelf;
243 HBRUSH hBkBrush;
244 COLORREF clrBk;
245 COLORREF clrText;
246 COLORREF clrTextBk;
247 HIMAGELIST himlNormal;
248 HIMAGELIST himlSmall;
249 HIMAGELIST himlState;
250 BOOL bLButtonDown;
251 BOOL bRButtonDown;
252 BOOL bDragging;
253 POINT ptClickPos; /* point where the user clicked */
254 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
255 INT nItemHeight;
256 INT nItemWidth;
257 RANGES selectionRanges;
258 INT nSelectionMark;
259 INT nHotItem;
260 SHORT notifyFormat;
261 HWND hwndNotify;
262 RECT rcList; /* This rectangle is really the window
263 * client rectangle possibly reduced by the
264 * horizontal scroll bar and/or header - see
265 * LISTVIEW_UpdateSize. This rectangle offset
266 * by the LISTVIEW_GetOrigin value is in
267 * client coordinates */
268 SIZE iconSize;
269 SIZE iconSpacing;
270 SIZE iconStateSize;
271 UINT uCallbackMask;
272 HWND hwndHeader;
273 HCURSOR hHotCursor;
274 HFONT hDefaultFont;
275 HFONT hFont;
276 INT ntmHeight; /* Some cached metrics of the font used */
277 INT ntmMaxCharWidth; /* by the listview to draw items */
278 INT nEllipsisWidth;
279 BOOL bRedraw; /* Turns on/off repaints & invalidations */
280 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
281 BOOL bFocus;
282 BOOL bDoChangeNotify; /* send change notification messages? */
283 INT nFocusedItem;
284 RECT rcFocus;
285 DWORD dwStyle; /* the cached window GWL_STYLE */
286 DWORD dwLvExStyle; /* extended listview style */
287 INT nItemCount; /* the number of items in the list */
288 HDPA hdpaItems; /* array ITEM_INFO pointers */
289 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
290 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
291 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
292 POINT currIconPos; /* this is the position next icon will be placed */
293 PFNLVCOMPARE pfnCompare;
294 LPARAM lParamSort;
295 HWND hwndEdit;
296 WNDPROC EditWndProc;
297 INT nEditLabelItem;
298 DWORD dwHoverTime;
299 HWND hwndToolTip;
301 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
303 DWORD lastKeyPressTimestamp;
304 WPARAM charCode;
305 INT nSearchParamLength;
306 WCHAR szSearchParam[ MAX_PATH ];
307 BOOL bIsDrawing;
308 INT nMeasureItemHeight;
309 INT xTrackLine; /* The x coefficient of the track line or -1 if none */
310 DELAYED_ITEM_EDIT itemEdit; /* Pointer to this structure will be the timer ID */
312 DWORD iVersion; /* CCM_[G,S]ETVERSION */
313 } LISTVIEW_INFO;
316 * constants
318 /* How many we debug buffer to allocate */
319 #define DEBUG_BUFFERS 20
320 /* The size of a single debug bbuffer */
321 #define DEBUG_BUFFER_SIZE 256
323 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
324 #define SB_INTERNAL -1
326 /* maximum size of a label */
327 #define DISP_TEXT_SIZE 512
329 /* padding for items in list and small icon display modes */
330 #define WIDTH_PADDING 12
332 /* padding for items in list, report and small icon display modes */
333 #define HEIGHT_PADDING 1
335 /* offset of items in report display mode */
336 #define REPORT_MARGINX 2
338 /* padding for icon in large icon display mode
339 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
340 * that HITTEST will see.
341 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
342 * ICON_TOP_PADDING - sum of the two above.
343 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
344 * LABEL_HOR_PADDING - between text and sides of box
345 * LABEL_VERT_PADDING - between bottom of text and end of box
347 * ICON_LR_PADDING - additional width above icon size.
348 * ICON_LR_HALF - half of the above value
350 #define ICON_TOP_PADDING_NOTHITABLE 2
351 #define ICON_TOP_PADDING_HITABLE 2
352 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
353 #define ICON_BOTTOM_PADDING 4
354 #define LABEL_HOR_PADDING 5
355 #define LABEL_VERT_PADDING 7
356 #define ICON_LR_PADDING 16
357 #define ICON_LR_HALF (ICON_LR_PADDING/2)
359 /* default label width for items in list and small icon display modes */
360 #define DEFAULT_LABEL_WIDTH 40
362 /* default column width for items in list display mode */
363 #define DEFAULT_COLUMN_WIDTH 128
365 /* Size of "line" scroll for V & H scrolls */
366 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
368 /* Padding between image and label */
369 #define IMAGE_PADDING 2
371 /* Padding behind the label */
372 #define TRAILING_LABEL_PADDING 12
373 #define TRAILING_HEADER_PADDING 11
375 /* Border for the icon caption */
376 #define CAPTION_BORDER 2
378 /* Standard DrawText flags */
379 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
380 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
381 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
383 /* Image index from state */
384 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
386 /* The time in milliseconds to reset the search in the list */
387 #define KEY_DELAY 450
389 /* Dump the LISTVIEW_INFO structure to the debug channel */
390 #define LISTVIEW_DUMP(iP) do { \
391 TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
392 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
393 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
394 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
395 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
396 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
397 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
398 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
399 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
400 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
401 } while(0)
403 static const WCHAR themeClass[] = {'L','i','s','t','V','i','e','w',0};
406 * forward declarations
408 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *, LPLVITEMW, BOOL);
409 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *, INT, LPRECT);
410 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *, INT, LPPOINT);
411 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *, INT, LPPOINT);
412 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *, INT, LPRECT);
413 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *, INT);
414 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *, LPPOINT);
415 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *, LPRECT);
416 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
417 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LVITEMW *, BOOL);
418 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *);
419 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
420 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
421 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
422 static LRESULT LISTVIEW_Command(const LISTVIEW_INFO *, WPARAM, LPARAM);
423 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *, LPCWSTR, BOOL);
424 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT, BOOL);
425 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *, INT, UINT);
426 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
427 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
428 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
429 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *);
430 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
431 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
432 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
433 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
435 /******** Text handling functions *************************************/
437 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
438 * text string. The string may be ANSI or Unicode, in which case
439 * the boolean isW tells us the type of the string.
441 * The name of the function tell what type of strings it expects:
442 * W: Unicode, T: ANSI/Unicode - function of isW
445 static inline BOOL is_textW(LPCWSTR text)
447 return text != NULL && text != LPSTR_TEXTCALLBACKW;
450 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
452 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
453 return is_textW(text);
456 static inline int textlenT(LPCWSTR text, BOOL isW)
458 return !is_textT(text, isW) ? 0 :
459 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
462 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
464 if (isDestW)
465 if (isSrcW) lstrcpynW(dest, src, max);
466 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
467 else
468 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
469 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
472 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
474 LPWSTR wstr = (LPWSTR)text;
476 if (!isW && is_textT(text, isW))
478 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
479 wstr = Alloc(len * sizeof(WCHAR));
480 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
482 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
483 return wstr;
486 static inline void textfreeT(LPWSTR wstr, BOOL isW)
488 if (!isW && is_textT(wstr, isW)) Free (wstr);
492 * dest is a pointer to a Unicode string
493 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
495 static BOOL textsetptrT(LPWSTR *dest, LPCWSTR src, BOOL isW)
497 BOOL bResult = TRUE;
499 if (src == LPSTR_TEXTCALLBACKW)
501 if (is_textW(*dest)) Free(*dest);
502 *dest = LPSTR_TEXTCALLBACKW;
504 else
506 LPWSTR pszText = textdupTtoW(src, isW);
507 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
508 bResult = Str_SetPtrW(dest, pszText);
509 textfreeT(pszText, isW);
511 return bResult;
515 * compares a Unicode to a Unicode/ANSI text string
517 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
519 if (!aw) return bt ? -1 : 0;
520 if (!bt) return aw ? 1 : 0;
521 if (aw == LPSTR_TEXTCALLBACKW)
522 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
523 if (bt != LPSTR_TEXTCALLBACKW)
525 LPWSTR bw = textdupTtoW(bt, isW);
526 int r = bw ? lstrcmpW(aw, bw) : 1;
527 textfreeT(bw, isW);
528 return r;
531 return 1;
534 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
536 int res;
538 n = min(min(n, lstrlenW(s1)), lstrlenW(s2));
539 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
540 return res ? res - sizeof(WCHAR) : res;
543 /******** Debugging functions *****************************************/
545 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
547 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
548 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
551 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
553 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
554 n = min(textlenT(text, isW), n);
555 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
558 static char* debug_getbuf(void)
560 static int index = 0;
561 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
562 return buffers[index++ % DEBUG_BUFFERS];
565 static inline const char* debugrange(const RANGE *lprng)
567 if (!lprng) return "(null)";
568 return wine_dbg_sprintf("[%d, %d]", lprng->lower, lprng->upper);
571 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
573 char* buf = debug_getbuf(), *text = buf;
574 int len, size = DEBUG_BUFFER_SIZE;
576 if (pScrollInfo == NULL) return "(null)";
577 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
578 if (len == -1) goto end; buf += len; size -= len;
579 if (pScrollInfo->fMask & SIF_RANGE)
580 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
581 else len = 0;
582 if (len == -1) goto end; buf += len; size -= len;
583 if (pScrollInfo->fMask & SIF_PAGE)
584 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
585 else len = 0;
586 if (len == -1) goto end; buf += len; size -= len;
587 if (pScrollInfo->fMask & SIF_POS)
588 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
589 else len = 0;
590 if (len == -1) goto end; buf += len; size -= len;
591 if (pScrollInfo->fMask & SIF_TRACKPOS)
592 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
593 else len = 0;
594 if (len == -1) goto end; buf += len; size -= len;
595 goto undo;
596 end:
597 buf = text + strlen(text);
598 undo:
599 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
600 return text;
603 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
605 if (!plvnm) return "(null)";
606 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
607 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
608 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
609 plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam);
612 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
614 char* buf = debug_getbuf(), *text = buf;
615 int len, size = DEBUG_BUFFER_SIZE;
617 if (lpLVItem == NULL) return "(null)";
618 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
619 if (len == -1) goto end; buf += len; size -= len;
620 if (lpLVItem->mask & LVIF_STATE)
621 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
622 else len = 0;
623 if (len == -1) goto end; buf += len; size -= len;
624 if (lpLVItem->mask & LVIF_TEXT)
625 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
626 else len = 0;
627 if (len == -1) goto end; buf += len; size -= len;
628 if (lpLVItem->mask & LVIF_IMAGE)
629 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
630 else len = 0;
631 if (len == -1) goto end; buf += len; size -= len;
632 if (lpLVItem->mask & LVIF_PARAM)
633 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
634 else len = 0;
635 if (len == -1) goto end; buf += len; size -= len;
636 if (lpLVItem->mask & LVIF_INDENT)
637 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
638 else len = 0;
639 if (len == -1) goto end; buf += len; size -= len;
640 goto undo;
641 end:
642 buf = text + strlen(text);
643 undo:
644 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
645 return text;
648 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
650 char* buf = debug_getbuf(), *text = buf;
651 int len, size = DEBUG_BUFFER_SIZE;
653 if (lpColumn == NULL) return "(null)";
654 len = snprintf(buf, size, "{");
655 if (len == -1) goto end; buf += len; size -= len;
656 if (lpColumn->mask & LVCF_SUBITEM)
657 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
658 else len = 0;
659 if (len == -1) goto end; buf += len; size -= len;
660 if (lpColumn->mask & LVCF_FMT)
661 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
662 else len = 0;
663 if (len == -1) goto end; buf += len; size -= len;
664 if (lpColumn->mask & LVCF_WIDTH)
665 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
666 else len = 0;
667 if (len == -1) goto end; buf += len; size -= len;
668 if (lpColumn->mask & LVCF_TEXT)
669 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
670 else len = 0;
671 if (len == -1) goto end; buf += len; size -= len;
672 if (lpColumn->mask & LVCF_IMAGE)
673 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
674 else len = 0;
675 if (len == -1) goto end; buf += len; size -= len;
676 if (lpColumn->mask & LVCF_ORDER)
677 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
678 else len = 0;
679 if (len == -1) goto end; buf += len; size -= len;
680 goto undo;
681 end:
682 buf = text + strlen(text);
683 undo:
684 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
685 return text;
688 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
690 if (!lpht) return "(null)";
692 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
693 wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
696 /* Return the corresponding text for a given scroll value */
697 static inline LPCSTR debugscrollcode(int nScrollCode)
699 switch(nScrollCode)
701 case SB_LINELEFT: return "SB_LINELEFT";
702 case SB_LINERIGHT: return "SB_LINERIGHT";
703 case SB_PAGELEFT: return "SB_PAGELEFT";
704 case SB_PAGERIGHT: return "SB_PAGERIGHT";
705 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
706 case SB_THUMBTRACK: return "SB_THUMBTRACK";
707 case SB_ENDSCROLL: return "SB_ENDSCROLL";
708 case SB_INTERNAL: return "SB_INTERNAL";
709 default: return "unknown";
714 /******** Notification functions ************************************/
716 static LRESULT notify_forward_header(const LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
718 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
719 (WPARAM)lpnmh->hdr.idFrom, (LPARAM)lpnmh);
722 static LRESULT notify_hdr(const LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
724 LRESULT result;
726 TRACE("(code=%d)\n", code);
728 pnmh->hwndFrom = infoPtr->hwndSelf;
729 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
730 pnmh->code = code;
731 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, pnmh->idFrom, (LPARAM)pnmh);
733 TRACE(" <= %ld\n", result);
735 return result;
738 static inline BOOL notify(const LISTVIEW_INFO *infoPtr, INT code)
740 NMHDR nmh;
741 HWND hwnd = infoPtr->hwndSelf;
742 notify_hdr(infoPtr, code, &nmh);
743 return IsWindow(hwnd);
746 static inline void notify_itemactivate(const LISTVIEW_INFO *infoPtr, const LVHITTESTINFO *htInfo)
748 NMITEMACTIVATE nmia;
749 LVITEMW item;
751 if (htInfo) {
752 nmia.uNewState = 0;
753 nmia.uOldState = 0;
754 nmia.uChanged = 0;
755 nmia.uKeyFlags = 0;
757 item.mask = LVIF_PARAM|LVIF_STATE;
758 item.iItem = htInfo->iItem;
759 item.iSubItem = 0;
760 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
761 nmia.lParam = item.lParam;
762 nmia.uOldState = item.state;
763 nmia.uNewState = item.state | LVIS_ACTIVATING;
764 nmia.uChanged = LVIF_STATE;
767 nmia.iItem = htInfo->iItem;
768 nmia.iSubItem = htInfo->iSubItem;
769 nmia.ptAction = htInfo->pt;
771 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
772 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
773 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
775 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
778 static inline LRESULT notify_listview(const LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
780 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
781 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
784 static BOOL notify_click(const LISTVIEW_INFO *infoPtr, INT code, const LVHITTESTINFO *lvht)
786 NMLISTVIEW nmlv;
787 LVITEMW item;
788 HWND hwnd = infoPtr->hwndSelf;
790 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
791 ZeroMemory(&nmlv, sizeof(nmlv));
792 nmlv.iItem = lvht->iItem;
793 nmlv.iSubItem = lvht->iSubItem;
794 nmlv.ptAction = lvht->pt;
795 item.mask = LVIF_PARAM;
796 item.iItem = lvht->iItem;
797 item.iSubItem = 0;
798 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
799 notify_listview(infoPtr, code, &nmlv);
800 return IsWindow(hwnd);
803 static BOOL notify_deleteitem(const LISTVIEW_INFO *infoPtr, INT nItem)
805 NMLISTVIEW nmlv;
806 LVITEMW item;
807 HWND hwnd = infoPtr->hwndSelf;
809 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
810 nmlv.iItem = nItem;
811 item.mask = LVIF_PARAM;
812 item.iItem = nItem;
813 item.iSubItem = 0;
814 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
815 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
816 return IsWindow(hwnd);
819 static int get_ansi_notification(UINT unicodeNotificationCode)
821 switch (unicodeNotificationCode)
823 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
824 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
825 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
826 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
827 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
828 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
830 ERR("unknown notification %x\n", unicodeNotificationCode);
831 assert(FALSE);
832 return 0;
836 Send notification. depends on dispinfoW having same
837 structure as dispinfoA.
838 infoPtr : listview struct
839 notificationCode : *Unicode* notification code
840 pdi : dispinfo structure (can be unicode or ansi)
841 isW : TRUE if dispinfo is Unicode
843 static BOOL notify_dispinfoT(const LISTVIEW_INFO *infoPtr, UINT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
845 BOOL bResult = FALSE;
846 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
847 INT cchTempBufMax = 0, savCchTextMax = 0;
848 UINT realNotifCode;
849 LPWSTR pszTempBuf = NULL, savPszText = NULL;
851 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
853 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
854 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
857 if (convertToAnsi || convertToUnicode)
859 if (notificationCode != LVN_GETDISPINFOW)
861 cchTempBufMax = convertToUnicode ?
862 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
863 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
865 else
867 cchTempBufMax = pdi->item.cchTextMax;
868 *pdi->item.pszText = 0; /* make sure we don't process garbage */
871 pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
872 if (!pszTempBuf) return FALSE;
874 if (convertToUnicode)
875 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
876 pszTempBuf, cchTempBufMax);
877 else
878 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
879 cchTempBufMax, NULL, NULL);
881 savCchTextMax = pdi->item.cchTextMax;
882 savPszText = pdi->item.pszText;
883 pdi->item.pszText = pszTempBuf;
884 pdi->item.cchTextMax = cchTempBufMax;
887 if (infoPtr->notifyFormat == NFR_ANSI)
888 realNotifCode = get_ansi_notification(notificationCode);
889 else
890 realNotifCode = notificationCode;
891 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
892 bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
894 if (convertToUnicode || convertToAnsi)
896 if (convertToUnicode) /* note : pointer can be changed by app ! */
897 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
898 savCchTextMax, NULL, NULL);
899 else
900 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
901 savPszText, savCchTextMax);
902 pdi->item.pszText = savPszText; /* restores our buffer */
903 pdi->item.cchTextMax = savCchTextMax;
904 Free (pszTempBuf);
906 return bResult;
909 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, const LISTVIEW_INFO *infoPtr, HDC hdc,
910 const RECT *rcBounds, const LVITEMW *lplvItem)
912 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
913 lpnmlvcd->nmcd.hdc = hdc;
914 lpnmlvcd->nmcd.rc = *rcBounds;
915 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
916 lpnmlvcd->clrText = infoPtr->clrText;
917 if (!lplvItem) return;
918 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
919 lpnmlvcd->iSubItem = lplvItem->iSubItem;
920 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
921 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
922 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
923 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
926 static inline DWORD notify_customdraw (const LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
928 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
929 DWORD result;
931 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
932 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
933 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
934 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
935 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
936 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
937 return result;
940 static void prepaint_setup (const LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd, BOOL SubItem)
942 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
943 lpnmlvcd->clrTextBk = comctl32_color.clrWindow;
944 if (lpnmlvcd->clrText == CLR_DEFAULT)
945 lpnmlvcd->clrText = comctl32_color.clrWindowText;
947 /* apparently, for selected items, we have to override the returned values */
948 if (!SubItem)
950 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
952 if (infoPtr->bFocus)
954 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
955 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
957 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
959 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
960 lpnmlvcd->clrText = comctl32_color.clrBtnText;
965 /* Set the text attributes */
966 if (lpnmlvcd->clrTextBk != CLR_NONE)
968 SetBkMode(hdc, OPAQUE);
969 SetBkColor(hdc,lpnmlvcd->clrTextBk);
971 else
972 SetBkMode(hdc, TRANSPARENT);
973 SetTextColor(hdc, lpnmlvcd->clrText);
976 static inline DWORD notify_postpaint (const LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
978 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
981 /******** Item iterator functions **********************************/
983 static RANGES ranges_create(int count);
984 static void ranges_destroy(RANGES ranges);
985 static BOOL ranges_add(RANGES ranges, RANGE range);
986 static BOOL ranges_del(RANGES ranges, RANGE range);
987 static void ranges_dump(RANGES ranges);
989 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
991 RANGE range = { nItem, nItem + 1 };
993 return ranges_add(ranges, range);
996 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
998 RANGE range = { nItem, nItem + 1 };
1000 return ranges_del(ranges, range);
1003 /***
1004 * ITERATOR DOCUMENTATION
1006 * The iterator functions allow for easy, and convenient iteration
1007 * over items of interest in the list. Typically, you create a
1008 * iterator, use it, and destroy it, as such:
1009 * ITERATOR i;
1011 * iterator_xxxitems(&i, ...);
1012 * while (iterator_{prev,next}(&i)
1014 * //code which uses i.nItem
1016 * iterator_destroy(&i);
1018 * where xxx is either: framed, or visible.
1019 * Note that it is important that the code destroys the iterator
1020 * after it's done with it, as the creation of the iterator may
1021 * allocate memory, which thus needs to be freed.
1023 * You can iterate both forwards, and backwards through the list,
1024 * by using iterator_next or iterator_prev respectively.
1026 * Lower numbered items are draw on top of higher number items in
1027 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1028 * items may overlap). So, to test items, you should use
1029 * iterator_next
1030 * which lists the items top to bottom (in Z-order).
1031 * For drawing items, you should use
1032 * iterator_prev
1033 * which lists the items bottom to top (in Z-order).
1034 * If you keep iterating over the items after the end-of-items
1035 * marker (-1) is returned, the iterator will start from the
1036 * beginning. Typically, you don't need to test for -1,
1037 * because iterator_{next,prev} will return TRUE if more items
1038 * are to be iterated over, or FALSE otherwise.
1040 * Note: the iterator is defined to be bidirectional. That is,
1041 * any number of prev followed by any number of next, or
1042 * five versa, should leave the iterator at the same item:
1043 * prev * n, next * n = next * n, prev * n
1045 * The iterator has a notion of an out-of-order, special item,
1046 * which sits at the start of the list. This is used in
1047 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1048 * which needs to be first, as it may overlap other items.
1050 * The code is a bit messy because we have:
1051 * - a special item to deal with
1052 * - simple range, or composite range
1053 * - empty range.
1054 * If you find bugs, or want to add features, please make sure you
1055 * always check/modify *both* iterator_prev, and iterator_next.
1058 /****
1059 * This function iterates through the items in increasing order,
1060 * but prefixed by the special item, then -1. That is:
1061 * special, 1, 2, 3, ..., n, -1.
1062 * Each item is listed only once.
1064 static inline BOOL iterator_next(ITERATOR* i)
1066 if (i->nItem == -1)
1068 i->nItem = i->nSpecial;
1069 if (i->nItem != -1) return TRUE;
1071 if (i->nItem == i->nSpecial)
1073 if (i->ranges) i->index = 0;
1074 goto pickarange;
1077 i->nItem++;
1078 testitem:
1079 if (i->nItem == i->nSpecial) i->nItem++;
1080 if (i->nItem < i->range.upper) return TRUE;
1082 pickarange:
1083 if (i->ranges)
1085 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1086 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1087 else goto end;
1089 else if (i->nItem >= i->range.upper) goto end;
1091 i->nItem = i->range.lower;
1092 if (i->nItem >= 0) goto testitem;
1093 end:
1094 i->nItem = -1;
1095 return FALSE;
1098 /****
1099 * This function iterates through the items in decreasing order,
1100 * followed by the special item, then -1. That is:
1101 * n, n-1, ..., 3, 2, 1, special, -1.
1102 * Each item is listed only once.
1104 static inline BOOL iterator_prev(ITERATOR* i)
1106 BOOL start = FALSE;
1108 if (i->nItem == -1)
1110 start = TRUE;
1111 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1112 goto pickarange;
1114 if (i->nItem == i->nSpecial)
1116 i->nItem = -1;
1117 return FALSE;
1120 testitem:
1121 i->nItem--;
1122 if (i->nItem == i->nSpecial) i->nItem--;
1123 if (i->nItem >= i->range.lower) return TRUE;
1125 pickarange:
1126 if (i->ranges)
1128 if (i->index > 0)
1129 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1130 else goto end;
1132 else if (!start && i->nItem < i->range.lower) goto end;
1134 i->nItem = i->range.upper;
1135 if (i->nItem > 0) goto testitem;
1136 end:
1137 return (i->nItem = i->nSpecial) != -1;
1140 static RANGE iterator_range(const ITERATOR *i)
1142 RANGE range;
1144 if (!i->ranges) return i->range;
1146 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1148 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1149 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1151 else range.lower = range.upper = 0;
1153 return range;
1156 /***
1157 * Releases resources associated with this ierator.
1159 static inline void iterator_destroy(const ITERATOR *i)
1161 ranges_destroy(i->ranges);
1164 /***
1165 * Create an empty iterator.
1167 static inline BOOL iterator_empty(ITERATOR* i)
1169 ZeroMemory(i, sizeof(*i));
1170 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1171 return TRUE;
1174 /***
1175 * Create an iterator over a range.
1177 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1179 iterator_empty(i);
1180 i->range = range;
1181 return TRUE;
1184 /***
1185 * Create an iterator over a bunch of ranges.
1186 * Please note that the iterator will take ownership of the ranges,
1187 * and will free them upon destruction.
1189 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1191 iterator_empty(i);
1192 i->ranges = ranges;
1193 return TRUE;
1196 /***
1197 * Creates an iterator over the items which intersect lprc.
1199 static BOOL iterator_frameditems(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *lprc)
1201 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1202 RECT frame = *lprc, rcItem, rcTemp;
1203 POINT Origin;
1205 /* in case we fail, we want to return an empty iterator */
1206 if (!iterator_empty(i)) return FALSE;
1208 LISTVIEW_GetOrigin(infoPtr, &Origin);
1210 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc));
1211 OffsetRect(&frame, -Origin.x, -Origin.y);
1213 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1215 INT nItem;
1217 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1219 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1220 if (IntersectRect(&rcTemp, &rcItem, lprc))
1221 i->nSpecial = infoPtr->nFocusedItem;
1223 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1224 /* to do better here, we need to have PosX, and PosY sorted */
1225 TRACE("building icon ranges:\n");
1226 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1228 rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1229 rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1230 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1231 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1232 if (IntersectRect(&rcTemp, &rcItem, &frame))
1233 ranges_additem(i->ranges, nItem);
1235 return TRUE;
1237 else if (uView == LVS_REPORT)
1239 RANGE range;
1241 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1242 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1244 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1245 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1246 if (range.upper <= range.lower) return TRUE;
1247 if (!iterator_rangeitems(i, range)) return FALSE;
1248 TRACE(" report=%s\n", debugrange(&i->range));
1250 else
1252 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1253 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1254 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1255 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1256 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1257 INT lower = nFirstCol * nPerCol + nFirstRow;
1258 RANGE item_range;
1259 INT nCol;
1261 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1262 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1264 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1266 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1267 TRACE("building list ranges:\n");
1268 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1270 item_range.lower = nCol * nPerCol + nFirstRow;
1271 if(item_range.lower >= infoPtr->nItemCount) break;
1272 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1273 TRACE(" list=%s\n", debugrange(&item_range));
1274 ranges_add(i->ranges, item_range);
1278 return TRUE;
1281 /***
1282 * Creates an iterator over the items which intersect the visible region of hdc.
1284 static BOOL iterator_visibleitems(ITERATOR *i, const LISTVIEW_INFO *infoPtr, HDC hdc)
1286 POINT Origin, Position;
1287 RECT rcItem, rcClip;
1288 INT rgntype;
1290 rgntype = GetClipBox(hdc, &rcClip);
1291 if (rgntype == NULLREGION) return iterator_empty(i);
1292 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1293 if (rgntype == SIMPLEREGION) return TRUE;
1295 /* first deal with the special item */
1296 if (i->nSpecial != -1)
1298 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1299 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1302 /* if we can't deal with the region, we'll just go with the simple range */
1303 LISTVIEW_GetOrigin(infoPtr, &Origin);
1304 TRACE("building visible range:\n");
1305 if (!i->ranges && i->range.lower < i->range.upper)
1307 if (!(i->ranges = ranges_create(50))) return TRUE;
1308 if (!ranges_add(i->ranges, i->range))
1310 ranges_destroy(i->ranges);
1311 i->ranges = 0;
1312 return TRUE;
1316 /* now delete the invisible items from the list */
1317 while(iterator_next(i))
1319 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1320 rcItem.left = Position.x + Origin.x;
1321 rcItem.top = Position.y + Origin.y;
1322 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1323 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1324 if (!RectVisible(hdc, &rcItem))
1325 ranges_delitem(i->ranges, i->nItem);
1327 /* the iterator should restart on the next iterator_next */
1328 TRACE("done\n");
1330 return TRUE;
1333 /******** Misc helper functions ************************************/
1335 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1336 WPARAM wParam, LPARAM lParam, BOOL isW)
1338 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1339 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1342 static inline BOOL is_autoarrange(const LISTVIEW_INFO *infoPtr)
1344 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1346 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1347 (uView == LVS_ICON || uView == LVS_SMALLICON);
1350 static void toggle_checkbox_state(LISTVIEW_INFO *infoPtr, INT nItem)
1352 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
1353 if(state == 1 || state == 2)
1355 LVITEMW lvitem;
1356 state ^= 3;
1357 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
1358 lvitem.stateMask = LVIS_STATEIMAGEMASK;
1359 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
1363 /******** Internal API functions ************************************/
1365 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(const LISTVIEW_INFO *infoPtr, INT nSubItem)
1367 static COLUMN_INFO mainItem;
1369 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1370 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1371 return DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1374 static INT LISTVIEW_CreateHeader(LISTVIEW_INFO *infoPtr)
1376 DWORD dFlags = WS_CHILD | HDS_HORZ | HDS_FULLDRAG | HDS_DRAGDROP;
1377 HINSTANCE hInst;
1379 if (infoPtr->hwndHeader) return 0;
1381 TRACE("Creating header for list %p\n", infoPtr->hwndSelf);
1383 /* setup creation flags */
1384 dFlags |= (LVS_NOSORTHEADER & infoPtr->dwStyle) ? 0 : HDS_BUTTONS;
1385 dFlags |= (LVS_NOCOLUMNHEADER & infoPtr->dwStyle) ? HDS_HIDDEN : 0;
1387 hInst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
1389 /* create header */
1390 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL, dFlags,
1391 0, 0, 0, 0, infoPtr->hwndSelf, NULL, hInst, NULL);
1392 if (!infoPtr->hwndHeader) return -1;
1394 /* set header unicode format */
1395 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, TRUE, 0);
1397 /* set header font */
1398 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
1400 LISTVIEW_UpdateSize(infoPtr);
1402 return 0;
1405 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO *infoPtr, INT nSubItem, LPRECT lprc)
1407 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1410 static inline BOOL LISTVIEW_GetItemW(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1412 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1415 /* used to handle collapse main item column case */
1416 static inline BOOL LISTVIEW_DrawFocusRect(const LISTVIEW_INFO *infoPtr, HDC hdc)
1418 return (infoPtr->rcFocus.left < infoPtr->rcFocus.right) ?
1419 DrawFocusRect(hdc, &infoPtr->rcFocus) : FALSE;
1422 /* Listview invalidation functions: use _only_ these functions to invalidate */
1424 static inline BOOL is_redrawing(const LISTVIEW_INFO *infoPtr)
1426 return infoPtr->bRedraw;
1429 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO *infoPtr, const RECT* rect)
1431 if(!is_redrawing(infoPtr)) return;
1432 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect));
1433 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1436 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO *infoPtr, INT nItem)
1438 RECT rcBox;
1440 if(!is_redrawing(infoPtr)) return;
1441 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1442 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1445 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1447 POINT Origin, Position;
1448 RECT rcBox;
1450 if(!is_redrawing(infoPtr)) return;
1451 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1452 LISTVIEW_GetOrigin(infoPtr, &Origin);
1453 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1454 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1455 rcBox.top = 0;
1456 rcBox.bottom = infoPtr->nItemHeight;
1457 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1458 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1461 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO *infoPtr)
1463 LISTVIEW_InvalidateRect(infoPtr, NULL);
1466 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO *infoPtr, INT nColumn)
1468 RECT rcCol;
1470 if(!is_redrawing(infoPtr)) return;
1471 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1472 rcCol.top = infoPtr->rcList.top;
1473 rcCol.bottom = infoPtr->rcList.bottom;
1474 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1477 /***
1478 * DESCRIPTION:
1479 * Retrieves the number of items that can fit vertically in the client area.
1481 * PARAMETER(S):
1482 * [I] infoPtr : valid pointer to the listview structure
1484 * RETURN:
1485 * Number of items per row.
1487 static inline INT LISTVIEW_GetCountPerRow(const LISTVIEW_INFO *infoPtr)
1489 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1491 return max(nListWidth/infoPtr->nItemWidth, 1);
1494 /***
1495 * DESCRIPTION:
1496 * Retrieves the number of items that can fit horizontally in the client
1497 * area.
1499 * PARAMETER(S):
1500 * [I] infoPtr : valid pointer to the listview structure
1502 * RETURN:
1503 * Number of items per column.
1505 static inline INT LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO *infoPtr)
1507 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1509 return max(nListHeight / infoPtr->nItemHeight, 1);
1513 /*************************************************************************
1514 * LISTVIEW_ProcessLetterKeys
1516 * Processes keyboard messages generated by pressing the letter keys
1517 * on the keyboard.
1518 * What this does is perform a case insensitive search from the
1519 * current position with the following quirks:
1520 * - If two chars or more are pressed in quick succession we search
1521 * for the corresponding string (e.g. 'abc').
1522 * - If there is a delay we wipe away the current search string and
1523 * restart with just that char.
1524 * - If the user keeps pressing the same character, whether slowly or
1525 * fast, so that the search string is entirely composed of this
1526 * character ('aaaaa' for instance), then we search for first item
1527 * that starting with that character.
1528 * - If the user types the above character in quick succession, then
1529 * we must also search for the corresponding string ('aaaaa'), and
1530 * go to that string if there is a match.
1532 * PARAMETERS
1533 * [I] hwnd : handle to the window
1534 * [I] charCode : the character code, the actual character
1535 * [I] keyData : key data
1537 * RETURNS
1539 * Zero.
1541 * BUGS
1543 * - The current implementation has a list of characters it will
1544 * accept and it ignores everything else. In particular it will
1545 * ignore accentuated characters which seems to match what
1546 * Windows does. But I'm not sure it makes sense to follow
1547 * Windows there.
1548 * - We don't sound a beep when the search fails.
1550 * SEE ALSO
1552 * TREEVIEW_ProcessLetterKeys
1554 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1556 INT nItem;
1557 INT endidx,idx;
1558 LVITEMW item;
1559 WCHAR buffer[MAX_PATH];
1560 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1562 /* simple parameter checking */
1563 if (!charCode || !keyData) return 0;
1565 /* only allow the valid WM_CHARs through */
1566 if (!isalnumW(charCode) &&
1567 charCode != '.' && charCode != '`' && charCode != '!' &&
1568 charCode != '@' && charCode != '#' && charCode != '$' &&
1569 charCode != '%' && charCode != '^' && charCode != '&' &&
1570 charCode != '*' && charCode != '(' && charCode != ')' &&
1571 charCode != '-' && charCode != '_' && charCode != '+' &&
1572 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1573 charCode != '}' && charCode != '[' && charCode != '{' &&
1574 charCode != '/' && charCode != '?' && charCode != '>' &&
1575 charCode != '<' && charCode != ',' && charCode != '~')
1576 return 0;
1578 /* if there's one item or less, there is no where to go */
1579 if (infoPtr->nItemCount <= 1) return 0;
1581 /* update the search parameters */
1582 infoPtr->lastKeyPressTimestamp = GetTickCount();
1583 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1584 if (infoPtr->nSearchParamLength < MAX_PATH-1)
1585 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1586 if (infoPtr->charCode != charCode)
1587 infoPtr->charCode = charCode = 0;
1588 } else {
1589 infoPtr->charCode=charCode;
1590 infoPtr->szSearchParam[0]=charCode;
1591 infoPtr->nSearchParamLength=1;
1592 /* Redundant with the 1 char string */
1593 charCode=0;
1596 /* and search from the current position */
1597 nItem=-1;
1598 if (infoPtr->nFocusedItem >= 0) {
1599 endidx=infoPtr->nFocusedItem;
1600 idx=endidx;
1601 /* if looking for single character match,
1602 * then we must always move forward
1604 if (infoPtr->nSearchParamLength == 1)
1605 idx++;
1606 } else {
1607 endidx=infoPtr->nItemCount;
1608 idx=0;
1611 /* Let application handle this for virtual listview */
1612 if (infoPtr->dwStyle & LVS_OWNERDATA)
1614 NMLVFINDITEMW nmlv;
1615 LVFINDINFOW lvfi;
1617 ZeroMemory(&lvfi, sizeof(lvfi));
1618 lvfi.flags = (LVFI_WRAP | LVFI_PARTIAL);
1619 infoPtr->szSearchParam[infoPtr->nSearchParamLength] = '\0';
1620 lvfi.psz = infoPtr->szSearchParam;
1621 nmlv.iStart = idx;
1622 nmlv.lvfi = lvfi;
1624 nItem = notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
1626 if (nItem != -1)
1627 LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1629 return 0;
1632 do {
1633 if (idx == infoPtr->nItemCount) {
1634 if (endidx == infoPtr->nItemCount || endidx == 0)
1635 break;
1636 idx=0;
1639 /* get item */
1640 item.mask = LVIF_TEXT;
1641 item.iItem = idx;
1642 item.iSubItem = 0;
1643 item.pszText = buffer;
1644 item.cchTextMax = MAX_PATH;
1645 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1647 /* check for a match */
1648 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1649 nItem=idx;
1650 break;
1651 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1652 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1653 /* This would work but we must keep looking for a longer match */
1654 nItem=idx;
1656 idx++;
1657 } while (idx != endidx);
1659 if (nItem != -1)
1660 LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1662 return 0;
1665 /*************************************************************************
1666 * LISTVIEW_UpdateHeaderSize [Internal]
1668 * Function to resize the header control
1670 * PARAMS
1671 * [I] hwnd : handle to a window
1672 * [I] nNewScrollPos : scroll pos to set
1674 * RETURNS
1675 * None.
1677 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1679 RECT winRect;
1680 POINT point[2];
1682 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1684 if (!infoPtr->hwndHeader) return;
1686 GetWindowRect(infoPtr->hwndHeader, &winRect);
1687 point[0].x = winRect.left;
1688 point[0].y = winRect.top;
1689 point[1].x = winRect.right;
1690 point[1].y = winRect.bottom;
1692 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1693 point[0].x = -nNewScrollPos;
1694 point[1].x += nNewScrollPos;
1696 SetWindowPos(infoPtr->hwndHeader,0,
1697 point[0].x,point[0].y,point[1].x,point[1].y,
1698 (infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW |
1699 SWP_NOZORDER | SWP_NOACTIVATE);
1702 /***
1703 * DESCRIPTION:
1704 * Update the scrollbars. This functions should be called whenever
1705 * the content, size or view changes.
1707 * PARAMETER(S):
1708 * [I] infoPtr : valid pointer to the listview structure
1710 * RETURN:
1711 * None
1713 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *infoPtr)
1715 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1716 SCROLLINFO horzInfo, vertInfo;
1717 INT dx, dy;
1719 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1721 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1722 horzInfo.cbSize = sizeof(SCROLLINFO);
1723 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1725 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1726 if (uView == LVS_LIST)
1728 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1729 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1731 /* scroll by at least one column per page */
1732 if(horzInfo.nPage < infoPtr->nItemWidth)
1733 horzInfo.nPage = infoPtr->nItemWidth;
1735 horzInfo.nPage /= infoPtr->nItemWidth;
1737 else if (uView == LVS_REPORT)
1739 horzInfo.nMax = infoPtr->nItemWidth;
1741 else /* LVS_ICON, or LVS_SMALLICON */
1743 RECT rcView;
1745 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1748 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1749 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1750 dx = GetScrollPos(infoPtr->hwndSelf, SB_HORZ);
1751 dx -= SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1752 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1754 /* Setting the horizontal scroll can change the listview size
1755 * (and potentially everything else) so we need to recompute
1756 * everything again for the vertical scroll
1759 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1760 vertInfo.cbSize = sizeof(SCROLLINFO);
1761 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1763 if (uView == LVS_REPORT)
1765 vertInfo.nMax = infoPtr->nItemCount;
1767 /* scroll by at least one page */
1768 if(vertInfo.nPage < infoPtr->nItemHeight)
1769 vertInfo.nPage = infoPtr->nItemHeight;
1771 if (infoPtr->nItemHeight > 0)
1772 vertInfo.nPage /= infoPtr->nItemHeight;
1774 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1776 RECT rcView;
1778 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1781 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1782 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1783 dy = GetScrollPos(infoPtr->hwndSelf, SB_VERT);
1784 dy -= SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1785 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1787 /* Change of the range may have changed the scroll pos. If so move the content */
1788 if (dx != 0 || dy != 0)
1790 RECT listRect;
1791 listRect = infoPtr->rcList;
1792 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &listRect, &listRect, 0, 0,
1793 SW_ERASE | SW_INVALIDATE);
1796 /* Update the Header Control */
1797 if (uView == LVS_REPORT)
1799 horzInfo.fMask = SIF_POS;
1800 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1801 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1806 /***
1807 * DESCRIPTION:
1808 * Shows/hides the focus rectangle.
1810 * PARAMETER(S):
1811 * [I] infoPtr : valid pointer to the listview structure
1812 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1814 * RETURN:
1815 * None
1817 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO *infoPtr, BOOL fShow)
1819 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1820 HDC hdc;
1822 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1824 if (infoPtr->nFocusedItem < 0) return;
1826 /* we need some gymnastics in ICON mode to handle large items */
1827 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1829 RECT rcBox;
1831 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1832 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1834 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1835 return;
1839 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1841 /* for some reason, owner draw should work only in report mode */
1842 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1844 DRAWITEMSTRUCT dis;
1845 LVITEMW item;
1847 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1848 HFONT hOldFont = SelectObject(hdc, hFont);
1850 item.iItem = infoPtr->nFocusedItem;
1851 item.iSubItem = 0;
1852 item.mask = LVIF_PARAM;
1853 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1855 ZeroMemory(&dis, sizeof(dis));
1856 dis.CtlType = ODT_LISTVIEW;
1857 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1858 dis.itemID = item.iItem;
1859 dis.itemAction = ODA_FOCUS;
1860 if (fShow) dis.itemState |= ODS_FOCUS;
1861 dis.hwndItem = infoPtr->hwndSelf;
1862 dis.hDC = hdc;
1863 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1864 dis.itemData = item.lParam;
1866 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1868 SelectObject(hdc, hOldFont);
1870 else
1872 LISTVIEW_DrawFocusRect(infoPtr, hdc);
1874 done:
1875 ReleaseDC(infoPtr->hwndSelf, hdc);
1878 /***
1879 * Invalidates all visible selected items.
1881 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO *infoPtr)
1883 ITERATOR i;
1885 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1886 while(iterator_next(&i))
1888 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1889 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1891 iterator_destroy(&i);
1895 /***
1896 * DESCRIPTION: [INTERNAL]
1897 * Computes an item's (left,top) corner, relative to rcView.
1898 * That is, the position has NOT been made relative to the Origin.
1899 * This is deliberate, to avoid computing the Origin over, and
1900 * over again, when this function is called in a loop. Instead,
1901 * one can factor the computation of the Origin before the loop,
1902 * and offset the value returned by this function, on every iteration.
1904 * PARAMETER(S):
1905 * [I] infoPtr : valid pointer to the listview structure
1906 * [I] nItem : item number
1907 * [O] lpptOrig : item top, left corner
1909 * RETURN:
1910 * None.
1912 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1914 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1916 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1918 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1920 lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1921 lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1923 else if (uView == LVS_LIST)
1925 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1926 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1927 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1929 else /* LVS_REPORT */
1931 lpptPosition->x = 0;
1932 lpptPosition->y = nItem * infoPtr->nItemHeight;
1936 /***
1937 * DESCRIPTION: [INTERNAL]
1938 * Compute the rectangles of an item. This is to localize all
1939 * the computations in one place. If you are not interested in some
1940 * of these values, simply pass in a NULL -- the function is smart
1941 * enough to compute only what's necessary. The function computes
1942 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1943 * one, the BOX rectangle. This rectangle is very cheap to compute,
1944 * and is guaranteed to contain all the other rectangles. Computing
1945 * the ICON rect is also cheap, but all the others are potentially
1946 * expensive. This gives an easy and effective optimization when
1947 * searching (like point inclusion, or rectangle intersection):
1948 * first test against the BOX, and if TRUE, test against the desired
1949 * rectangle.
1950 * If the function does not have all the necessary information
1951 * to computed the requested rectangles, will crash with a
1952 * failed assertion. This is done so we catch all programming
1953 * errors, given that the function is called only from our code.
1955 * We have the following 'special' meanings for a few fields:
1956 * * If LVIS_FOCUSED is set, we assume the item has the focus
1957 * This is important in ICON mode, where it might get a larger
1958 * then usual rectangle
1960 * Please note that subitem support works only in REPORT mode.
1962 * PARAMETER(S):
1963 * [I] infoPtr : valid pointer to the listview structure
1964 * [I] lpLVItem : item to compute the measures for
1965 * [O] lprcBox : ptr to Box rectangle
1966 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1967 * [0] lprcSelectBox : ptr to select box rectangle
1968 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
1969 * [O] lprcIcon : ptr to Icon rectangle
1970 * Same as LVM_GETITEMRECT with LVIR_ICON
1971 * [O] lprcStateIcon: ptr to State Icon rectangle
1972 * [O] lprcLabel : ptr to Label rectangle
1973 * Same as LVM_GETITEMRECT with LVIR_LABEL
1975 * RETURN:
1976 * None.
1978 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1979 LPRECT lprcBox, LPRECT lprcSelectBox,
1980 LPRECT lprcIcon, LPRECT lprcStateIcon, LPRECT lprcLabel)
1982 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1983 BOOL doSelectBox = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1984 RECT Box, SelectBox, Icon, Label;
1985 COLUMN_INFO *lpColumnInfo = NULL;
1986 SIZE labelSize = { 0, 0 };
1988 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1990 /* Be smart and try to figure out the minimum we have to do */
1991 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1992 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1994 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1995 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1997 if (lprcSelectBox) doSelectBox = TRUE;
1998 if (lprcLabel) doLabel = TRUE;
1999 if (doLabel || lprcIcon || lprcStateIcon) doIcon = TRUE;
2000 if (doSelectBox)
2002 doIcon = TRUE;
2003 doLabel = TRUE;
2006 /************************************************************/
2007 /* compute the box rectangle (it should be cheap to do) */
2008 /************************************************************/
2009 if (lpLVItem->iSubItem || uView == LVS_REPORT)
2010 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
2012 if (lpLVItem->iSubItem)
2014 Box = lpColumnInfo->rcHeader;
2016 else
2018 Box.left = 0;
2019 Box.right = infoPtr->nItemWidth;
2021 Box.top = 0;
2022 Box.bottom = infoPtr->nItemHeight;
2024 /******************************************************************/
2025 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
2026 /******************************************************************/
2027 if (doIcon)
2029 LONG state_width = 0;
2031 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
2032 state_width = infoPtr->iconStateSize.cx;
2034 if (uView == LVS_ICON)
2036 Icon.left = Box.left + state_width;
2037 if (infoPtr->himlNormal)
2038 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx - state_width) / 2;
2039 Icon.top = Box.top + ICON_TOP_PADDING;
2040 Icon.right = Icon.left;
2041 Icon.bottom = Icon.top;
2042 if (infoPtr->himlNormal)
2044 Icon.right += infoPtr->iconSize.cx;
2045 Icon.bottom += infoPtr->iconSize.cy;
2048 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2050 Icon.left = Box.left + state_width;
2052 if (uView == LVS_REPORT)
2053 Icon.left += REPORT_MARGINX;
2055 Icon.top = Box.top;
2056 Icon.right = Icon.left;
2057 if (infoPtr->himlSmall &&
2058 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
2059 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
2060 Icon.right += infoPtr->iconSize.cx;
2061 Icon.bottom = Icon.top + infoPtr->iconSize.cy;
2063 if(lprcIcon) *lprcIcon = Icon;
2064 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon));
2066 /* TODO: is this correct? */
2067 if (lprcStateIcon)
2069 lprcStateIcon->left = Icon.left - state_width;
2070 lprcStateIcon->right = Icon.left;
2071 lprcStateIcon->top = Icon.top;
2072 lprcStateIcon->bottom = lprcStateIcon->top + infoPtr->iconSize.cy;
2073 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon));
2076 else Icon.right = 0;
2078 /************************************************************/
2079 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2080 /************************************************************/
2081 if (doLabel)
2083 /* calculate how far to the right can the label stretch */
2084 Label.right = Box.right;
2085 if (uView == LVS_REPORT)
2087 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
2090 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
2092 labelSize.cx = infoPtr->nItemWidth;
2093 labelSize.cy = infoPtr->nItemHeight;
2094 goto calc_label;
2097 /* we need the text in non owner draw mode */
2098 assert(lpLVItem->mask & LVIF_TEXT);
2099 if (is_textT(lpLVItem->pszText, TRUE))
2101 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2102 HDC hdc = GetDC(infoPtr->hwndSelf);
2103 HFONT hOldFont = SelectObject(hdc, hFont);
2104 UINT uFormat;
2105 RECT rcText;
2107 /* compute rough rectangle where the label will go */
2108 SetRectEmpty(&rcText);
2109 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2110 rcText.bottom = infoPtr->nItemHeight;
2111 if (uView == LVS_ICON)
2112 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2114 /* now figure out the flags */
2115 if (uView == LVS_ICON)
2116 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2117 else
2118 uFormat = LV_SL_DT_FLAGS;
2120 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2122 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2123 labelSize.cy = rcText.bottom - rcText.top;
2125 SelectObject(hdc, hOldFont);
2126 ReleaseDC(infoPtr->hwndSelf, hdc);
2129 calc_label:
2130 if (uView == LVS_ICON)
2132 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2133 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2134 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2135 Label.right = Label.left + labelSize.cx;
2136 Label.bottom = Label.top + infoPtr->nItemHeight;
2137 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2139 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2140 labelSize.cy /= infoPtr->ntmHeight;
2141 labelSize.cy = max(labelSize.cy, 1);
2142 labelSize.cy *= infoPtr->ntmHeight;
2144 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2146 else if (uView == LVS_REPORT)
2148 Label.left = Icon.right;
2149 Label.top = Box.top;
2150 Label.right = lpColumnInfo->rcHeader.right;
2151 Label.bottom = Label.top + infoPtr->nItemHeight;
2153 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2155 Label.left = Icon.right;
2156 Label.top = Box.top;
2157 Label.right = min(Label.left + labelSize.cx, Label.right);
2158 Label.bottom = Label.top + infoPtr->nItemHeight;
2161 if (lprcLabel) *lprcLabel = Label;
2162 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label));
2165 /************************************************************/
2166 /* compute STATEICON bounding box */
2167 /************************************************************/
2168 if (doSelectBox)
2170 if (uView == LVS_REPORT)
2172 SelectBox.left = Icon.right; /* FIXME: should be Icon.left */
2173 SelectBox.top = Box.top;
2174 SelectBox.bottom = Box.bottom;
2175 if (lpLVItem->iSubItem == 0)
2177 /* we need the indent in report mode */
2178 assert(lpLVItem->mask & LVIF_INDENT);
2179 SelectBox.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
2181 SelectBox.right = min(SelectBox.left + labelSize.cx, Label.right);
2183 else
2185 UnionRect(&SelectBox, &Icon, &Label);
2187 if (lprcSelectBox) *lprcSelectBox = SelectBox;
2188 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox));
2191 /* Fix the Box if necessary */
2192 if (lprcBox)
2194 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2195 else *lprcBox = Box;
2197 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box));
2200 /***
2201 * DESCRIPTION: [INTERNAL]
2203 * PARAMETER(S):
2204 * [I] infoPtr : valid pointer to the listview structure
2205 * [I] nItem : item number
2206 * [O] lprcBox : ptr to Box rectangle
2208 * RETURN:
2209 * None.
2211 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2213 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2214 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2215 POINT Position, Origin;
2216 LVITEMW lvItem;
2218 LISTVIEW_GetOrigin(infoPtr, &Origin);
2219 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2221 /* Be smart and try to figure out the minimum we have to do */
2222 lvItem.mask = 0;
2223 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2224 lvItem.mask |= LVIF_TEXT;
2225 lvItem.iItem = nItem;
2226 lvItem.iSubItem = 0;
2227 lvItem.pszText = szDispText;
2228 lvItem.cchTextMax = DISP_TEXT_SIZE;
2229 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2230 if (uView == LVS_ICON)
2232 lvItem.mask |= LVIF_STATE;
2233 lvItem.stateMask = LVIS_FOCUSED;
2234 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2236 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0, 0);
2238 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2242 /***
2243 * DESCRIPTION:
2244 * Returns the current icon position, and advances it along the top.
2245 * The returned position is not offset by Origin.
2247 * PARAMETER(S):
2248 * [I] infoPtr : valid pointer to the listview structure
2249 * [O] lpPos : will get the current icon position
2251 * RETURN:
2252 * None
2254 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2256 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2258 *lpPos = infoPtr->currIconPos;
2260 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2261 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2263 infoPtr->currIconPos.x = 0;
2264 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2268 /***
2269 * DESCRIPTION:
2270 * Returns the current icon position, and advances it down the left edge.
2271 * The returned position is not offset by Origin.
2273 * PARAMETER(S):
2274 * [I] infoPtr : valid pointer to the listview structure
2275 * [O] lpPos : will get the current icon position
2277 * RETURN:
2278 * None
2280 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2282 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2284 *lpPos = infoPtr->currIconPos;
2286 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2287 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2289 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2290 infoPtr->currIconPos.y = 0;
2294 /***
2295 * DESCRIPTION:
2296 * Moves an icon to the specified position.
2297 * It takes care of invalidating the item, etc.
2299 * PARAMETER(S):
2300 * [I] infoPtr : valid pointer to the listview structure
2301 * [I] nItem : the item to move
2302 * [I] lpPos : the new icon position
2303 * [I] isNew : flags the item as being new
2305 * RETURN:
2306 * Success: TRUE
2307 * Failure: FALSE
2309 static BOOL LISTVIEW_MoveIconTo(const LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2311 POINT old;
2313 if (!isNew)
2315 old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2316 old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2318 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2319 LISTVIEW_InvalidateItem(infoPtr, nItem);
2322 /* Allocating a POINTER for every item is too resource intensive,
2323 * so we'll keep the (x,y) in different arrays */
2324 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2325 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2327 LISTVIEW_InvalidateItem(infoPtr, nItem);
2329 return TRUE;
2332 /***
2333 * DESCRIPTION:
2334 * Arranges listview items in icon display mode.
2336 * PARAMETER(S):
2337 * [I] infoPtr : valid pointer to the listview structure
2338 * [I] nAlignCode : alignment code
2340 * RETURN:
2341 * SUCCESS : TRUE
2342 * FAILURE : FALSE
2344 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2346 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2347 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2348 POINT pos;
2349 INT i;
2351 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2353 TRACE("nAlignCode=%d\n", nAlignCode);
2355 if (nAlignCode == LVA_DEFAULT)
2357 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2358 else nAlignCode = LVA_ALIGNTOP;
2361 switch (nAlignCode)
2363 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2364 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2365 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2366 default: return FALSE;
2369 infoPtr->bAutoarrange = TRUE;
2370 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2371 for (i = 0; i < infoPtr->nItemCount; i++)
2373 next_pos(infoPtr, &pos);
2374 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2377 return TRUE;
2380 /***
2381 * DESCRIPTION:
2382 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2384 * PARAMETER(S):
2385 * [I] infoPtr : valid pointer to the listview structure
2386 * [O] lprcView : bounding rectangle
2388 * RETURN:
2389 * SUCCESS : TRUE
2390 * FAILURE : FALSE
2392 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2394 INT i, x, y;
2396 SetRectEmpty(lprcView);
2398 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2400 case LVS_ICON:
2401 case LVS_SMALLICON:
2402 for (i = 0; i < infoPtr->nItemCount; i++)
2404 x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2405 y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2406 lprcView->right = max(lprcView->right, x);
2407 lprcView->bottom = max(lprcView->bottom, y);
2409 if (infoPtr->nItemCount > 0)
2411 lprcView->right += infoPtr->nItemWidth;
2412 lprcView->bottom += infoPtr->nItemHeight;
2414 break;
2416 case LVS_LIST:
2417 y = LISTVIEW_GetCountPerColumn(infoPtr);
2418 x = infoPtr->nItemCount / y;
2419 if (infoPtr->nItemCount % y) x++;
2420 lprcView->right = x * infoPtr->nItemWidth;
2421 lprcView->bottom = y * infoPtr->nItemHeight;
2422 break;
2424 case LVS_REPORT:
2425 lprcView->right = infoPtr->nItemWidth;
2426 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2427 break;
2431 /***
2432 * DESCRIPTION:
2433 * Retrieves the bounding rectangle of all the items.
2435 * PARAMETER(S):
2436 * [I] infoPtr : valid pointer to the listview structure
2437 * [O] lprcView : bounding rectangle
2439 * RETURN:
2440 * SUCCESS : TRUE
2441 * FAILURE : FALSE
2443 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2445 POINT ptOrigin;
2447 TRACE("(lprcView=%p)\n", lprcView);
2449 if (!lprcView) return FALSE;
2451 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2452 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2453 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2455 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView));
2457 return TRUE;
2460 /***
2461 * DESCRIPTION:
2462 * Retrieves the subitem pointer associated with the subitem index.
2464 * PARAMETER(S):
2465 * [I] hdpaSubItems : DPA handle for a specific item
2466 * [I] nSubItem : index of subitem
2468 * RETURN:
2469 * SUCCESS : subitem pointer
2470 * FAILURE : NULL
2472 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2474 SUBITEM_INFO *lpSubItem;
2475 INT i;
2477 /* we should binary search here if need be */
2478 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2480 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
2481 if (lpSubItem->iSubItem == nSubItem)
2482 return lpSubItem;
2485 return NULL;
2489 /***
2490 * DESCRIPTION:
2491 * Calculates the desired item width.
2493 * PARAMETER(S):
2494 * [I] infoPtr : valid pointer to the listview structure
2496 * RETURN:
2497 * The desired item width.
2499 static INT LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO *infoPtr)
2501 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2502 INT nItemWidth = 0;
2504 TRACE("uView=%d\n", uView);
2506 if (uView == LVS_ICON)
2507 nItemWidth = infoPtr->iconSpacing.cx;
2508 else if (uView == LVS_REPORT)
2510 RECT rcHeader;
2512 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2514 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2515 nItemWidth = rcHeader.right;
2518 else /* LVS_SMALLICON, or LVS_LIST */
2520 INT i;
2522 for (i = 0; i < infoPtr->nItemCount; i++)
2523 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2525 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2526 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2528 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2531 return max(nItemWidth, 1);
2534 /***
2535 * DESCRIPTION:
2536 * Calculates the desired item height.
2538 * PARAMETER(S):
2539 * [I] infoPtr : valid pointer to the listview structure
2541 * RETURN:
2542 * The desired item height.
2544 static INT LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO *infoPtr)
2546 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2547 INT nItemHeight;
2549 TRACE("uView=%d\n", uView);
2551 if (uView == LVS_ICON)
2552 nItemHeight = infoPtr->iconSpacing.cy;
2553 else
2555 nItemHeight = infoPtr->ntmHeight;
2556 if (uView == LVS_REPORT && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
2557 nItemHeight++;
2558 if (infoPtr->himlState)
2559 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2560 if (infoPtr->himlSmall)
2561 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2562 if (infoPtr->himlState || infoPtr->himlSmall)
2563 nItemHeight += HEIGHT_PADDING;
2564 if (infoPtr->nMeasureItemHeight > 0)
2565 nItemHeight = infoPtr->nMeasureItemHeight;
2568 return max(nItemHeight, 1);
2571 /***
2572 * DESCRIPTION:
2573 * Updates the width, and height of an item.
2575 * PARAMETER(S):
2576 * [I] infoPtr : valid pointer to the listview structure
2578 * RETURN:
2579 * None.
2581 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2583 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2584 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2588 /***
2589 * DESCRIPTION:
2590 * Retrieves and saves important text metrics info for the current
2591 * Listview font.
2593 * PARAMETER(S):
2594 * [I] infoPtr : valid pointer to the listview structure
2597 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2599 HDC hdc = GetDC(infoPtr->hwndSelf);
2600 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2601 HFONT hOldFont = SelectObject(hdc, hFont);
2602 TEXTMETRICW tm;
2603 SIZE sz;
2605 if (GetTextMetricsW(hdc, &tm))
2607 infoPtr->ntmHeight = tm.tmHeight;
2608 infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
2611 if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
2612 infoPtr->nEllipsisWidth = sz.cx;
2614 SelectObject(hdc, hOldFont);
2615 ReleaseDC(infoPtr->hwndSelf, hdc);
2617 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2620 /***
2621 * DESCRIPTION:
2622 * A compare function for ranges
2624 * PARAMETER(S)
2625 * [I] range1 : pointer to range 1;
2626 * [I] range2 : pointer to range 2;
2627 * [I] flags : flags
2629 * RETURNS:
2630 * > 0 : if range 1 > range 2
2631 * < 0 : if range 2 > range 1
2632 * = 0 : if range intersects range 2
2634 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2636 INT cmp;
2638 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2639 cmp = -1;
2640 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2641 cmp = 1;
2642 else
2643 cmp = 0;
2645 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1), debugrange(range2), cmp);
2647 return cmp;
2650 #if DEBUG_RANGES
2651 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2652 #else
2653 #define ranges_check(ranges, desc) do { } while(0)
2654 #endif
2656 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2658 INT i;
2659 RANGE *prev, *curr;
2661 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2662 assert (ranges);
2663 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2664 ranges_dump(ranges);
2665 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2667 prev = DPA_GetPtr(ranges->hdpa, 0);
2668 assert (prev->lower >= 0 && prev->lower < prev->upper);
2669 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2671 curr = DPA_GetPtr(ranges->hdpa, i);
2672 assert (prev->upper <= curr->lower);
2673 assert (curr->lower < curr->upper);
2674 prev = curr;
2677 TRACE("--- Done checking---\n");
2680 static RANGES ranges_create(int count)
2682 RANGES ranges = Alloc(sizeof(struct tagRANGES));
2683 if (!ranges) return NULL;
2684 ranges->hdpa = DPA_Create(count);
2685 if (ranges->hdpa) return ranges;
2686 Free(ranges);
2687 return NULL;
2690 static void ranges_clear(RANGES ranges)
2692 INT i;
2694 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2695 Free(DPA_GetPtr(ranges->hdpa, i));
2696 DPA_DeleteAllPtrs(ranges->hdpa);
2700 static void ranges_destroy(RANGES ranges)
2702 if (!ranges) return;
2703 ranges_clear(ranges);
2704 DPA_Destroy(ranges->hdpa);
2705 Free(ranges);
2708 static RANGES ranges_clone(RANGES ranges)
2710 RANGES clone;
2711 INT i;
2713 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2715 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2717 RANGE *newrng = Alloc(sizeof(RANGE));
2718 if (!newrng) goto fail;
2719 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2720 DPA_SetPtr(clone->hdpa, i, newrng);
2722 return clone;
2724 fail:
2725 TRACE ("clone failed\n");
2726 ranges_destroy(clone);
2727 return NULL;
2730 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2732 INT i;
2734 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2735 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2737 return ranges;
2740 static void ranges_dump(RANGES ranges)
2742 INT i;
2744 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2745 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2748 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2750 RANGE srchrng = { nItem, nItem + 1 };
2752 TRACE("(nItem=%d)\n", nItem);
2753 ranges_check(ranges, "before contain");
2754 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2757 static INT ranges_itemcount(RANGES ranges)
2759 INT i, count = 0;
2761 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2763 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2764 count += sel->upper - sel->lower;
2767 return count;
2770 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2772 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2773 INT index;
2775 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2776 if (index == -1) return TRUE;
2778 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2780 chkrng = DPA_GetPtr(ranges->hdpa, index);
2781 if (chkrng->lower >= nItem)
2782 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2783 if (chkrng->upper > nItem)
2784 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2786 return TRUE;
2789 static BOOL ranges_add(RANGES ranges, RANGE range)
2791 RANGE srchrgn;
2792 INT index;
2794 TRACE("(%s)\n", debugrange(&range));
2795 ranges_check(ranges, "before add");
2797 /* try find overlapping regions first */
2798 srchrgn.lower = range.lower - 1;
2799 srchrgn.upper = range.upper + 1;
2800 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2802 if (index == -1)
2804 RANGE *newrgn;
2806 TRACE("Adding new range\n");
2808 /* create the brand new range to insert */
2809 newrgn = Alloc(sizeof(RANGE));
2810 if(!newrgn) goto fail;
2811 *newrgn = range;
2813 /* figure out where to insert it */
2814 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2815 TRACE("index=%d\n", index);
2816 if (index == -1) index = 0;
2818 /* and get it over with */
2819 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2821 Free(newrgn);
2822 goto fail;
2825 else
2827 RANGE *chkrgn, *mrgrgn;
2828 INT fromindex, mergeindex;
2830 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2831 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2833 chkrgn->lower = min(range.lower, chkrgn->lower);
2834 chkrgn->upper = max(range.upper, chkrgn->upper);
2836 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2838 /* merge now common ranges */
2839 fromindex = 0;
2840 srchrgn.lower = chkrgn->lower - 1;
2841 srchrgn.upper = chkrgn->upper + 1;
2845 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2846 if (mergeindex == -1) break;
2847 if (mergeindex == index)
2849 fromindex = index + 1;
2850 continue;
2853 TRACE("Merge with index %i\n", mergeindex);
2855 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2856 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2857 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2858 Free(mrgrgn);
2859 DPA_DeletePtr(ranges->hdpa, mergeindex);
2860 if (mergeindex < index) index --;
2861 } while(1);
2864 ranges_check(ranges, "after add");
2865 return TRUE;
2867 fail:
2868 ranges_check(ranges, "failed add");
2869 return FALSE;
2872 static BOOL ranges_del(RANGES ranges, RANGE range)
2874 RANGE *chkrgn;
2875 INT index;
2877 TRACE("(%s)\n", debugrange(&range));
2878 ranges_check(ranges, "before del");
2880 /* we don't use DPAS_SORTED here, since we need *
2881 * to find the first overlapping range */
2882 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2883 while(index != -1)
2885 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2887 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2889 /* case 1: Same range */
2890 if ( (chkrgn->upper == range.upper) &&
2891 (chkrgn->lower == range.lower) )
2893 DPA_DeletePtr(ranges->hdpa, index);
2894 break;
2896 /* case 2: engulf */
2897 else if ( (chkrgn->upper <= range.upper) &&
2898 (chkrgn->lower >= range.lower) )
2900 DPA_DeletePtr(ranges->hdpa, index);
2902 /* case 3: overlap upper */
2903 else if ( (chkrgn->upper <= range.upper) &&
2904 (chkrgn->lower < range.lower) )
2906 chkrgn->upper = range.lower;
2908 /* case 4: overlap lower */
2909 else if ( (chkrgn->upper > range.upper) &&
2910 (chkrgn->lower >= range.lower) )
2912 chkrgn->lower = range.upper;
2913 break;
2915 /* case 5: fully internal */
2916 else
2918 RANGE tmprgn = *chkrgn, *newrgn;
2920 if (!(newrgn = Alloc(sizeof(RANGE)))) goto fail;
2921 newrgn->lower = chkrgn->lower;
2922 newrgn->upper = range.lower;
2923 chkrgn->lower = range.upper;
2924 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2926 Free(newrgn);
2927 goto fail;
2929 chkrgn = &tmprgn;
2930 break;
2933 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2936 ranges_check(ranges, "after del");
2937 return TRUE;
2939 fail:
2940 ranges_check(ranges, "failed del");
2941 return FALSE;
2944 /***
2945 * DESCRIPTION:
2946 * Removes all selection ranges
2948 * Parameters(s):
2949 * [I] infoPtr : valid pointer to the listview structure
2950 * [I] toSkip : item range to skip removing the selection
2952 * RETURNS:
2953 * SUCCESS : TRUE
2954 * FAILURE : FALSE
2956 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2958 LVITEMW lvItem;
2959 ITERATOR i;
2960 RANGES clone;
2962 TRACE("()\n");
2964 lvItem.state = 0;
2965 lvItem.stateMask = LVIS_SELECTED;
2967 /* need to clone the DPA because callbacks can change it */
2968 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2969 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2970 while(iterator_next(&i))
2971 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2972 /* note that the iterator destructor will free the cloned range */
2973 iterator_destroy(&i);
2975 return TRUE;
2978 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2980 RANGES toSkip;
2982 if (!(toSkip = ranges_create(1))) return FALSE;
2983 if (nItem != -1) ranges_additem(toSkip, nItem);
2984 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2985 ranges_destroy(toSkip);
2986 return TRUE;
2989 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2991 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2994 /***
2995 * DESCRIPTION:
2996 * Retrieves the number of items that are marked as selected.
2998 * PARAMETER(S):
2999 * [I] infoPtr : valid pointer to the listview structure
3001 * RETURN:
3002 * Number of items selected.
3004 static INT LISTVIEW_GetSelectedCount(const LISTVIEW_INFO *infoPtr)
3006 INT nSelectedCount = 0;
3008 if (infoPtr->uCallbackMask & LVIS_SELECTED)
3010 INT i;
3011 for (i = 0; i < infoPtr->nItemCount; i++)
3013 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
3014 nSelectedCount++;
3017 else
3018 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
3020 TRACE("nSelectedCount=%d\n", nSelectedCount);
3021 return nSelectedCount;
3024 /***
3025 * DESCRIPTION:
3026 * Manages the item focus.
3028 * PARAMETER(S):
3029 * [I] infoPtr : valid pointer to the listview structure
3030 * [I] nItem : item index
3032 * RETURN:
3033 * TRUE : focused item changed
3034 * FALSE : focused item has NOT changed
3036 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
3038 INT oldFocus = infoPtr->nFocusedItem;
3039 LVITEMW lvItem;
3041 if (nItem == infoPtr->nFocusedItem) return FALSE;
3043 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
3044 lvItem.stateMask = LVIS_FOCUSED;
3045 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
3047 return oldFocus != infoPtr->nFocusedItem;
3050 /* Helper function for LISTVIEW_ShiftIndices *only* */
3051 static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
3053 if (nShiftItem < nItem) return nShiftItem;
3055 if (nShiftItem > nItem) return nShiftItem + direction;
3057 if (direction > 0) return nShiftItem + direction;
3059 return min(nShiftItem, infoPtr->nItemCount - 1);
3063 * DESCRIPTION:
3064 * Updates the various indices after an item has been inserted or deleted.
3066 * PARAMETER(S):
3067 * [I] infoPtr : valid pointer to the listview structure
3068 * [I] nItem : item index
3069 * [I] direction : Direction of shift, +1 or -1.
3071 * RETURN:
3072 * None
3074 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
3076 INT nNewFocus;
3077 BOOL bOldChange;
3079 /* temporarily disable change notification while shifting items */
3080 bOldChange = infoPtr->bDoChangeNotify;
3081 infoPtr->bDoChangeNotify = FALSE;
3083 TRACE("Shifting %iu, %i steps\n", nItem, direction);
3085 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
3087 assert(abs(direction) == 1);
3089 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
3091 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
3092 if (nNewFocus != infoPtr->nFocusedItem)
3093 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
3095 /* But we are not supposed to modify nHotItem! */
3097 infoPtr->bDoChangeNotify = bOldChange;
3102 * DESCRIPTION:
3103 * Adds a block of selections.
3105 * PARAMETER(S):
3106 * [I] infoPtr : valid pointer to the listview structure
3107 * [I] nItem : item index
3109 * RETURN:
3110 * Whether the window is still valid.
3112 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3114 INT nFirst = min(infoPtr->nSelectionMark, nItem);
3115 INT nLast = max(infoPtr->nSelectionMark, nItem);
3116 HWND hwndSelf = infoPtr->hwndSelf;
3117 NMLVODSTATECHANGE nmlv;
3118 LVITEMW item;
3119 BOOL bOldChange;
3120 INT i;
3122 /* Temporarily disable change notification
3123 * If the control is LVS_OWNERDATA, we need to send
3124 * only one LVN_ODSTATECHANGED notification.
3125 * See MSDN documentation for LVN_ITEMCHANGED.
3127 bOldChange = infoPtr->bDoChangeNotify;
3128 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3130 if (nFirst == -1) nFirst = nItem;
3132 item.state = LVIS_SELECTED;
3133 item.stateMask = LVIS_SELECTED;
3135 for (i = nFirst; i <= nLast; i++)
3136 LISTVIEW_SetItemState(infoPtr,i,&item);
3138 ZeroMemory(&nmlv, sizeof(nmlv));
3139 nmlv.iFrom = nFirst;
3140 nmlv.iTo = nLast;
3141 nmlv.uNewState = 0;
3142 nmlv.uOldState = item.state;
3144 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3145 if (!IsWindow(hwndSelf))
3146 return FALSE;
3147 infoPtr->bDoChangeNotify = bOldChange;
3148 return TRUE;
3152 /***
3153 * DESCRIPTION:
3154 * Sets a single group selection.
3156 * PARAMETER(S):
3157 * [I] infoPtr : valid pointer to the listview structure
3158 * [I] nItem : item index
3160 * RETURN:
3161 * None
3163 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3165 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3166 RANGES selection;
3167 LVITEMW item;
3168 ITERATOR i;
3169 BOOL bOldChange;
3171 if (!(selection = ranges_create(100))) return;
3173 item.state = LVIS_SELECTED;
3174 item.stateMask = LVIS_SELECTED;
3176 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3178 if (infoPtr->nSelectionMark == -1)
3180 infoPtr->nSelectionMark = nItem;
3181 ranges_additem(selection, nItem);
3183 else
3185 RANGE sel;
3187 sel.lower = min(infoPtr->nSelectionMark, nItem);
3188 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3189 ranges_add(selection, sel);
3192 else
3194 RECT rcItem, rcSel, rcSelMark;
3195 POINT ptItem;
3197 rcItem.left = LVIR_BOUNDS;
3198 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3199 rcSelMark.left = LVIR_BOUNDS;
3200 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3201 UnionRect(&rcSel, &rcItem, &rcSelMark);
3202 iterator_frameditems(&i, infoPtr, &rcSel);
3203 while(iterator_next(&i))
3205 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3206 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3208 iterator_destroy(&i);
3211 /* disable per item notifications on LVS_OWNERDATA style
3212 FIXME: single LVN_ODSTATECHANGED should be used */
3213 bOldChange = infoPtr->bDoChangeNotify;
3214 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3216 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3219 iterator_rangesitems(&i, selection);
3220 while(iterator_next(&i))
3221 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3222 /* this will also destroy the selection */
3223 iterator_destroy(&i);
3225 infoPtr->bDoChangeNotify = bOldChange;
3227 LISTVIEW_SetItemFocus(infoPtr, nItem);
3230 /***
3231 * DESCRIPTION:
3232 * Sets a single selection.
3234 * PARAMETER(S):
3235 * [I] infoPtr : valid pointer to the listview structure
3236 * [I] nItem : item index
3238 * RETURN:
3239 * None
3241 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3243 LVITEMW lvItem;
3245 TRACE("nItem=%d\n", nItem);
3247 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3249 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3250 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3251 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3253 infoPtr->nSelectionMark = nItem;
3256 /***
3257 * DESCRIPTION:
3258 * Set selection(s) with keyboard.
3260 * PARAMETER(S):
3261 * [I] infoPtr : valid pointer to the listview structure
3262 * [I] nItem : item index
3263 * [I] space : VK_SPACE code sent
3265 * RETURN:
3266 * SUCCESS : TRUE (needs to be repainted)
3267 * FAILURE : FALSE (nothing has changed)
3269 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem, BOOL space)
3271 /* FIXME: pass in the state */
3272 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3273 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3274 BOOL bResult = FALSE;
3276 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3277 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3279 if (infoPtr->dwStyle & LVS_SINGLESEL)
3281 bResult = TRUE;
3282 LISTVIEW_SetSelection(infoPtr, nItem);
3284 else
3286 if (wShift)
3288 bResult = TRUE;
3289 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3291 else if (wCtrl)
3293 LVITEMW lvItem;
3294 lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3295 lvItem.stateMask = LVIS_SELECTED;
3296 if (space)
3298 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3299 if (lvItem.state & LVIS_SELECTED)
3300 infoPtr->nSelectionMark = nItem;
3302 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3304 else
3306 bResult = TRUE;
3307 LISTVIEW_SetSelection(infoPtr, nItem);
3310 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3313 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3314 return bResult;
3317 static BOOL LISTVIEW_GetItemAtPt(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3319 LVHITTESTINFO lvHitTestInfo;
3321 ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3322 lvHitTestInfo.pt.x = pt.x;
3323 lvHitTestInfo.pt.y = pt.y;
3325 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3327 lpLVItem->mask = LVIF_PARAM;
3328 lpLVItem->iItem = lvHitTestInfo.iItem;
3329 lpLVItem->iSubItem = 0;
3331 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3334 static inline BOOL LISTVIEW_isHotTracking(const LISTVIEW_INFO *infoPtr)
3336 return ((infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) ||
3337 (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE) ||
3338 (infoPtr->dwLvExStyle & LVS_EX_TWOCLICKACTIVATE));
3341 /***
3342 * DESCRIPTION:
3343 * Called when the mouse is being actively tracked and has hovered for a specified
3344 * amount of time
3346 * PARAMETER(S):
3347 * [I] infoPtr : valid pointer to the listview structure
3348 * [I] fwKeys : key indicator
3349 * [I] x,y : mouse position
3351 * RETURN:
3352 * 0 if the message was processed, non-zero if there was an error
3354 * INFO:
3355 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3356 * over the item for a certain period of time.
3359 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3361 if (LISTVIEW_isHotTracking(infoPtr))
3363 LVITEMW item;
3364 POINT pt;
3366 pt.x = x;
3367 pt.y = y;
3369 if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3370 LISTVIEW_SetSelection(infoPtr, item.iItem);
3373 return 0;
3376 /***
3377 * DESCRIPTION:
3378 * Called whenever WM_MOUSEMOVE is received.
3380 * PARAMETER(S):
3381 * [I] infoPtr : valid pointer to the listview structure
3382 * [I] fwKeys : key indicator
3383 * [I] x,y : mouse position
3385 * RETURN:
3386 * 0 if the message is processed, non-zero if there was an error
3388 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3390 TRACKMOUSEEVENT trackinfo;
3392 if (!(fwKeys & MK_LBUTTON))
3393 infoPtr->bLButtonDown = FALSE;
3395 if (infoPtr->bLButtonDown)
3397 POINT tmp;
3398 RECT rect;
3399 WORD wDragWidth = GetSystemMetrics(SM_CXDRAG);
3400 WORD wDragHeight= GetSystemMetrics(SM_CYDRAG);
3402 rect.left = infoPtr->ptClickPos.x - wDragWidth;
3403 rect.right = infoPtr->ptClickPos.x + wDragWidth;
3404 rect.top = infoPtr->ptClickPos.y - wDragHeight;
3405 rect.bottom = infoPtr->ptClickPos.y + wDragHeight;
3407 tmp.x = x;
3408 tmp.y = y;
3410 if (!PtInRect(&rect, tmp))
3412 LVHITTESTINFO lvHitTestInfo;
3413 NMLISTVIEW nmlv;
3415 lvHitTestInfo.pt = infoPtr->ptClickPos;
3416 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3418 ZeroMemory(&nmlv, sizeof(nmlv));
3419 nmlv.iItem = lvHitTestInfo.iItem;
3420 nmlv.ptAction = infoPtr->ptClickPos;
3422 if (!infoPtr->bDragging)
3424 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3425 infoPtr->bDragging = TRUE;
3428 return 0;
3431 else
3432 infoPtr->bLButtonDown = FALSE;
3434 /* see if we are supposed to be tracking mouse hovering */
3435 if (LISTVIEW_isHotTracking(infoPtr)) {
3436 /* fill in the trackinfo struct */
3437 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3438 trackinfo.dwFlags = TME_QUERY;
3439 trackinfo.hwndTrack = infoPtr->hwndSelf;
3440 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3442 /* see if we are already tracking this hwnd */
3443 _TrackMouseEvent(&trackinfo);
3445 if(!(trackinfo.dwFlags & TME_HOVER)) {
3446 trackinfo.dwFlags = TME_HOVER;
3448 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3449 _TrackMouseEvent(&trackinfo);
3453 return 0;
3457 /***
3458 * Tests whether the item is assignable to a list with style lStyle
3460 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3462 if ( (lpLVItem->mask & LVIF_TEXT) &&
3463 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3464 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3466 return TRUE;
3470 /***
3471 * DESCRIPTION:
3472 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3474 * PARAMETER(S):
3475 * [I] infoPtr : valid pointer to the listview structure
3476 * [I] lpLVItem : valid pointer to new item attributes
3477 * [I] isNew : the item being set is being inserted
3478 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3479 * [O] bChanged : will be set to TRUE if the item really changed
3481 * RETURN:
3482 * SUCCESS : TRUE
3483 * FAILURE : FALSE
3485 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3487 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3488 ITEM_INFO *lpItem;
3489 NMLISTVIEW nmlv;
3490 UINT uChanged = 0;
3491 LVITEMW item;
3492 /* stateMask is ignored for LVM_INSERTITEM */
3493 UINT stateMask = isNew ? ~0 : lpLVItem->stateMask;
3495 TRACE("()\n");
3497 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3499 if (lpLVItem->mask == 0) return TRUE;
3501 if (infoPtr->dwStyle & LVS_OWNERDATA)
3503 /* a virtual listview only stores selection and focus */
3504 if (lpLVItem->mask & ~LVIF_STATE)
3505 return FALSE;
3506 lpItem = NULL;
3508 else
3510 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3511 lpItem = DPA_GetPtr(hdpaSubItems, 0);
3512 assert (lpItem);
3515 /* we need to get the lParam and state of the item */
3516 item.iItem = lpLVItem->iItem;
3517 item.iSubItem = lpLVItem->iSubItem;
3518 item.mask = LVIF_STATE | LVIF_PARAM;
3519 item.stateMask = ~0;
3520 item.state = 0;
3521 item.lParam = 0;
3522 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3524 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3525 /* determine what fields will change */
3526 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & stateMask & ~infoPtr->uCallbackMask))
3527 uChanged |= LVIF_STATE;
3529 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3530 uChanged |= LVIF_IMAGE;
3532 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3533 uChanged |= LVIF_PARAM;
3535 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3536 uChanged |= LVIF_INDENT;
3538 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3539 uChanged |= LVIF_TEXT;
3541 TRACE("uChanged=0x%x\n", uChanged);
3542 if (!uChanged) return TRUE;
3543 *bChanged = TRUE;
3545 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3546 nmlv.iItem = lpLVItem->iItem;
3547 nmlv.uNewState = (item.state & ~stateMask) | (lpLVItem->state & stateMask);
3548 nmlv.uOldState = item.state;
3549 nmlv.uChanged = uChanged;
3550 nmlv.lParam = item.lParam;
3552 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3553 /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
3554 /* are enabled */
3555 if(lpItem && !isNew && infoPtr->bDoChangeNotify)
3557 HWND hwndSelf = infoPtr->hwndSelf;
3559 if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3560 return FALSE;
3561 if (!IsWindow(hwndSelf))
3562 return FALSE;
3565 /* copy information */
3566 if (lpLVItem->mask & LVIF_TEXT)
3567 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3569 if (lpLVItem->mask & LVIF_IMAGE)
3570 lpItem->hdr.iImage = lpLVItem->iImage;
3572 if (lpLVItem->mask & LVIF_PARAM)
3573 lpItem->lParam = lpLVItem->lParam;
3575 if (lpLVItem->mask & LVIF_INDENT)
3576 lpItem->iIndent = lpLVItem->iIndent;
3578 if (uChanged & LVIF_STATE)
3580 if (lpItem && (stateMask & ~infoPtr->uCallbackMask))
3582 lpItem->state &= ~stateMask;
3583 lpItem->state |= (lpLVItem->state & stateMask);
3585 if (lpLVItem->state & stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3587 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3588 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3590 else if (stateMask & LVIS_SELECTED)
3592 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3594 /* if we are asked to change focus, and we manage it, do it */
3595 if (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3597 if (lpLVItem->state & LVIS_FOCUSED)
3599 if (infoPtr->nFocusedItem != -1)
3601 /* remove current focus */
3602 item.mask = LVIF_STATE;
3603 item.state = 0;
3604 item.stateMask = LVIS_FOCUSED;
3606 /* recurse with redrawing an item */
3607 LISTVIEW_SetItemState(infoPtr, infoPtr->nFocusedItem, &item);
3610 infoPtr->nFocusedItem = lpLVItem->iItem;
3611 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3613 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3615 infoPtr->nFocusedItem = -1;
3620 /* if we're inserting the item, we're done */
3621 if (isNew) return TRUE;
3623 /* send LVN_ITEMCHANGED notification */
3624 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3625 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3627 return TRUE;
3630 /***
3631 * DESCRIPTION:
3632 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3634 * PARAMETER(S):
3635 * [I] infoPtr : valid pointer to the listview structure
3636 * [I] lpLVItem : valid pointer to new subitem attributes
3637 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3638 * [O] bChanged : will be set to TRUE if the item really changed
3640 * RETURN:
3641 * SUCCESS : TRUE
3642 * FAILURE : FALSE
3644 static BOOL set_sub_item(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3646 HDPA hdpaSubItems;
3647 SUBITEM_INFO *lpSubItem;
3649 /* we do not support subitems for virtual listviews */
3650 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3652 /* set subitem only if column is present */
3653 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3655 /* First do some sanity checks */
3656 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
3657 particularly useful. We currently do not actually do anything with
3658 the flag on subitems.
3660 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)) return FALSE;
3661 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))) return TRUE;
3663 /* get the subitem structure, and create it if not there */
3664 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3665 assert (hdpaSubItems);
3667 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3668 if (!lpSubItem)
3670 SUBITEM_INFO *tmpSubItem;
3671 INT i;
3673 lpSubItem = Alloc(sizeof(SUBITEM_INFO));
3674 if (!lpSubItem) return FALSE;
3675 /* we could binary search here, if need be...*/
3676 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3678 tmpSubItem = DPA_GetPtr(hdpaSubItems, i);
3679 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3681 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3683 Free(lpSubItem);
3684 return FALSE;
3686 lpSubItem->iSubItem = lpLVItem->iSubItem;
3687 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3688 *bChanged = TRUE;
3691 if (lpLVItem->mask & LVIF_IMAGE)
3692 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3694 lpSubItem->hdr.iImage = lpLVItem->iImage;
3695 *bChanged = TRUE;
3698 if (lpLVItem->mask & LVIF_TEXT)
3699 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3701 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3702 *bChanged = TRUE;
3705 return TRUE;
3708 /***
3709 * DESCRIPTION:
3710 * Sets item attributes.
3712 * PARAMETER(S):
3713 * [I] infoPtr : valid pointer to the listview structure
3714 * [I] lpLVItem : new item attributes
3715 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3717 * RETURN:
3718 * SUCCESS : TRUE
3719 * FAILURE : FALSE
3721 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem, BOOL isW)
3723 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3724 HWND hwndSelf = infoPtr->hwndSelf;
3725 LPWSTR pszText = NULL;
3726 BOOL bResult, bChanged = FALSE;
3728 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3730 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3731 return FALSE;
3733 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3734 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3736 pszText = lpLVItem->pszText;
3737 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
3740 /* actually set the fields */
3741 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3743 if (lpLVItem->iSubItem)
3744 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3745 else
3746 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3747 if (!IsWindow(hwndSelf))
3748 return FALSE;
3750 /* redraw item, if necessary */
3751 if (bChanged && !infoPtr->bIsDrawing)
3753 /* this little optimization eliminates some nasty flicker */
3754 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3755 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
3756 lpLVItem->iSubItem > 0 && lpLVItem->iSubItem <= DPA_GetPtrCount(infoPtr->hdpaColumns) )
3757 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3758 else
3759 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3761 /* restore text */
3762 if (pszText)
3764 textfreeT(lpLVItem->pszText, isW);
3765 lpLVItem->pszText = pszText;
3768 return bResult;
3771 /***
3772 * DESCRIPTION:
3773 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3775 * PARAMETER(S):
3776 * [I] infoPtr : valid pointer to the listview structure
3778 * RETURN:
3779 * item index
3781 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *infoPtr)
3783 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3784 INT nItem = 0;
3785 SCROLLINFO scrollInfo;
3787 scrollInfo.cbSize = sizeof(SCROLLINFO);
3788 scrollInfo.fMask = SIF_POS;
3790 if (uView == LVS_LIST)
3792 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3793 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3795 else if (uView == LVS_REPORT)
3797 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3798 nItem = scrollInfo.nPos;
3800 else
3802 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3803 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3806 TRACE("nItem=%d\n", nItem);
3808 return nItem;
3812 /***
3813 * DESCRIPTION:
3814 * Erases the background of the given rectangle
3816 * PARAMETER(S):
3817 * [I] infoPtr : valid pointer to the listview structure
3818 * [I] hdc : device context handle
3819 * [I] lprcBox : clipping rectangle
3821 * RETURN:
3822 * Success: TRUE
3823 * Failure: FALSE
3825 static inline BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3827 if (!infoPtr->hBkBrush) return FALSE;
3829 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
3831 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3834 /***
3835 * DESCRIPTION:
3836 * Draws an item.
3838 * PARAMETER(S):
3839 * [I] infoPtr : valid pointer to the listview structure
3840 * [I] hdc : device context handle
3841 * [I] nItem : item index
3842 * [I] nSubItem : subitem index
3843 * [I] pos : item position in client coordinates
3844 * [I] cdmode : custom draw mode
3846 * RETURN:
3847 * Success: TRUE
3848 * Failure: FALSE
3850 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3852 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3853 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3854 static WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3855 DWORD cdsubitemmode = CDRF_DODEFAULT;
3856 LPRECT lprcFocus;
3857 RECT rcSelect, rcBox, rcIcon, rcLabel, rcStateIcon;
3858 NMLVCUSTOMDRAW nmlvcd;
3859 HIMAGELIST himl;
3860 LVITEMW lvItem;
3861 HFONT hOldFont;
3863 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
3865 /* get information needed for drawing the item */
3866 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
3867 if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
3868 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3869 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3870 lvItem.iItem = nItem;
3871 lvItem.iSubItem = nSubItem;
3872 lvItem.state = 0;
3873 lvItem.lParam = 0;
3874 lvItem.cchTextMax = DISP_TEXT_SIZE;
3875 lvItem.pszText = szDispText;
3876 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3877 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3878 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3879 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
3880 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3882 /* now check if we need to update the focus rectangle */
3883 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3885 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3886 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
3887 OffsetRect(&rcBox, pos.x, pos.y);
3888 OffsetRect(&rcSelect, pos.x, pos.y);
3889 OffsetRect(&rcIcon, pos.x, pos.y);
3890 OffsetRect(&rcStateIcon, pos.x, pos.y);
3891 OffsetRect(&rcLabel, pos.x, pos.y);
3892 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
3893 wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect),
3894 wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
3896 /* fill in the custom draw structure */
3897 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3899 hOldFont = GetCurrentObject(hdc, OBJ_FONT);
3900 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3901 if (cdmode & CDRF_SKIPDEFAULT) goto postpaint;
3902 if (cdmode & CDRF_NOTIFYITEMDRAW)
3903 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3904 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3905 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3906 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3907 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3909 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3910 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3912 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3913 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
3914 else if ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) == FALSE)
3915 prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE);
3917 /* in full row select, subitems, will just use main item's colors */
3918 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3919 nmlvcd.clrTextBk = CLR_NONE;
3921 /* state icons */
3922 if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && (nSubItem == 0))
3924 UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
3925 if (uStateImage)
3927 TRACE("uStateImage=%d\n", uStateImage);
3928 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
3929 rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
3933 /* small icons */
3934 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3935 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3937 TRACE("iImage=%d\n", lvItem.iImage);
3938 ImageList_DrawEx(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3939 rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk, CLR_DEFAULT,
3940 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3943 /* Don't bother painting item being edited */
3944 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3946 /* FIXME: temporary hack */
3947 rcSelect.left = rcLabel.left;
3949 /* draw the selection background, if we're drawing the main item */
3950 if (nSubItem == 0)
3952 /* in icon mode, the label rect is really what we want to draw the
3953 * background for */
3954 if (uView == LVS_ICON)
3955 rcSelect = rcLabel;
3957 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3958 rcSelect.right = rcBox.right;
3960 if (nmlvcd.clrTextBk != CLR_NONE)
3961 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3962 if(lprcFocus) *lprcFocus = rcSelect;
3965 /* figure out the text drawing flags */
3966 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3967 if (uView == LVS_ICON)
3968 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3969 else if (nSubItem)
3971 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3973 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3974 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3975 default: uFormat |= DT_LEFT;
3978 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3980 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3981 else rcLabel.left += LABEL_HOR_PADDING;
3983 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3985 /* for GRIDLINES reduce the bottom so the text formats correctly */
3986 if (uView == LVS_REPORT && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
3987 rcLabel.bottom--;
3989 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3991 postpaint:
3992 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3993 notify_postpaint(infoPtr, &nmlvcd);
3994 if (cdsubitemmode & CDRF_NEWFONT)
3995 SelectObject(hdc, hOldFont);
3996 return TRUE;
3999 /***
4000 * DESCRIPTION:
4001 * Draws listview items when in owner draw mode.
4003 * PARAMETER(S):
4004 * [I] infoPtr : valid pointer to the listview structure
4005 * [I] hdc : device context handle
4007 * RETURN:
4008 * None
4010 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4012 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
4013 DWORD cditemmode = CDRF_DODEFAULT;
4014 NMLVCUSTOMDRAW nmlvcd;
4015 POINT Origin, Position;
4016 DRAWITEMSTRUCT dis;
4017 LVITEMW item;
4019 TRACE("()\n");
4021 ZeroMemory(&dis, sizeof(dis));
4023 /* Get scroll info once before loop */
4024 LISTVIEW_GetOrigin(infoPtr, &Origin);
4026 /* iterate through the invalidated rows */
4027 while(iterator_next(i))
4029 item.iItem = i->nItem;
4030 item.iSubItem = 0;
4031 item.mask = LVIF_PARAM | LVIF_STATE;
4032 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4033 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
4035 dis.CtlType = ODT_LISTVIEW;
4036 dis.CtlID = uID;
4037 dis.itemID = item.iItem;
4038 dis.itemAction = ODA_DRAWENTIRE;
4039 dis.itemState = 0;
4040 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
4041 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
4042 dis.hwndItem = infoPtr->hwndSelf;
4043 dis.hDC = hdc;
4044 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
4045 dis.rcItem.left = Position.x + Origin.x;
4046 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
4047 dis.rcItem.top = Position.y + Origin.y;
4048 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
4049 dis.itemData = item.lParam;
4051 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
4054 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
4055 * structure for the rest. of the paint cycle
4057 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
4058 if (cdmode & CDRF_NOTIFYITEMDRAW)
4059 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4061 if (!(cditemmode & CDRF_SKIPDEFAULT))
4063 prepaint_setup (infoPtr, hdc, &nmlvcd, FALSE);
4064 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
4067 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
4068 notify_postpaint(infoPtr, &nmlvcd);
4072 /***
4073 * DESCRIPTION:
4074 * Draws listview items when in report display mode.
4076 * PARAMETER(S):
4077 * [I] infoPtr : valid pointer to the listview structure
4078 * [I] hdc : device context handle
4079 * [I] cdmode : custom draw mode
4081 * RETURN:
4082 * None
4084 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4086 INT rgntype;
4087 RECT rcClip, rcItem;
4088 POINT Origin, Position;
4089 RANGE colRange;
4090 ITERATOR j;
4092 TRACE("()\n");
4094 /* figure out what to draw */
4095 rgntype = GetClipBox(hdc, &rcClip);
4096 if (rgntype == NULLREGION) return;
4098 /* Get scroll info once before loop */
4099 LISTVIEW_GetOrigin(infoPtr, &Origin);
4101 /* narrow down the columns we need to paint */
4102 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
4104 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
4105 if (rcItem.right + Origin.x >= rcClip.left) break;
4107 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
4109 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
4110 if (rcItem.left + Origin.x < rcClip.right) break;
4112 iterator_rangeitems(&j, colRange);
4114 /* in full row select, we _have_ to draw the main item */
4115 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
4116 j.nSpecial = 0;
4118 /* iterate through the invalidated rows */
4119 while(iterator_next(i))
4121 /* iterate through the invalidated columns */
4122 while(iterator_next(&j))
4124 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4125 Position.x += Origin.x;
4126 Position.y += Origin.y;
4128 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
4130 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4131 rcItem.top = 0;
4132 rcItem.bottom = infoPtr->nItemHeight;
4133 OffsetRect(&rcItem, Position.x, Position.y);
4134 if (!RectVisible(hdc, &rcItem)) continue;
4137 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
4140 iterator_destroy(&j);
4143 /***
4144 * DESCRIPTION:
4145 * Draws the gridlines if necessary when in report display mode.
4147 * PARAMETER(S):
4148 * [I] infoPtr : valid pointer to the listview structure
4149 * [I] hdc : device context handle
4151 * RETURN:
4152 * None
4154 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO *infoPtr, HDC hdc)
4156 INT rgntype;
4157 INT y, itemheight;
4158 HPEN hPen, hOldPen;
4159 RECT rcClip, rcItem = {0};
4160 POINT Origin;
4161 RANGE colRange;
4162 ITERATOR j;
4163 BOOL rmost = FALSE;
4165 TRACE("()\n");
4167 /* figure out what to draw */
4168 rgntype = GetClipBox(hdc, &rcClip);
4169 if (rgntype == NULLREGION) return;
4171 /* Get scroll info once before loop */
4172 LISTVIEW_GetOrigin(infoPtr, &Origin);
4174 /* narrow down the columns we need to paint */
4175 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
4177 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
4178 if (rcItem.right + Origin.x >= rcClip.left) break;
4180 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
4182 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
4183 if (rcItem.left + Origin.x < rcClip.right) break;
4185 /* is right most vertical line visible? */
4186 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
4188 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcItem);
4189 rmost = (rcItem.right + Origin.x < rcClip.right);
4192 if ((hPen = CreatePen( PS_SOLID, 1, comctl32_color.clr3dFace )))
4194 hOldPen = SelectObject ( hdc, hPen );
4196 /* draw the vertical lines for the columns */
4197 iterator_rangeitems(&j, colRange);
4198 while(iterator_next(&j))
4200 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4201 if (rcItem.left == 0) continue; /* skip first column */
4202 rcItem.left += Origin.x;
4203 rcItem.right += Origin.x;
4204 rcItem.top = infoPtr->rcList.top;
4205 rcItem.bottom = infoPtr->rcList.bottom;
4206 TRACE("vert col=%d, rcItem=%s\n", j.nItem, wine_dbgstr_rect(&rcItem));
4207 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4208 LineTo (hdc, rcItem.left, rcItem.bottom);
4210 iterator_destroy(&j);
4211 /* draw rightmost grid line if visible */
4212 if (rmost)
4214 MoveToEx (hdc, rcItem.right, rcItem.top, NULL);
4215 LineTo (hdc, rcItem.right, rcItem.bottom);
4218 /* draw the horizontial lines for the rows */
4219 itemheight = LISTVIEW_CalculateItemHeight(infoPtr);
4220 rcItem.left = infoPtr->rcList.left;
4221 rcItem.right = infoPtr->rcList.right;
4222 rcItem.bottom = rcItem.top = Origin.y - 1;
4223 MoveToEx(hdc, rcItem.left, rcItem.top, NULL);
4224 LineTo(hdc, rcItem.right, rcItem.top);
4225 for(y=itemheight-1+Origin.y; y<=infoPtr->rcList.bottom; y+=itemheight)
4227 rcItem.bottom = rcItem.top = y;
4228 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem));
4229 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4230 LineTo (hdc, rcItem.right, rcItem.top);
4233 SelectObject( hdc, hOldPen );
4234 DeleteObject( hPen );
4238 /***
4239 * DESCRIPTION:
4240 * Draws listview items when in list display mode.
4242 * PARAMETER(S):
4243 * [I] infoPtr : valid pointer to the listview structure
4244 * [I] hdc : device context handle
4245 * [I] cdmode : custom draw mode
4247 * RETURN:
4248 * None
4250 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4252 POINT Origin, Position;
4254 /* Get scroll info once before loop */
4255 LISTVIEW_GetOrigin(infoPtr, &Origin);
4257 while(iterator_prev(i))
4259 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4260 Position.x += Origin.x;
4261 Position.y += Origin.y;
4263 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
4268 /***
4269 * DESCRIPTION:
4270 * Draws listview items.
4272 * PARAMETER(S):
4273 * [I] infoPtr : valid pointer to the listview structure
4274 * [I] hdc : device context handle
4275 * [I] prcErase : rect to be erased before refresh (may be NULL)
4277 * RETURN:
4278 * NoneX
4280 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcErase)
4282 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4283 COLORREF oldTextColor = 0, oldBkColor = 0, oldClrTextBk, oldClrText;
4284 NMLVCUSTOMDRAW nmlvcd;
4285 HFONT hOldFont = 0;
4286 DWORD cdmode;
4287 INT oldBkMode = 0;
4288 RECT rcClient;
4289 ITERATOR i;
4290 HDC hdcOrig = hdc;
4291 HBITMAP hbmp = NULL;
4293 LISTVIEW_DUMP(infoPtr);
4295 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4296 TRACE("double buffering\n");
4298 hdc = CreateCompatibleDC(hdcOrig);
4299 if (!hdc) {
4300 ERR("Failed to create DC for backbuffer\n");
4301 return;
4303 hbmp = CreateCompatibleBitmap(hdcOrig, infoPtr->rcList.right,
4304 infoPtr->rcList.bottom);
4305 if (!hbmp) {
4306 ERR("Failed to create bitmap for backbuffer\n");
4307 DeleteDC(hdc);
4308 return;
4311 SelectObject(hdc, hbmp);
4312 SelectObject(hdc, infoPtr->hFont);
4313 } else {
4314 /* Save dc values we're gonna trash while drawing
4315 * FIXME: Should be done in LISTVIEW_DrawItem() */
4316 hOldFont = SelectObject(hdc, infoPtr->hFont);
4317 oldBkMode = GetBkMode(hdc);
4318 oldBkColor = GetBkColor(hdc);
4319 oldTextColor = GetTextColor(hdc);
4322 infoPtr->bIsDrawing = TRUE;
4324 if (prcErase) {
4325 LISTVIEW_FillBkgnd(infoPtr, hdc, prcErase);
4326 } else if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4327 /* If no erasing was done (usually because RedrawWindow was called
4328 * with RDW_INVALIDATE only) we need to copy the old contents into
4329 * the backbuffer before continuing. */
4330 BitBlt(hdc, infoPtr->rcList.left, infoPtr->rcList.top,
4331 infoPtr->rcList.right - infoPtr->rcList.left,
4332 infoPtr->rcList.bottom - infoPtr->rcList.top,
4333 hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4336 /* FIXME: Shouldn't need to do this */
4337 oldClrTextBk = infoPtr->clrTextBk;
4338 oldClrText = infoPtr->clrText;
4340 infoPtr->cditemmode = CDRF_DODEFAULT;
4342 GetClientRect(infoPtr->hwndSelf, &rcClient);
4343 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4344 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4345 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4346 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4348 /* Use these colors to draw the items */
4349 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4350 infoPtr->clrText = nmlvcd.clrText;
4352 /* nothing to draw */
4353 if(infoPtr->nItemCount == 0) goto enddraw;
4355 /* figure out what we need to draw */
4356 iterator_visibleitems(&i, infoPtr, hdc);
4358 /* send cache hint notification */
4359 if (infoPtr->dwStyle & LVS_OWNERDATA)
4361 RANGE range = iterator_range(&i);
4362 NMLVCACHEHINT nmlv;
4364 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4365 nmlv.iFrom = range.lower;
4366 nmlv.iTo = range.upper - 1;
4367 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4370 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4371 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4372 else
4374 if (uView == LVS_REPORT)
4375 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4376 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4377 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4379 /* if we have a focus rect, draw it */
4380 if (infoPtr->bFocus)
4381 LISTVIEW_DrawFocusRect(infoPtr, hdc);
4383 iterator_destroy(&i);
4385 enddraw:
4386 /* For LVS_EX_GRIDLINES go and draw lines */
4387 /* This includes the case where there were *no* items */
4388 if ((uView == LVS_REPORT) && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
4389 LISTVIEW_RefreshReportGrid(infoPtr, hdc);
4391 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4392 notify_postpaint(infoPtr, &nmlvcd);
4394 infoPtr->clrTextBk = oldClrTextBk;
4395 infoPtr->clrText = oldClrText;
4397 if(hbmp) {
4398 BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top,
4399 infoPtr->rcList.right - infoPtr->rcList.left,
4400 infoPtr->rcList.bottom - infoPtr->rcList.top,
4401 hdc, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4403 DeleteObject(hbmp);
4404 DeleteDC(hdc);
4405 } else {
4406 SelectObject(hdc, hOldFont);
4407 SetBkMode(hdc, oldBkMode);
4408 SetBkColor(hdc, oldBkColor);
4409 SetTextColor(hdc, oldTextColor);
4412 infoPtr->bIsDrawing = FALSE;
4416 /***
4417 * DESCRIPTION:
4418 * Calculates the approximate width and height of a given number of items.
4420 * PARAMETER(S):
4421 * [I] infoPtr : valid pointer to the listview structure
4422 * [I] nItemCount : number of items
4423 * [I] wWidth : width
4424 * [I] wHeight : height
4426 * RETURN:
4427 * Returns a DWORD. The width in the low word and the height in high word.
4429 static DWORD LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO *infoPtr, INT nItemCount,
4430 WORD wWidth, WORD wHeight)
4432 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4433 INT nItemCountPerColumn = 1;
4434 INT nColumnCount = 0;
4435 DWORD dwViewRect = 0;
4437 if (nItemCount == -1)
4438 nItemCount = infoPtr->nItemCount;
4440 if (uView == LVS_LIST)
4442 if (wHeight == 0xFFFF)
4444 /* use current height */
4445 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4448 if (wHeight < infoPtr->nItemHeight)
4449 wHeight = infoPtr->nItemHeight;
4451 if (nItemCount > 0)
4453 if (infoPtr->nItemHeight > 0)
4455 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4456 if (nItemCountPerColumn == 0)
4457 nItemCountPerColumn = 1;
4459 if (nItemCount % nItemCountPerColumn != 0)
4460 nColumnCount = nItemCount / nItemCountPerColumn;
4461 else
4462 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4466 /* Microsoft padding magic */
4467 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4468 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4470 dwViewRect = MAKELONG(wWidth, wHeight);
4472 else if (uView == LVS_REPORT)
4474 RECT rcBox;
4476 if (infoPtr->nItemCount > 0)
4478 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4479 wWidth = rcBox.right - rcBox.left;
4480 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4482 else
4484 /* use current height and width */
4485 if (wHeight == 0xffff)
4486 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4487 if (wWidth == 0xffff)
4488 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4491 dwViewRect = MAKELONG(wWidth, wHeight);
4493 else if (uView == LVS_SMALLICON)
4494 FIXME("uView == LVS_SMALLICON: not implemented\n");
4495 else if (uView == LVS_ICON)
4496 FIXME("uView == LVS_ICON: not implemented\n");
4498 return dwViewRect;
4502 /***
4503 * DESCRIPTION:
4504 * Create a drag image list for the specified item.
4506 * PARAMETER(S):
4507 * [I] infoPtr : valid pointer to the listview structure
4508 * [I] iItem : index of item
4509 * [O] lppt : Upper-left corner of the image
4511 * RETURN:
4512 * Returns a handle to the image list if successful, NULL otherwise.
4514 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4516 RECT rcItem;
4517 SIZE size;
4518 POINT pos;
4519 HDC hdc, hdcOrig;
4520 HBITMAP hbmp, hOldbmp;
4521 HIMAGELIST dragList = 0;
4522 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
4524 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4525 return 0;
4527 rcItem.left = LVIR_BOUNDS;
4528 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4529 return 0;
4531 lppt->x = rcItem.left;
4532 lppt->y = rcItem.top;
4534 size.cx = rcItem.right - rcItem.left;
4535 size.cy = rcItem.bottom - rcItem.top;
4537 hdcOrig = GetDC(infoPtr->hwndSelf);
4538 hdc = CreateCompatibleDC(hdcOrig);
4539 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4540 hOldbmp = SelectObject(hdc, hbmp);
4542 rcItem.left = rcItem.top = 0;
4543 rcItem.right = size.cx;
4544 rcItem.bottom = size.cy;
4545 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4547 pos.x = pos.y = 0;
4548 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4550 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4551 SelectObject(hdc, hOldbmp);
4552 ImageList_Add(dragList, hbmp, 0);
4554 else
4555 SelectObject(hdc, hOldbmp);
4557 DeleteObject(hbmp);
4558 DeleteDC(hdc);
4559 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4561 TRACE("ret=%p\n", dragList);
4563 return dragList;
4567 /***
4568 * DESCRIPTION:
4569 * Removes all listview items and subitems.
4571 * PARAMETER(S):
4572 * [I] infoPtr : valid pointer to the listview structure
4574 * RETURN:
4575 * SUCCESS : TRUE
4576 * FAILURE : FALSE
4578 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr, BOOL destroy)
4580 NMLISTVIEW nmlv;
4581 HDPA hdpaSubItems = NULL;
4582 BOOL bSuppress;
4583 ITEMHDR *hdrItem;
4584 INT i, j;
4586 TRACE("()\n");
4588 /* we do it directly, to avoid notifications */
4589 ranges_clear(infoPtr->selectionRanges);
4590 infoPtr->nSelectionMark = -1;
4591 infoPtr->nFocusedItem = -1;
4592 SetRectEmpty(&infoPtr->rcFocus);
4593 /* But we are supposed to leave nHotItem as is! */
4596 /* send LVN_DELETEALLITEMS notification */
4597 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4598 nmlv.iItem = -1;
4599 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4601 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4603 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4605 /* send LVN_DELETEITEM notification, if not suppressed
4606 and if it is not a virtual listview */
4607 if (!bSuppress) notify_deleteitem(infoPtr, i);
4608 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
4609 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4611 hdrItem = DPA_GetPtr(hdpaSubItems, j);
4612 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4613 Free(hdrItem);
4615 DPA_Destroy(hdpaSubItems);
4616 DPA_DeletePtr(infoPtr->hdpaItems, i);
4618 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4619 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4620 infoPtr->nItemCount --;
4623 if (!destroy)
4625 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4626 LISTVIEW_UpdateScroll(infoPtr);
4628 LISTVIEW_InvalidateList(infoPtr);
4630 return TRUE;
4633 /***
4634 * DESCRIPTION:
4635 * Scrolls, and updates the columns, when a column is changing width.
4637 * PARAMETER(S):
4638 * [I] infoPtr : valid pointer to the listview structure
4639 * [I] nColumn : column to scroll
4640 * [I] dx : amount of scroll, in pixels
4642 * RETURN:
4643 * None.
4645 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4647 COLUMN_INFO *lpColumnInfo;
4648 RECT rcOld, rcCol;
4649 POINT ptOrigin;
4650 INT nCol;
4652 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4653 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4654 rcCol = lpColumnInfo->rcHeader;
4655 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4656 rcCol.left = rcCol.right;
4658 /* adjust the other columns */
4659 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4661 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4662 lpColumnInfo->rcHeader.left += dx;
4663 lpColumnInfo->rcHeader.right += dx;
4666 /* do not update screen if not in report mode */
4667 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4669 /* if we have a focus, we must first erase the focus rect */
4670 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4672 /* Need to reset the item width when inserting a new column */
4673 infoPtr->nItemWidth += dx;
4675 LISTVIEW_UpdateScroll(infoPtr);
4676 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4678 /* scroll to cover the deleted column, and invalidate for redraw */
4679 rcOld = infoPtr->rcList;
4680 rcOld.left = ptOrigin.x + rcCol.left + dx;
4681 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4683 /* we can restore focus now */
4684 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4687 /***
4688 * DESCRIPTION:
4689 * Removes a column from the listview control.
4691 * PARAMETER(S):
4692 * [I] infoPtr : valid pointer to the listview structure
4693 * [I] nColumn : column index
4695 * RETURN:
4696 * SUCCESS : TRUE
4697 * FAILURE : FALSE
4699 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4701 RECT rcCol;
4703 TRACE("nColumn=%d\n", nColumn);
4705 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4706 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4708 /* While the MSDN specifically says that column zero should not be deleted,
4709 what actually happens is that the column itself is deleted but no items or subitems
4710 are removed.
4713 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4715 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4716 return FALSE;
4718 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4719 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4721 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4723 SUBITEM_INFO *lpSubItem, *lpDelItem;
4724 HDPA hdpaSubItems;
4725 INT nItem, nSubItem, i;
4727 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4729 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
4730 nSubItem = 0;
4731 lpDelItem = 0;
4732 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4734 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
4735 if (lpSubItem->iSubItem == nColumn)
4737 nSubItem = i;
4738 lpDelItem = lpSubItem;
4740 else if (lpSubItem->iSubItem > nColumn)
4742 lpSubItem->iSubItem--;
4746 /* if we found our subitem, zapp it */
4747 if (nSubItem > 0)
4749 /* free string */
4750 if (is_textW(lpDelItem->hdr.pszText))
4751 Free(lpDelItem->hdr.pszText);
4753 /* free item */
4754 Free(lpDelItem);
4756 /* free dpa memory */
4757 DPA_DeletePtr(hdpaSubItems, nSubItem);
4762 /* update the other column info */
4763 LISTVIEW_UpdateItemSize(infoPtr);
4764 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4765 LISTVIEW_InvalidateList(infoPtr);
4766 else
4767 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4769 return TRUE;
4772 /***
4773 * DESCRIPTION:
4774 * Invalidates the listview after an item's insertion or deletion.
4776 * PARAMETER(S):
4777 * [I] infoPtr : valid pointer to the listview structure
4778 * [I] nItem : item index
4779 * [I] dir : -1 if deleting, 1 if inserting
4781 * RETURN:
4782 * None
4784 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4786 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4787 INT nPerCol, nItemCol, nItemRow;
4788 RECT rcScroll;
4789 POINT Origin;
4791 /* if we don't refresh, what's the point of scrolling? */
4792 if (!is_redrawing(infoPtr)) return;
4794 assert (abs(dir) == 1);
4796 /* arrange icons if autoarrange is on */
4797 if (is_autoarrange(infoPtr))
4799 BOOL arrange = TRUE;
4800 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4801 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4802 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4805 /* scrollbars need updating */
4806 LISTVIEW_UpdateScroll(infoPtr);
4808 /* figure out the item's position */
4809 if (uView == LVS_REPORT)
4810 nPerCol = infoPtr->nItemCount + 1;
4811 else if (uView == LVS_LIST)
4812 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4813 else /* LVS_ICON, or LVS_SMALLICON */
4814 return;
4816 nItemCol = nItem / nPerCol;
4817 nItemRow = nItem % nPerCol;
4818 LISTVIEW_GetOrigin(infoPtr, &Origin);
4820 /* move the items below up a slot */
4821 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4822 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4823 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4824 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4825 OffsetRect(&rcScroll, Origin.x, Origin.y);
4826 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
4827 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4829 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
4830 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4831 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4834 /* report has only that column, so we're done */
4835 if (uView == LVS_REPORT) return;
4837 /* now for LISTs, we have to deal with the columns to the right */
4838 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4839 rcScroll.top = 0;
4840 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4841 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4842 OffsetRect(&rcScroll, Origin.x, Origin.y);
4843 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4844 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4845 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4848 /***
4849 * DESCRIPTION:
4850 * Removes an item from the listview control.
4852 * PARAMETER(S):
4853 * [I] infoPtr : valid pointer to the listview structure
4854 * [I] nItem : item index
4856 * RETURN:
4857 * SUCCESS : TRUE
4858 * FAILURE : FALSE
4860 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4862 LVITEMW item;
4863 const UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4864 const BOOL is_icon = (uView == LVS_SMALLICON || uView == LVS_ICON);
4866 TRACE("(nItem=%d)\n", nItem);
4868 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4870 /* remove selection, and focus */
4871 item.state = 0;
4872 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4873 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4875 /* send LVN_DELETEITEM notification. */
4876 if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
4878 /* we need to do this here, because we'll be deleting stuff */
4879 if (is_icon)
4880 LISTVIEW_InvalidateItem(infoPtr, nItem);
4882 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4884 HDPA hdpaSubItems;
4885 ITEMHDR *hdrItem;
4886 INT i;
4888 hdpaSubItems = DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4889 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4891 hdrItem = DPA_GetPtr(hdpaSubItems, i);
4892 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4893 Free(hdrItem);
4895 DPA_Destroy(hdpaSubItems);
4898 if (is_icon)
4900 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4901 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4904 infoPtr->nItemCount--;
4905 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4907 /* now is the invalidation fun */
4908 if (!is_icon)
4909 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4910 return TRUE;
4914 /***
4915 * DESCRIPTION:
4916 * Callback implementation for editlabel control
4918 * PARAMETER(S):
4919 * [I] infoPtr : valid pointer to the listview structure
4920 * [I] pszText : modified text
4921 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4923 * RETURN:
4924 * SUCCESS : TRUE
4925 * FAILURE : FALSE
4927 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4929 HWND hwndSelf = infoPtr->hwndSelf;
4930 NMLVDISPINFOW dispInfo;
4931 INT editedItem = infoPtr->nEditLabelItem;
4932 BOOL bSame;
4934 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4936 infoPtr->nEditLabelItem = -1;
4938 ZeroMemory(&dispInfo, sizeof(dispInfo));
4939 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4940 dispInfo.item.iItem = editedItem;
4941 dispInfo.item.iSubItem = 0;
4942 dispInfo.item.stateMask = ~0;
4943 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4945 if (isW)
4946 bSame = (lstrcmpW(dispInfo.item.pszText, pszText) == 0);
4947 else
4949 LPWSTR tmp = textdupTtoW(pszText, FALSE);
4950 bSame = (lstrcmpW(dispInfo.item.pszText, tmp) == 0);
4951 textfreeT(tmp, FALSE);
4953 if (bSame) return TRUE;
4955 /* add the text from the edit in */
4956 dispInfo.item.mask |= LVIF_TEXT;
4957 dispInfo.item.pszText = pszText;
4958 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4960 /* Do we need to update the Item Text */
4961 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4962 if (!IsWindow(hwndSelf))
4963 return FALSE;
4964 if (!pszText) return TRUE;
4966 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4968 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, editedItem);
4969 ITEM_INFO* lpItem = DPA_GetPtr(hdpaSubItems, 0);
4970 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4972 LISTVIEW_InvalidateItem(infoPtr, editedItem);
4973 return TRUE;
4977 ZeroMemory(&dispInfo, sizeof(dispInfo));
4978 dispInfo.item.mask = LVIF_TEXT;
4979 dispInfo.item.iItem = editedItem;
4980 dispInfo.item.iSubItem = 0;
4981 dispInfo.item.pszText = pszText;
4982 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4983 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4986 /***
4987 * DESCRIPTION:
4988 * Begin in place editing of specified list view item
4990 * PARAMETER(S):
4991 * [I] infoPtr : valid pointer to the listview structure
4992 * [I] nItem : item index
4993 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4995 * RETURN:
4996 * SUCCESS : TRUE
4997 * FAILURE : FALSE
4999 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
5001 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
5002 NMLVDISPINFOW dispInfo;
5003 RECT rect;
5004 HWND hwndSelf = infoPtr->hwndSelf;
5006 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
5008 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
5009 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5011 infoPtr->nEditLabelItem = nItem;
5013 /* Is the EditBox still there, if so remove it */
5014 if(infoPtr->hwndEdit != 0)
5016 SetFocus(infoPtr->hwndSelf);
5017 infoPtr->hwndEdit = 0;
5020 LISTVIEW_SetSelection(infoPtr, nItem);
5021 LISTVIEW_SetItemFocus(infoPtr, nItem);
5022 LISTVIEW_InvalidateItem(infoPtr, nItem);
5024 rect.left = LVIR_LABEL;
5025 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
5027 ZeroMemory(&dispInfo, sizeof(dispInfo));
5028 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
5029 dispInfo.item.iItem = nItem;
5030 dispInfo.item.iSubItem = 0;
5031 dispInfo.item.stateMask = ~0;
5032 dispInfo.item.pszText = szDispText;
5033 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
5034 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
5036 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
5037 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
5038 if (!infoPtr->hwndEdit) return 0;
5040 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
5042 if (!IsWindow(hwndSelf))
5043 return 0;
5044 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
5045 infoPtr->hwndEdit = 0;
5046 return 0;
5049 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
5050 SetFocus(infoPtr->hwndEdit);
5051 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
5052 return infoPtr->hwndEdit;
5056 /***
5057 * DESCRIPTION:
5058 * Ensures the specified item is visible, scrolling into view if necessary.
5060 * PARAMETER(S):
5061 * [I] infoPtr : valid pointer to the listview structure
5062 * [I] nItem : item index
5063 * [I] bPartial : partially or entirely visible
5065 * RETURN:
5066 * SUCCESS : TRUE
5067 * FAILURE : FALSE
5069 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
5071 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5072 INT nScrollPosHeight = 0;
5073 INT nScrollPosWidth = 0;
5074 INT nHorzAdjust = 0;
5075 INT nVertAdjust = 0;
5076 INT nHorzDiff = 0;
5077 INT nVertDiff = 0;
5078 RECT rcItem, rcTemp;
5080 rcItem.left = LVIR_BOUNDS;
5081 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
5083 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
5085 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
5087 /* scroll left/right, but in LVS_REPORT mode */
5088 if (uView == LVS_LIST)
5089 nScrollPosWidth = infoPtr->nItemWidth;
5090 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5091 nScrollPosWidth = 1;
5093 if (rcItem.left < infoPtr->rcList.left)
5095 nHorzAdjust = -1;
5096 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
5098 else
5100 nHorzAdjust = 1;
5101 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
5105 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
5107 /* scroll up/down, but not in LVS_LIST mode */
5108 if (uView == LVS_REPORT)
5109 nScrollPosHeight = infoPtr->nItemHeight;
5110 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
5111 nScrollPosHeight = 1;
5113 if (rcItem.top < infoPtr->rcList.top)
5115 nVertAdjust = -1;
5116 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
5118 else
5120 nVertAdjust = 1;
5121 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
5125 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
5127 if (nScrollPosWidth)
5129 INT diff = nHorzDiff / nScrollPosWidth;
5130 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
5131 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
5134 if (nScrollPosHeight)
5136 INT diff = nVertDiff / nScrollPosHeight;
5137 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
5138 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
5141 return TRUE;
5144 /***
5145 * DESCRIPTION:
5146 * Searches for an item with specific characteristics.
5148 * PARAMETER(S):
5149 * [I] hwnd : window handle
5150 * [I] nStart : base item index
5151 * [I] lpFindInfo : item information to look for
5153 * RETURN:
5154 * SUCCESS : index of item
5155 * FAILURE : -1
5157 static INT LISTVIEW_FindItemW(const LISTVIEW_INFO *infoPtr, INT nStart,
5158 const LVFINDINFOW *lpFindInfo)
5160 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5161 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5162 BOOL bWrap = FALSE, bNearest = FALSE;
5163 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
5164 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
5165 POINT Position, Destination;
5166 LVITEMW lvItem;
5168 /* Search in virtual listviews should be done by application, not by
5169 listview control, so we just send LVN_ODFINDITEMW and return the result */
5170 if (infoPtr->dwStyle & LVS_OWNERDATA)
5172 NMLVFINDITEMW nmlv;
5174 nmlv.iStart = nStart;
5175 nmlv.lvfi = *lpFindInfo;
5176 return notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
5179 if (!lpFindInfo || nItem < 0) return -1;
5181 lvItem.mask = 0;
5182 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
5184 lvItem.mask |= LVIF_TEXT;
5185 lvItem.pszText = szDispText;
5186 lvItem.cchTextMax = DISP_TEXT_SIZE;
5189 if (lpFindInfo->flags & LVFI_WRAP)
5190 bWrap = TRUE;
5192 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
5193 (uView == LVS_ICON || uView ==LVS_SMALLICON))
5195 POINT Origin;
5196 RECT rcArea;
5198 LISTVIEW_GetOrigin(infoPtr, &Origin);
5199 Destination.x = lpFindInfo->pt.x - Origin.x;
5200 Destination.y = lpFindInfo->pt.y - Origin.y;
5201 switch(lpFindInfo->vkDirection)
5203 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
5204 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
5205 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
5206 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
5207 case VK_HOME: Destination.x = Destination.y = 0; break;
5208 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5209 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5210 case VK_END:
5211 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
5212 Destination.x = rcArea.right;
5213 Destination.y = rcArea.bottom;
5214 break;
5215 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
5217 bNearest = TRUE;
5219 else Destination.x = Destination.y = 0;
5221 /* if LVFI_PARAM is specified, all other flags are ignored */
5222 if (lpFindInfo->flags & LVFI_PARAM)
5224 lvItem.mask |= LVIF_PARAM;
5225 bNearest = FALSE;
5226 lvItem.mask &= ~LVIF_TEXT;
5229 again:
5230 for (; nItem < nLast; nItem++)
5232 lvItem.iItem = nItem;
5233 lvItem.iSubItem = 0;
5234 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
5236 if (lvItem.mask & LVIF_PARAM)
5238 if (lpFindInfo->lParam == lvItem.lParam)
5239 return nItem;
5240 else
5241 continue;
5244 if (lvItem.mask & LVIF_TEXT)
5246 if (lpFindInfo->flags & LVFI_PARTIAL)
5248 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
5250 else
5252 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
5256 if (!bNearest) return nItem;
5258 /* This is very inefficient. To do a good job here,
5259 * we need a sorted array of (x,y) item positions */
5260 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5262 /* compute the distance^2 to the destination */
5263 xdist = Destination.x - Position.x;
5264 ydist = Destination.y - Position.y;
5265 dist = xdist * xdist + ydist * ydist;
5267 /* remember the distance, and item if it's closer */
5268 if (dist < mindist)
5270 mindist = dist;
5271 nNearestItem = nItem;
5275 if (bWrap)
5277 nItem = 0;
5278 nLast = min(nStart + 1, infoPtr->nItemCount);
5279 bWrap = FALSE;
5280 goto again;
5283 return nNearestItem;
5286 /***
5287 * DESCRIPTION:
5288 * Searches for an item with specific characteristics.
5290 * PARAMETER(S):
5291 * [I] hwnd : window handle
5292 * [I] nStart : base item index
5293 * [I] lpFindInfo : item information to look for
5295 * RETURN:
5296 * SUCCESS : index of item
5297 * FAILURE : -1
5299 static INT LISTVIEW_FindItemA(const LISTVIEW_INFO *infoPtr, INT nStart,
5300 const LVFINDINFOA *lpFindInfo)
5302 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
5303 LVFINDINFOW fiw;
5304 INT res;
5305 LPWSTR strW = NULL;
5307 memcpy(&fiw, lpFindInfo, sizeof(fiw));
5308 if (hasText) fiw.psz = strW = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
5309 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
5310 textfreeT(strW, FALSE);
5311 return res;
5314 /***
5315 * DESCRIPTION:
5316 * Retrieves the background image of the listview control.
5318 * PARAMETER(S):
5319 * [I] infoPtr : valid pointer to the listview structure
5320 * [O] lpBkImage : background image attributes
5322 * RETURN:
5323 * SUCCESS : TRUE
5324 * FAILURE : FALSE
5326 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
5327 /* { */
5328 /* FIXME (listview, "empty stub!\n"); */
5329 /* return FALSE; */
5330 /* } */
5332 /***
5333 * DESCRIPTION:
5334 * Retrieves column attributes.
5336 * PARAMETER(S):
5337 * [I] infoPtr : valid pointer to the listview structure
5338 * [I] nColumn : column index
5339 * [IO] lpColumn : column information
5340 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5341 * otherwise it is in fact a LPLVCOLUMNA
5343 * RETURN:
5344 * SUCCESS : TRUE
5345 * FAILURE : FALSE
5347 static BOOL LISTVIEW_GetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
5349 COLUMN_INFO *lpColumnInfo;
5350 HDITEMW hdi;
5352 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5353 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
5355 /* initialize memory */
5356 ZeroMemory(&hdi, sizeof(hdi));
5358 if (lpColumn->mask & LVCF_TEXT)
5360 hdi.mask |= HDI_TEXT;
5361 hdi.pszText = lpColumn->pszText;
5362 hdi.cchTextMax = lpColumn->cchTextMax;
5365 if (lpColumn->mask & LVCF_IMAGE)
5366 hdi.mask |= HDI_IMAGE;
5368 if (lpColumn->mask & LVCF_ORDER)
5369 hdi.mask |= HDI_ORDER;
5371 if (lpColumn->mask & LVCF_SUBITEM)
5372 hdi.mask |= HDI_LPARAM;
5374 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
5376 if (lpColumn->mask & LVCF_FMT)
5377 lpColumn->fmt = lpColumnInfo->fmt;
5379 if (lpColumn->mask & LVCF_WIDTH)
5380 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
5382 if (lpColumn->mask & LVCF_IMAGE)
5383 lpColumn->iImage = hdi.iImage;
5385 if (lpColumn->mask & LVCF_ORDER)
5386 lpColumn->iOrder = hdi.iOrder;
5388 if (lpColumn->mask & LVCF_SUBITEM)
5389 lpColumn->iSubItem = hdi.lParam;
5391 return TRUE;
5395 static BOOL LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5397 TRACE("iCount=%d, lpiArray=%p\n", iCount, lpiArray);
5399 if (!lpiArray)
5400 return FALSE;
5402 return SendMessageW(infoPtr->hwndHeader, HDM_GETORDERARRAY, iCount, (LPARAM)lpiArray);
5405 /***
5406 * DESCRIPTION:
5407 * Retrieves the column width.
5409 * PARAMETER(S):
5410 * [I] infoPtr : valid pointer to the listview structure
5411 * [I] int : column index
5413 * RETURN:
5414 * SUCCESS : column width
5415 * FAILURE : zero
5417 static INT LISTVIEW_GetColumnWidth(const LISTVIEW_INFO *infoPtr, INT nColumn)
5419 INT nColumnWidth = 0;
5420 HDITEMW hdItem;
5422 TRACE("nColumn=%d\n", nColumn);
5424 /* we have a 'column' in LIST and REPORT mode only */
5425 switch(infoPtr->dwStyle & LVS_TYPEMASK)
5427 case LVS_LIST:
5428 nColumnWidth = infoPtr->nItemWidth;
5429 break;
5430 case LVS_REPORT:
5431 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDM_ITEMCHANGED.
5432 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
5433 * HDM_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
5435 * TODO: should we do the same in LVM_GETCOLUMN?
5437 hdItem.mask = HDI_WIDTH;
5438 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdItem))
5440 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr->hwndSelf, nColumn);
5441 return 0;
5443 nColumnWidth = hdItem.cxy;
5444 break;
5447 TRACE("nColumnWidth=%d\n", nColumnWidth);
5448 return nColumnWidth;
5451 /***
5452 * DESCRIPTION:
5453 * In list or report display mode, retrieves the number of items that can fit
5454 * vertically in the visible area. In icon or small icon display mode,
5455 * retrieves the total number of visible items.
5457 * PARAMETER(S):
5458 * [I] infoPtr : valid pointer to the listview structure
5460 * RETURN:
5461 * Number of fully visible items.
5463 static INT LISTVIEW_GetCountPerPage(const LISTVIEW_INFO *infoPtr)
5465 switch (infoPtr->dwStyle & LVS_TYPEMASK)
5467 case LVS_ICON:
5468 case LVS_SMALLICON:
5469 return infoPtr->nItemCount;
5470 case LVS_REPORT:
5471 return LISTVIEW_GetCountPerColumn(infoPtr);
5472 case LVS_LIST:
5473 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5475 assert(FALSE);
5476 return 0;
5479 /***
5480 * DESCRIPTION:
5481 * Retrieves an image list handle.
5483 * PARAMETER(S):
5484 * [I] infoPtr : valid pointer to the listview structure
5485 * [I] nImageList : image list identifier
5487 * RETURN:
5488 * SUCCESS : image list handle
5489 * FAILURE : NULL
5491 static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImageList)
5493 switch (nImageList)
5495 case LVSIL_NORMAL: return infoPtr->himlNormal;
5496 case LVSIL_SMALL: return infoPtr->himlSmall;
5497 case LVSIL_STATE: return infoPtr->himlState;
5499 return NULL;
5502 /* LISTVIEW_GetISearchString */
5504 /***
5505 * DESCRIPTION:
5506 * Retrieves item attributes.
5508 * PARAMETER(S):
5509 * [I] hwnd : window handle
5510 * [IO] lpLVItem : item info
5511 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5512 * if FALSE, then lpLVItem is a LPLVITEMA.
5514 * NOTE:
5515 * This is the internal 'GetItem' interface -- it tries to
5516 * be smart and avoid text copies, if possible, by modifying
5517 * lpLVItem->pszText to point to the text string. Please note
5518 * that this is not always possible (e.g. OWNERDATA), so on
5519 * entry you *must* supply valid values for pszText, and cchTextMax.
5520 * The only difference to the documented interface is that upon
5521 * return, you should use *only* the lpLVItem->pszText, rather than
5522 * the buffer pointer you provided on input. Most code already does
5523 * that, so it's not a problem.
5524 * For the two cases when the text must be copied (that is,
5525 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5527 * RETURN:
5528 * SUCCESS : TRUE
5529 * FAILURE : FALSE
5531 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5533 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5534 NMLVDISPINFOW dispInfo;
5535 ITEM_INFO *lpItem;
5536 ITEMHDR* pItemHdr;
5537 HDPA hdpaSubItems;
5538 INT isubitem;
5540 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5542 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5543 return FALSE;
5545 if (lpLVItem->mask == 0) return TRUE;
5547 /* make a local copy */
5548 isubitem = lpLVItem->iSubItem;
5550 /* a quick optimization if all we're asked is the focus state
5551 * these queries are worth optimising since they are common,
5552 * and can be answered in constant time, without the heavy accesses */
5553 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5554 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5556 lpLVItem->state = 0;
5557 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5558 lpLVItem->state |= LVIS_FOCUSED;
5559 return TRUE;
5562 ZeroMemory(&dispInfo, sizeof(dispInfo));
5564 /* if the app stores all the data, handle it separately */
5565 if (infoPtr->dwStyle & LVS_OWNERDATA)
5567 dispInfo.item.state = 0;
5569 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5570 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5572 UINT mask = lpLVItem->mask;
5574 /* NOTE: copy only fields which we _know_ are initialized, some apps
5575 * depend on the uninitialized fields being 0 */
5576 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5577 dispInfo.item.iItem = lpLVItem->iItem;
5578 dispInfo.item.iSubItem = isubitem;
5579 if (lpLVItem->mask & LVIF_TEXT)
5581 if (lpLVItem->mask & LVIF_NORECOMPUTE)
5582 /* reset mask */
5583 dispInfo.item.mask &= ~(LVIF_TEXT | LVIF_NORECOMPUTE);
5584 else
5586 dispInfo.item.pszText = lpLVItem->pszText;
5587 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5590 if (lpLVItem->mask & LVIF_STATE)
5591 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5592 /* could be zeroed on LVIF_NORECOMPUTE case */
5593 if (dispInfo.item.mask != 0)
5595 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5596 dispInfo.item.stateMask = lpLVItem->stateMask;
5597 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5599 /* full size structure expected - _WIN32IE >= 0x560 */
5600 *lpLVItem = dispInfo.item;
5602 else if (lpLVItem->mask & LVIF_INDENT)
5604 /* indent member expected - _WIN32IE >= 0x300 */
5605 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5607 else
5609 /* minimal structure expected */
5610 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5612 lpLVItem->mask = mask;
5613 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5617 /* make sure lParam is zeroed out */
5618 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5620 /* callback marked pointer required here */
5621 if ((lpLVItem->mask & LVIF_TEXT) && (lpLVItem->mask & LVIF_NORECOMPUTE))
5622 lpLVItem->pszText = LPSTR_TEXTCALLBACKW;
5624 /* we store only a little state, so if we're not asked, we're done */
5625 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5627 /* if focus is handled by us, report it */
5628 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5630 lpLVItem->state &= ~LVIS_FOCUSED;
5631 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5632 lpLVItem->state |= LVIS_FOCUSED;
5635 /* and do the same for selection, if we handle it */
5636 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5638 lpLVItem->state &= ~LVIS_SELECTED;
5639 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5640 lpLVItem->state |= LVIS_SELECTED;
5643 return TRUE;
5646 /* find the item and subitem structures before we proceed */
5647 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5648 lpItem = DPA_GetPtr(hdpaSubItems, 0);
5649 assert (lpItem);
5651 if (isubitem)
5653 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5654 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5655 if (!lpSubItem)
5657 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5658 isubitem = 0;
5661 else
5662 pItemHdr = &lpItem->hdr;
5664 /* Do we need to query the state from the app? */
5665 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5667 dispInfo.item.mask |= LVIF_STATE;
5668 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5671 /* Do we need to enquire about the image? */
5672 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5673 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5675 dispInfo.item.mask |= LVIF_IMAGE;
5676 dispInfo.item.iImage = I_IMAGECALLBACK;
5679 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5680 if ((lpLVItem->mask & LVIF_TEXT) && !(lpLVItem->mask & LVIF_NORECOMPUTE) &&
5681 !is_textW(pItemHdr->pszText))
5683 dispInfo.item.mask |= LVIF_TEXT;
5684 dispInfo.item.pszText = lpLVItem->pszText;
5685 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5686 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5687 *dispInfo.item.pszText = '\0';
5690 /* If we don't have all the requested info, query the application */
5691 if (dispInfo.item.mask != 0)
5693 dispInfo.item.iItem = lpLVItem->iItem;
5694 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5695 dispInfo.item.lParam = lpItem->lParam;
5696 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5697 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5700 /* we should not store values for subitems */
5701 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5703 /* Now, handle the iImage field */
5704 if (dispInfo.item.mask & LVIF_IMAGE)
5706 lpLVItem->iImage = dispInfo.item.iImage;
5707 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5708 pItemHdr->iImage = dispInfo.item.iImage;
5710 else if (lpLVItem->mask & LVIF_IMAGE)
5712 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5713 lpLVItem->iImage = pItemHdr->iImage;
5714 else
5715 lpLVItem->iImage = 0;
5718 /* The pszText field */
5719 if (dispInfo.item.mask & LVIF_TEXT)
5721 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5722 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5724 lpLVItem->pszText = dispInfo.item.pszText;
5726 else if (lpLVItem->mask & LVIF_TEXT)
5728 /* if LVN_GETDISPINFO's disabled with LVIF_NORECOMPUTE return callback placeholder */
5729 if (isW || !is_textW(pItemHdr->pszText)) lpLVItem->pszText = pItemHdr->pszText;
5730 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5733 /* Next is the lParam field */
5734 if (dispInfo.item.mask & LVIF_PARAM)
5736 lpLVItem->lParam = dispInfo.item.lParam;
5737 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5738 lpItem->lParam = dispInfo.item.lParam;
5740 else if (lpLVItem->mask & LVIF_PARAM)
5741 lpLVItem->lParam = lpItem->lParam;
5743 /* if this is a subitem, we're done */
5744 if (isubitem) return TRUE;
5746 /* ... the state field (this one is different due to uCallbackmask) */
5747 if (lpLVItem->mask & LVIF_STATE)
5749 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5750 if (dispInfo.item.mask & LVIF_STATE)
5752 lpLVItem->state &= ~dispInfo.item.stateMask;
5753 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5755 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5757 lpLVItem->state &= ~LVIS_FOCUSED;
5758 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5759 lpLVItem->state |= LVIS_FOCUSED;
5761 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5763 lpLVItem->state &= ~LVIS_SELECTED;
5764 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5765 lpLVItem->state |= LVIS_SELECTED;
5769 /* and last, but not least, the indent field */
5770 if (lpLVItem->mask & LVIF_INDENT)
5771 lpLVItem->iIndent = lpItem->iIndent;
5773 return TRUE;
5776 /***
5777 * DESCRIPTION:
5778 * Retrieves item attributes.
5780 * PARAMETER(S):
5781 * [I] hwnd : window handle
5782 * [IO] lpLVItem : item info
5783 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5784 * if FALSE, then lpLVItem is a LPLVITEMA.
5786 * NOTE:
5787 * This is the external 'GetItem' interface -- it properly copies
5788 * the text in the provided buffer.
5790 * RETURN:
5791 * SUCCESS : TRUE
5792 * FAILURE : FALSE
5794 static BOOL LISTVIEW_GetItemExtT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5796 LPWSTR pszText;
5797 BOOL bResult;
5799 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5800 return FALSE;
5802 pszText = lpLVItem->pszText;
5803 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5804 if (bResult && lpLVItem->pszText != pszText)
5806 if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW)
5807 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5808 else
5809 pszText = LPSTR_TEXTCALLBACKW;
5811 lpLVItem->pszText = pszText;
5813 return bResult;
5817 /***
5818 * DESCRIPTION:
5819 * Retrieves the position (upper-left) of the listview control item.
5820 * Note that for LVS_ICON style, the upper-left is that of the icon
5821 * and not the bounding box.
5823 * PARAMETER(S):
5824 * [I] infoPtr : valid pointer to the listview structure
5825 * [I] nItem : item index
5826 * [O] lpptPosition : coordinate information
5828 * RETURN:
5829 * SUCCESS : TRUE
5830 * FAILURE : FALSE
5832 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5834 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5835 POINT Origin;
5837 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5839 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5841 LISTVIEW_GetOrigin(infoPtr, &Origin);
5842 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5844 if (uView == LVS_ICON)
5846 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5847 lpptPosition->y += ICON_TOP_PADDING;
5849 lpptPosition->x += Origin.x;
5850 lpptPosition->y += Origin.y;
5852 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
5853 return TRUE;
5857 /***
5858 * DESCRIPTION:
5859 * Retrieves the bounding rectangle for a listview control item.
5861 * PARAMETER(S):
5862 * [I] infoPtr : valid pointer to the listview structure
5863 * [I] nItem : item index
5864 * [IO] lprc : bounding rectangle coordinates
5865 * lprc->left specifies the portion of the item for which the bounding
5866 * rectangle will be retrieved.
5868 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5869 * including the icon and label.
5871 * * For LVS_ICON
5872 * * Experiment shows that native control returns:
5873 * * width = min (48, length of text line)
5874 * * .left = position.x - (width - iconsize.cx)/2
5875 * * .right = .left + width
5876 * * height = #lines of text * ntmHeight + icon height + 8
5877 * * .top = position.y - 2
5878 * * .bottom = .top + height
5879 * * separation between items .y = itemSpacing.cy - height
5880 * * .x = itemSpacing.cx - width
5881 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5883 * * For LVS_ICON
5884 * * Experiment shows that native control returns:
5885 * * width = iconSize.cx + 16
5886 * * .left = position.x - (width - iconsize.cx)/2
5887 * * .right = .left + width
5888 * * height = iconSize.cy + 4
5889 * * .top = position.y - 2
5890 * * .bottom = .top + height
5891 * * separation between items .y = itemSpacing.cy - height
5892 * * .x = itemSpacing.cx - width
5893 * LVIR_LABEL Returns the bounding rectangle of the item text.
5895 * * For LVS_ICON
5896 * * Experiment shows that native control returns:
5897 * * width = text length
5898 * * .left = position.x - width/2
5899 * * .right = .left + width
5900 * * height = ntmH * linecount + 2
5901 * * .top = position.y + iconSize.cy + 6
5902 * * .bottom = .top + height
5903 * * separation between items .y = itemSpacing.cy - height
5904 * * .x = itemSpacing.cx - width
5905 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5906 * rectangles, but excludes columns in report view.
5908 * RETURN:
5909 * SUCCESS : TRUE
5910 * FAILURE : FALSE
5912 * NOTES
5913 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5914 * upon whether the window has the focus currently and on whether the item
5915 * is the one with the focus. Ensure that the control's record of which
5916 * item has the focus agrees with the items' records.
5918 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5920 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5921 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5922 BOOL doLabel = TRUE, oversizedBox = FALSE;
5923 POINT Position, Origin;
5924 LVITEMW lvItem;
5926 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5928 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5930 LISTVIEW_GetOrigin(infoPtr, &Origin);
5931 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5933 /* Be smart and try to figure out the minimum we have to do */
5934 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5935 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5936 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5937 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5938 oversizedBox = TRUE;
5940 /* get what we need from the item before hand, so we make
5941 * only one request. This can speed up things, if data
5942 * is stored on the app side */
5943 lvItem.mask = 0;
5944 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5945 if (doLabel) lvItem.mask |= LVIF_TEXT;
5946 lvItem.iItem = nItem;
5947 lvItem.iSubItem = 0;
5948 lvItem.pszText = szDispText;
5949 lvItem.cchTextMax = DISP_TEXT_SIZE;
5950 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5951 /* we got the state already up, simulate it here, to avoid a reget */
5952 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5954 lvItem.mask |= LVIF_STATE;
5955 lvItem.stateMask = LVIS_FOCUSED;
5956 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5959 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5960 lprc->left = LVIR_BOUNDS;
5961 switch(lprc->left)
5963 case LVIR_ICON:
5964 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
5965 break;
5967 case LVIR_LABEL:
5968 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc);
5969 break;
5971 case LVIR_BOUNDS:
5972 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
5973 break;
5975 case LVIR_SELECTBOUNDS:
5976 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL);
5977 break;
5979 default:
5980 WARN("Unknown value: %d\n", lprc->left);
5981 return FALSE;
5984 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5986 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
5988 return TRUE;
5991 /***
5992 * DESCRIPTION:
5993 * Retrieves the spacing between listview control items.
5995 * PARAMETER(S):
5996 * [I] infoPtr : valid pointer to the listview structure
5997 * [IO] lprc : rectangle to receive the output
5998 * on input, lprc->top = nSubItem
5999 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
6001 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
6002 * not only those of the first column.
6003 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
6005 * RETURN:
6006 * TRUE: success
6007 * FALSE: failure
6009 static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
6011 POINT Position;
6012 LVITEMW lvItem;
6013 INT nColumn;
6015 if (!lprc) return FALSE;
6017 nColumn = lprc->top;
6019 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
6020 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
6021 if (lprc->top == 0)
6022 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
6024 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
6026 /* special case for header items */
6027 if (nItem == -1)
6029 if (lprc->left != LVIR_BOUNDS)
6031 FIXME("Only LVIR_BOUNDS is implemented for header, got %d\n", lprc->left);
6032 return FALSE;
6035 if (infoPtr->hwndHeader)
6036 return Header_GetItemRect(infoPtr->hwndHeader, lprc->top, lprc);
6037 else
6039 memset(lprc, 0, sizeof(RECT));
6040 return TRUE;
6044 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
6046 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6048 lvItem.mask = 0;
6049 lvItem.iItem = nItem;
6050 lvItem.iSubItem = nColumn;
6052 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
6053 switch(lprc->left)
6055 case LVIR_ICON:
6056 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
6057 break;
6059 case LVIR_LABEL:
6060 case LVIR_BOUNDS:
6061 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
6062 break;
6064 default:
6065 ERR("Unknown bounds=%d\n", lprc->left);
6066 return FALSE;
6069 OffsetRect(lprc, Position.x, Position.y);
6070 return TRUE;
6074 /***
6075 * DESCRIPTION:
6076 * Retrieves the width of a label.
6078 * PARAMETER(S):
6079 * [I] infoPtr : valid pointer to the listview structure
6081 * RETURN:
6082 * SUCCESS : string width (in pixels)
6083 * FAILURE : zero
6085 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *infoPtr, INT nItem)
6087 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6088 LVITEMW lvItem;
6090 TRACE("(nItem=%d)\n", nItem);
6092 lvItem.mask = LVIF_TEXT;
6093 lvItem.iItem = nItem;
6094 lvItem.iSubItem = 0;
6095 lvItem.pszText = szDispText;
6096 lvItem.cchTextMax = DISP_TEXT_SIZE;
6097 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6099 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6102 /***
6103 * DESCRIPTION:
6104 * Retrieves the spacing between listview control items.
6106 * PARAMETER(S):
6107 * [I] infoPtr : valid pointer to the listview structure
6108 * [I] bSmall : flag for small or large icon
6110 * RETURN:
6111 * Horizontal + vertical spacing
6113 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall)
6115 LONG lResult;
6117 if (!bSmall)
6119 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6121 else
6123 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
6124 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6125 else
6126 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6128 return lResult;
6131 /***
6132 * DESCRIPTION:
6133 * Retrieves the state of a listview control item.
6135 * PARAMETER(S):
6136 * [I] infoPtr : valid pointer to the listview structure
6137 * [I] nItem : item index
6138 * [I] uMask : state mask
6140 * RETURN:
6141 * State specified by the mask.
6143 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
6145 LVITEMW lvItem;
6147 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6149 lvItem.iItem = nItem;
6150 lvItem.iSubItem = 0;
6151 lvItem.mask = LVIF_STATE;
6152 lvItem.stateMask = uMask;
6153 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6155 return lvItem.state & uMask;
6158 /***
6159 * DESCRIPTION:
6160 * Retrieves the text of a listview control item or subitem.
6162 * PARAMETER(S):
6163 * [I] hwnd : window handle
6164 * [I] nItem : item index
6165 * [IO] lpLVItem : item information
6166 * [I] isW : TRUE if lpLVItem is Unicode
6168 * RETURN:
6169 * SUCCESS : string length
6170 * FAILURE : 0
6172 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6174 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6176 lpLVItem->mask = LVIF_TEXT;
6177 lpLVItem->iItem = nItem;
6178 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
6180 return textlenT(lpLVItem->pszText, isW);
6183 /***
6184 * DESCRIPTION:
6185 * Searches for an item based on properties + relationships.
6187 * PARAMETER(S):
6188 * [I] infoPtr : valid pointer to the listview structure
6189 * [I] nItem : item index
6190 * [I] uFlags : relationship flag
6192 * RETURN:
6193 * SUCCESS : item index
6194 * FAILURE : -1
6196 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
6198 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6199 UINT uMask = 0;
6200 LVFINDINFOW lvFindInfo;
6201 INT nCountPerColumn;
6202 INT nCountPerRow;
6203 INT i;
6205 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
6206 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
6208 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6210 if (uFlags & LVNI_CUT)
6211 uMask |= LVIS_CUT;
6213 if (uFlags & LVNI_DROPHILITED)
6214 uMask |= LVIS_DROPHILITED;
6216 if (uFlags & LVNI_FOCUSED)
6217 uMask |= LVIS_FOCUSED;
6219 if (uFlags & LVNI_SELECTED)
6220 uMask |= LVIS_SELECTED;
6222 /* if we're asked for the focused item, that's only one,
6223 * so it's worth optimizing */
6224 if (uFlags & LVNI_FOCUSED)
6226 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
6227 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
6230 if (uFlags & LVNI_ABOVE)
6232 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6234 while (nItem >= 0)
6236 nItem--;
6237 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6238 return nItem;
6241 else
6243 /* Special case for autoarrange - move 'til the top of a list */
6244 if (is_autoarrange(infoPtr))
6246 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6247 while (nItem - nCountPerRow >= 0)
6249 nItem -= nCountPerRow;
6250 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6251 return nItem;
6253 return -1;
6255 lvFindInfo.flags = LVFI_NEARESTXY;
6256 lvFindInfo.vkDirection = VK_UP;
6257 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6258 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6260 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6261 return nItem;
6265 else if (uFlags & LVNI_BELOW)
6267 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6269 while (nItem < infoPtr->nItemCount)
6271 nItem++;
6272 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6273 return nItem;
6276 else
6278 /* Special case for autoarrange - move 'til the bottom of a list */
6279 if (is_autoarrange(infoPtr))
6281 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6282 while (nItem + nCountPerRow < infoPtr->nItemCount )
6284 nItem += nCountPerRow;
6285 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6286 return nItem;
6288 return -1;
6290 lvFindInfo.flags = LVFI_NEARESTXY;
6291 lvFindInfo.vkDirection = VK_DOWN;
6292 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6293 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6295 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6296 return nItem;
6300 else if (uFlags & LVNI_TOLEFT)
6302 if (uView == LVS_LIST)
6304 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6305 while (nItem - nCountPerColumn >= 0)
6307 nItem -= nCountPerColumn;
6308 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6309 return nItem;
6312 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6314 /* Special case for autoarrange - move 'til the beginning of a row */
6315 if (is_autoarrange(infoPtr))
6317 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6318 while (nItem % nCountPerRow > 0)
6320 nItem --;
6321 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6322 return nItem;
6324 return -1;
6326 lvFindInfo.flags = LVFI_NEARESTXY;
6327 lvFindInfo.vkDirection = VK_LEFT;
6328 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6329 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6331 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6332 return nItem;
6336 else if (uFlags & LVNI_TORIGHT)
6338 if (uView == LVS_LIST)
6340 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6341 while (nItem + nCountPerColumn < infoPtr->nItemCount)
6343 nItem += nCountPerColumn;
6344 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6345 return nItem;
6348 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6350 /* Special case for autoarrange - move 'til the end of a row */
6351 if (is_autoarrange(infoPtr))
6353 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6354 while (nItem % nCountPerRow < nCountPerRow - 1 )
6356 nItem ++;
6357 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6358 return nItem;
6360 return -1;
6362 lvFindInfo.flags = LVFI_NEARESTXY;
6363 lvFindInfo.vkDirection = VK_RIGHT;
6364 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6365 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6367 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6368 return nItem;
6372 else
6374 nItem++;
6376 /* search by index */
6377 for (i = nItem; i < infoPtr->nItemCount; i++)
6379 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
6380 return i;
6384 return -1;
6387 /* LISTVIEW_GetNumberOfWorkAreas */
6389 /***
6390 * DESCRIPTION:
6391 * Retrieves the origin coordinates when in icon or small icon display mode.
6393 * PARAMETER(S):
6394 * [I] infoPtr : valid pointer to the listview structure
6395 * [O] lpptOrigin : coordinate information
6397 * RETURN:
6398 * None.
6400 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
6402 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6403 INT nHorzPos = 0, nVertPos = 0;
6404 SCROLLINFO scrollInfo;
6406 scrollInfo.cbSize = sizeof(SCROLLINFO);
6407 scrollInfo.fMask = SIF_POS;
6409 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6410 nHorzPos = scrollInfo.nPos;
6411 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6412 nVertPos = scrollInfo.nPos;
6414 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6416 lpptOrigin->x = infoPtr->rcList.left;
6417 lpptOrigin->y = infoPtr->rcList.top;
6418 if (uView == LVS_LIST)
6419 nHorzPos *= infoPtr->nItemWidth;
6420 else if (uView == LVS_REPORT)
6421 nVertPos *= infoPtr->nItemHeight;
6423 lpptOrigin->x -= nHorzPos;
6424 lpptOrigin->y -= nVertPos;
6426 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
6429 /***
6430 * DESCRIPTION:
6431 * Retrieves the width of a string.
6433 * PARAMETER(S):
6434 * [I] hwnd : window handle
6435 * [I] lpszText : text string to process
6436 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6438 * RETURN:
6439 * SUCCESS : string width (in pixels)
6440 * FAILURE : zero
6442 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6444 SIZE stringSize;
6446 stringSize.cx = 0;
6447 if (is_textT(lpszText, isW))
6449 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6450 HDC hdc = GetDC(infoPtr->hwndSelf);
6451 HFONT hOldFont = SelectObject(hdc, hFont);
6453 if (isW)
6454 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6455 else
6456 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6457 SelectObject(hdc, hOldFont);
6458 ReleaseDC(infoPtr->hwndSelf, hdc);
6460 return stringSize.cx;
6463 /***
6464 * DESCRIPTION:
6465 * Determines which listview item is located at the specified position.
6467 * PARAMETER(S):
6468 * [I] infoPtr : valid pointer to the listview structure
6469 * [IO] lpht : hit test information
6470 * [I] subitem : fill out iSubItem.
6471 * [I] select : return the index only if the hit selects the item
6473 * NOTE:
6474 * (mm 20001022): We must not allow iSubItem to be touched, for
6475 * an app might pass only a structure with space up to iItem!
6476 * (MS Office 97 does that for instance in the file open dialog)
6478 * RETURN:
6479 * SUCCESS : item index
6480 * FAILURE : -1
6482 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6484 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6485 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6486 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6487 POINT Origin, Position, opt;
6488 LVITEMW lvItem;
6489 ITERATOR i;
6490 INT iItem;
6492 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6494 lpht->flags = 0;
6495 lpht->iItem = -1;
6496 if (subitem) lpht->iSubItem = 0;
6498 if (infoPtr->rcList.left > lpht->pt.x)
6499 lpht->flags |= LVHT_TOLEFT;
6500 else if (infoPtr->rcList.right < lpht->pt.x)
6501 lpht->flags |= LVHT_TORIGHT;
6503 if (infoPtr->rcList.top > lpht->pt.y)
6504 lpht->flags |= LVHT_ABOVE;
6505 else if (infoPtr->rcList.bottom < lpht->pt.y)
6506 lpht->flags |= LVHT_BELOW;
6508 TRACE("lpht->flags=0x%x\n", lpht->flags);
6509 if (lpht->flags) return -1;
6511 lpht->flags |= LVHT_NOWHERE;
6513 LISTVIEW_GetOrigin(infoPtr, &Origin);
6515 /* first deal with the large items */
6516 rcSearch.left = lpht->pt.x;
6517 rcSearch.top = lpht->pt.y;
6518 rcSearch.right = rcSearch.left + 1;
6519 rcSearch.bottom = rcSearch.top + 1;
6521 iterator_frameditems(&i, infoPtr, &rcSearch);
6522 iterator_next(&i); /* go to first item in the sequence */
6523 iItem = i.nItem;
6524 iterator_destroy(&i);
6526 TRACE("lpht->iItem=%d\n", iItem);
6527 if (iItem == -1) return -1;
6529 lvItem.mask = LVIF_STATE | LVIF_TEXT;
6530 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6531 lvItem.stateMask = LVIS_STATEIMAGEMASK;
6532 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6533 lvItem.iItem = iItem;
6534 lvItem.iSubItem = 0;
6535 lvItem.pszText = szDispText;
6536 lvItem.cchTextMax = DISP_TEXT_SIZE;
6537 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6538 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6540 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
6541 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6542 opt.x = lpht->pt.x - Position.x - Origin.x;
6543 opt.y = lpht->pt.y - Position.y - Origin.y;
6545 if (uView == LVS_REPORT)
6546 rcBounds = rcBox;
6547 else
6549 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6550 UnionRect(&rcBounds, &rcBounds, &rcState);
6552 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6553 if (!PtInRect(&rcBounds, opt)) return -1;
6555 if (PtInRect(&rcIcon, opt))
6556 lpht->flags |= LVHT_ONITEMICON;
6557 else if (PtInRect(&rcLabel, opt))
6558 lpht->flags |= LVHT_ONITEMLABEL;
6559 else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6560 lpht->flags |= LVHT_ONITEMSTATEICON;
6561 if (lpht->flags & LVHT_ONITEM)
6562 lpht->flags &= ~LVHT_NOWHERE;
6564 TRACE("lpht->flags=0x%x\n", lpht->flags);
6565 if (uView == LVS_REPORT && subitem)
6567 INT j;
6569 rcBounds.right = rcBounds.left;
6570 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6572 rcBounds.left = rcBounds.right;
6573 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6574 if (PtInRect(&rcBounds, opt))
6576 lpht->iSubItem = j;
6577 break;
6582 if (select && !(uView == LVS_REPORT &&
6583 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6584 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6586 if (uView == LVS_REPORT)
6588 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6589 UnionRect(&rcBounds, &rcBounds, &rcState);
6591 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6593 return lpht->iItem = iItem;
6597 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6598 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6599 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6600 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6601 their own sort proc. when sending LVM_SORTITEMS.
6603 /* Platform SDK:
6604 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6606 LVS_SORTXXX must be specified,
6607 LVS_OWNERDRAW is not set,
6608 <item>.pszText is not LPSTR_TEXTCALLBACK.
6610 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6611 are sorted based on item text..."
6613 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6615 ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
6616 ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
6617 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6619 /* if we're sorting descending, negate the return value */
6620 return (((const LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6623 /***
6624 * DESCRIPTION:
6625 * Inserts a new item in the listview control.
6627 * PARAMETER(S):
6628 * [I] infoPtr : valid pointer to the listview structure
6629 * [I] lpLVItem : item information
6630 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6632 * RETURN:
6633 * SUCCESS : new item index
6634 * FAILURE : -1
6636 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6638 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6639 INT nItem;
6640 HDPA hdpaSubItems;
6641 NMLISTVIEW nmlv;
6642 ITEM_INFO *lpItem;
6643 BOOL is_sorted, has_changed;
6644 LVITEMW item;
6645 HWND hwndSelf = infoPtr->hwndSelf;
6647 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6649 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6651 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6652 if (!lpLVItem || lpLVItem->iSubItem) return -1;
6654 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6656 if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
6658 /* insert item in listview control data structure */
6659 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6660 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6662 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6663 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6665 if (lpLVItem->iItem < 0 && !is_sorted) return -1;
6667 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6668 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6669 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6670 if (nItem == -1) goto fail;
6671 infoPtr->nItemCount++;
6673 /* shift indices first so they don't get tangled */
6674 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6676 /* set the item attributes */
6677 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6679 /* full size structure expected - _WIN32IE >= 0x560 */
6680 item = *lpLVItem;
6682 else if (lpLVItem->mask & LVIF_INDENT)
6684 /* indent member expected - _WIN32IE >= 0x300 */
6685 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6687 else
6689 /* minimal structure expected */
6690 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6692 item.iItem = nItem;
6693 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6695 item.mask |= LVIF_STATE;
6696 item.stateMask |= LVIS_STATEIMAGEMASK;
6697 item.state &= ~LVIS_STATEIMAGEMASK;
6698 item.state |= INDEXTOSTATEIMAGEMASK(1);
6700 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6702 /* if we're sorted, sort the list, and update the index */
6703 if (is_sorted)
6705 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6706 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6707 assert(nItem != -1);
6710 /* make room for the position, if we are in the right mode */
6711 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6713 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6714 goto undo;
6715 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6717 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6718 goto undo;
6722 /* send LVN_INSERTITEM notification */
6723 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6724 nmlv.iItem = nItem;
6725 nmlv.lParam = lpItem->lParam;
6726 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6727 if (!IsWindow(hwndSelf))
6728 return -1;
6730 /* align items (set position of each item) */
6731 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6733 POINT pt;
6735 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6736 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6737 else
6738 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6740 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6743 /* now is the invalidation fun */
6744 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6745 return nItem;
6747 undo:
6748 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6749 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6750 infoPtr->nItemCount--;
6751 fail:
6752 DPA_DeletePtr(hdpaSubItems, 0);
6753 DPA_Destroy (hdpaSubItems);
6754 Free (lpItem);
6755 return -1;
6758 /***
6759 * DESCRIPTION:
6760 * Redraws a range of items.
6762 * PARAMETER(S):
6763 * [I] infoPtr : valid pointer to the listview structure
6764 * [I] nFirst : first item
6765 * [I] nLast : last item
6767 * RETURN:
6768 * SUCCESS : TRUE
6769 * FAILURE : FALSE
6771 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6773 INT i;
6775 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6776 max(nFirst, nLast) >= infoPtr->nItemCount)
6777 return FALSE;
6779 for (i = nFirst; i <= nLast; i++)
6780 LISTVIEW_InvalidateItem(infoPtr, i);
6782 return TRUE;
6785 /***
6786 * DESCRIPTION:
6787 * Scroll the content of a listview.
6789 * PARAMETER(S):
6790 * [I] infoPtr : valid pointer to the listview structure
6791 * [I] dx : horizontal scroll amount in pixels
6792 * [I] dy : vertical scroll amount in pixels
6794 * RETURN:
6795 * SUCCESS : TRUE
6796 * FAILURE : FALSE
6798 * COMMENTS:
6799 * If the control is in report mode (LVS_REPORT) the control can
6800 * be scrolled only in line increments. "dy" will be rounded to the
6801 * nearest number of pixels that are a whole line. Ex: if line height
6802 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6803 * is passed, then the scroll will be 0. (per MSDN 7/2002)
6805 * For: (per experimentation with native control and CSpy ListView)
6806 * LVS_ICON dy=1 = 1 pixel (vertical only)
6807 * dx ignored
6808 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6809 * dx ignored
6810 * LVS_LIST dx=1 = 1 column (horizontal only)
6811 * but will only scroll 1 column per message
6812 * no matter what the value.
6813 * dy must be 0 or FALSE returned.
6814 * LVS_REPORT dx=1 = 1 pixel
6815 * dy= see above
6818 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6820 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6821 case LVS_REPORT:
6822 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6823 dy /= infoPtr->nItemHeight;
6824 break;
6825 case LVS_LIST:
6826 if (dy != 0) return FALSE;
6827 break;
6828 default: /* icon */
6829 dx = 0;
6830 break;
6833 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6834 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6836 return TRUE;
6839 /***
6840 * DESCRIPTION:
6841 * Sets the background color.
6843 * PARAMETER(S):
6844 * [I] infoPtr : valid pointer to the listview structure
6845 * [I] clrBk : background color
6847 * RETURN:
6848 * SUCCESS : TRUE
6849 * FAILURE : FALSE
6851 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6853 TRACE("(clrBk=%x)\n", clrBk);
6855 if(infoPtr->clrBk != clrBk) {
6856 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6857 infoPtr->clrBk = clrBk;
6858 if (clrBk == CLR_NONE)
6859 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6860 else
6861 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6862 LISTVIEW_InvalidateList(infoPtr);
6865 return TRUE;
6868 /* LISTVIEW_SetBkImage */
6870 /*** Helper for {Insert,Set}ColumnT *only* */
6871 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
6872 const LVCOLUMNW *lpColumn, BOOL isW)
6874 if (lpColumn->mask & LVCF_FMT)
6876 /* format member is valid */
6877 lphdi->mask |= HDI_FORMAT;
6879 /* set text alignment (leftmost column must be left-aligned) */
6880 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6881 lphdi->fmt |= HDF_LEFT;
6882 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6883 lphdi->fmt |= HDF_RIGHT;
6884 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6885 lphdi->fmt |= HDF_CENTER;
6887 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6888 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6890 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6892 lphdi->fmt |= HDF_IMAGE;
6893 lphdi->iImage = I_IMAGECALLBACK;
6897 if (lpColumn->mask & LVCF_WIDTH)
6899 lphdi->mask |= HDI_WIDTH;
6900 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6902 /* make it fill the remainder of the controls width */
6903 RECT rcHeader;
6904 INT item_index;
6906 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6908 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6909 lphdi->cxy += rcHeader.right - rcHeader.left;
6912 /* retrieve the layout of the header */
6913 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6914 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
6916 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6918 else
6919 lphdi->cxy = lpColumn->cx;
6922 if (lpColumn->mask & LVCF_TEXT)
6924 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6925 lphdi->fmt |= HDF_STRING;
6926 lphdi->pszText = lpColumn->pszText;
6927 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6930 if (lpColumn->mask & LVCF_IMAGE)
6932 lphdi->mask |= HDI_IMAGE;
6933 lphdi->iImage = lpColumn->iImage;
6936 if (lpColumn->mask & LVCF_ORDER)
6938 lphdi->mask |= HDI_ORDER;
6939 lphdi->iOrder = lpColumn->iOrder;
6944 /***
6945 * DESCRIPTION:
6946 * Inserts a new column.
6948 * PARAMETER(S):
6949 * [I] infoPtr : valid pointer to the listview structure
6950 * [I] nColumn : column index
6951 * [I] lpColumn : column information
6952 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6954 * RETURN:
6955 * SUCCESS : new column index
6956 * FAILURE : -1
6958 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6959 const LVCOLUMNW *lpColumn, BOOL isW)
6961 COLUMN_INFO *lpColumnInfo;
6962 INT nNewColumn;
6963 HDITEMW hdi;
6964 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6966 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6968 if (!lpColumn || nColumn < 0) return -1;
6969 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6971 ZeroMemory(&hdi, sizeof(HDITEMW));
6972 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6975 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
6976 * (can be seen in SPY) otherwise column never gets added.
6978 if (!(lpColumn->mask & LVCF_WIDTH)) {
6979 hdi.mask |= HDI_WIDTH;
6980 hdi.cxy = 10;
6984 * when the iSubItem is available Windows copies it to the header lParam. It seems
6985 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
6987 if (lpColumn->mask & LVCF_SUBITEM)
6989 hdi.mask |= HDI_LPARAM;
6990 hdi.lParam = lpColumn->iSubItem;
6993 /* create header if not present */
6994 LISTVIEW_CreateHeader(infoPtr);
6995 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle) &&
6996 (LVS_REPORT == uView) && (WS_VISIBLE & infoPtr->dwStyle))
6998 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7001 /* insert item in header control */
7002 nNewColumn = SendMessageW(infoPtr->hwndHeader,
7003 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
7004 (WPARAM)nColumn, (LPARAM)&hdi);
7005 if (nNewColumn == -1) return -1;
7006 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
7008 /* create our own column info */
7009 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
7010 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
7012 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
7013 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
7015 /* now we have to actually adjust the data */
7016 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
7018 SUBITEM_INFO *lpSubItem;
7019 HDPA hdpaSubItems;
7020 INT nItem, i;
7022 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
7024 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
7025 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
7027 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
7028 if (lpSubItem->iSubItem >= nNewColumn)
7029 lpSubItem->iSubItem++;
7034 /* make space for the new column */
7035 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7036 LISTVIEW_UpdateItemSize(infoPtr);
7038 return nNewColumn;
7040 fail:
7041 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
7042 if (lpColumnInfo)
7044 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
7045 Free(lpColumnInfo);
7047 return -1;
7050 /***
7051 * DESCRIPTION:
7052 * Sets the attributes of a header item.
7054 * PARAMETER(S):
7055 * [I] infoPtr : valid pointer to the listview structure
7056 * [I] nColumn : column index
7057 * [I] lpColumn : column attributes
7058 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
7060 * RETURN:
7061 * SUCCESS : TRUE
7062 * FAILURE : FALSE
7064 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
7065 const LVCOLUMNW *lpColumn, BOOL isW)
7067 HDITEMW hdi, hdiget;
7068 BOOL bResult;
7070 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
7072 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7074 ZeroMemory(&hdi, sizeof(HDITEMW));
7075 if (lpColumn->mask & LVCF_FMT)
7077 hdi.mask |= HDI_FORMAT;
7078 hdiget.mask = HDI_FORMAT;
7079 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
7080 hdi.fmt = hdiget.fmt & HDF_STRING;
7082 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
7084 /* set header item attributes */
7085 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
7086 if (!bResult) return FALSE;
7088 if (lpColumn->mask & LVCF_FMT)
7090 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
7091 int oldFmt = lpColumnInfo->fmt;
7093 lpColumnInfo->fmt = lpColumn->fmt;
7094 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
7096 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7097 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
7101 return TRUE;
7104 /***
7105 * DESCRIPTION:
7106 * Sets the column order array
7108 * PARAMETERS:
7109 * [I] infoPtr : valid pointer to the listview structure
7110 * [I] iCount : number of elements in column order array
7111 * [I] lpiArray : pointer to column order array
7113 * RETURN:
7114 * SUCCESS : TRUE
7115 * FAILURE : FALSE
7117 static BOOL LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
7119 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7121 if (!lpiArray)
7122 return FALSE;
7124 return TRUE;
7128 /***
7129 * DESCRIPTION:
7130 * Sets the width of a column
7132 * PARAMETERS:
7133 * [I] infoPtr : valid pointer to the listview structure
7134 * [I] nColumn : column index
7135 * [I] cx : column width
7137 * RETURN:
7138 * SUCCESS : TRUE
7139 * FAILURE : FALSE
7141 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
7143 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7144 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
7145 INT max_cx = 0;
7146 HDITEMW hdi;
7148 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
7150 /* set column width only if in report or list mode */
7151 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
7153 /* take care of invalid cx values */
7154 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
7155 else if (uView == LVS_LIST && cx < 1) return FALSE;
7157 /* resize all columns if in LVS_LIST mode */
7158 if(uView == LVS_LIST)
7160 infoPtr->nItemWidth = cx;
7161 LISTVIEW_InvalidateList(infoPtr);
7162 return TRUE;
7165 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7167 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
7169 INT nLabelWidth;
7170 LVITEMW lvItem;
7172 lvItem.mask = LVIF_TEXT;
7173 lvItem.iItem = 0;
7174 lvItem.iSubItem = nColumn;
7175 lvItem.pszText = szDispText;
7176 lvItem.cchTextMax = DISP_TEXT_SIZE;
7177 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7179 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
7180 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
7181 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
7183 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
7184 max_cx += infoPtr->iconSize.cx;
7185 max_cx += TRAILING_LABEL_PADDING;
7188 /* autosize based on listview items width */
7189 if(cx == LVSCW_AUTOSIZE)
7190 cx = max_cx;
7191 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7193 /* if iCol is the last column make it fill the remainder of the controls width */
7194 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
7196 RECT rcHeader;
7197 POINT Origin;
7199 LISTVIEW_GetOrigin(infoPtr, &Origin);
7200 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
7202 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
7204 else
7206 /* Despite what the MS docs say, if this is not the last
7207 column, then MS resizes the column to the width of the
7208 largest text string in the column, including headers
7209 and items. This is different from LVSCW_AUTOSIZE in that
7210 LVSCW_AUTOSIZE ignores the header string length. */
7211 cx = 0;
7213 /* retrieve header text */
7214 hdi.mask = HDI_TEXT;
7215 hdi.cchTextMax = DISP_TEXT_SIZE;
7216 hdi.pszText = szDispText;
7217 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
7219 HDC hdc = GetDC(infoPtr->hwndSelf);
7220 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
7221 SIZE size;
7223 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
7224 cx = size.cx + TRAILING_HEADER_PADDING;
7225 /* FIXME: Take into account the header image, if one is present */
7226 SelectObject(hdc, old_font);
7227 ReleaseDC(infoPtr->hwndSelf, hdc);
7229 cx = max (cx, max_cx);
7233 if (cx < 0) return FALSE;
7235 /* call header to update the column change */
7236 hdi.mask = HDI_WIDTH;
7237 hdi.cxy = cx;
7238 TRACE("hdi.cxy=%d\n", hdi.cxy);
7239 return Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7242 /***
7243 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
7246 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
7248 HDC hdc_wnd, hdc;
7249 HBITMAP hbm_im, hbm_mask, hbm_orig;
7250 RECT rc;
7251 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
7252 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
7253 HIMAGELIST himl;
7255 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
7256 ILC_COLOR | ILC_MASK, 2, 2);
7257 hdc_wnd = GetDC(infoPtr->hwndSelf);
7258 hdc = CreateCompatibleDC(hdc_wnd);
7259 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
7260 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
7261 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
7263 rc.left = rc.top = 0;
7264 rc.right = GetSystemMetrics(SM_CXSMICON);
7265 rc.bottom = GetSystemMetrics(SM_CYSMICON);
7267 hbm_orig = SelectObject(hdc, hbm_mask);
7268 FillRect(hdc, &rc, hbr_white);
7269 InflateRect(&rc, -2, -2);
7270 FillRect(hdc, &rc, hbr_black);
7272 SelectObject(hdc, hbm_im);
7273 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
7274 SelectObject(hdc, hbm_orig);
7275 ImageList_Add(himl, hbm_im, hbm_mask);
7277 SelectObject(hdc, hbm_im);
7278 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
7279 SelectObject(hdc, hbm_orig);
7280 ImageList_Add(himl, hbm_im, hbm_mask);
7282 DeleteObject(hbm_mask);
7283 DeleteObject(hbm_im);
7284 DeleteDC(hdc);
7286 return himl;
7289 /***
7290 * DESCRIPTION:
7291 * Sets the extended listview style.
7293 * PARAMETERS:
7294 * [I] infoPtr : valid pointer to the listview structure
7295 * [I] dwMask : mask
7296 * [I] dwStyle : style
7298 * RETURN:
7299 * SUCCESS : previous style
7300 * FAILURE : 0
7302 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwExStyle)
7304 DWORD dwOldExStyle = infoPtr->dwLvExStyle;
7306 /* set new style */
7307 if (dwMask)
7308 infoPtr->dwLvExStyle = (dwOldExStyle & ~dwMask) | (dwExStyle & dwMask);
7309 else
7310 infoPtr->dwLvExStyle = dwExStyle;
7312 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_CHECKBOXES)
7314 HIMAGELIST himl = 0;
7315 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7317 LVITEMW item;
7318 item.mask = LVIF_STATE;
7319 item.stateMask = LVIS_STATEIMAGEMASK;
7320 item.state = INDEXTOSTATEIMAGEMASK(1);
7321 LISTVIEW_SetItemState(infoPtr, -1, &item);
7323 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
7325 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
7328 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_HEADERDRAGDROP)
7330 DWORD dwStyle;
7332 /* if not already created */
7333 LISTVIEW_CreateHeader(infoPtr);
7335 dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
7336 if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
7337 dwStyle |= HDS_DRAGDROP;
7338 else
7339 dwStyle &= ~HDS_DRAGDROP;
7340 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
7343 /* GRIDLINES adds decoration at top so changes sizes */
7344 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_GRIDLINES)
7346 LISTVIEW_UpdateSize(infoPtr);
7350 LISTVIEW_InvalidateList(infoPtr);
7351 return dwOldExStyle;
7354 /***
7355 * DESCRIPTION:
7356 * Sets the new hot cursor used during hot tracking and hover selection.
7358 * PARAMETER(S):
7359 * [I] infoPtr : valid pointer to the listview structure
7360 * [I] hCursor : the new hot cursor handle
7362 * RETURN:
7363 * Returns the previous hot cursor
7365 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
7367 HCURSOR oldCursor = infoPtr->hHotCursor;
7369 infoPtr->hHotCursor = hCursor;
7371 return oldCursor;
7375 /***
7376 * DESCRIPTION:
7377 * Sets the hot item index.
7379 * PARAMETERS:
7380 * [I] infoPtr : valid pointer to the listview structure
7381 * [I] iIndex : index
7383 * RETURN:
7384 * SUCCESS : previous hot item index
7385 * FAILURE : -1 (no hot item)
7387 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
7389 INT iOldIndex = infoPtr->nHotItem;
7391 infoPtr->nHotItem = iIndex;
7393 return iOldIndex;
7397 /***
7398 * DESCRIPTION:
7399 * Sets the amount of time the cursor must hover over an item before it is selected.
7401 * PARAMETER(S):
7402 * [I] infoPtr : valid pointer to the listview structure
7403 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7405 * RETURN:
7406 * Returns the previous hover time
7408 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
7410 DWORD oldHoverTime = infoPtr->dwHoverTime;
7412 infoPtr->dwHoverTime = dwHoverTime;
7414 return oldHoverTime;
7417 /***
7418 * DESCRIPTION:
7419 * Sets spacing for icons of LVS_ICON style.
7421 * PARAMETER(S):
7422 * [I] infoPtr : valid pointer to the listview structure
7423 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7424 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7426 * RETURN:
7427 * MAKELONG(oldcx, oldcy)
7429 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
7431 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7432 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7434 TRACE("requested=(%d,%d)\n", cx, cy);
7436 /* this is supported only for LVS_ICON style */
7437 if (uView != LVS_ICON) return oldspacing;
7439 /* set to defaults, if instructed to */
7440 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
7441 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
7443 /* if 0 then compute width
7444 * FIXME: Should scan each item and determine max width of
7445 * icon or label, then make that the width */
7446 if (cx == 0)
7447 cx = infoPtr->iconSpacing.cx;
7449 /* if 0 then compute height */
7450 if (cy == 0)
7451 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
7452 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
7455 infoPtr->iconSpacing.cx = cx;
7456 infoPtr->iconSpacing.cy = cy;
7458 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7459 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
7460 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7461 infoPtr->ntmHeight);
7463 /* these depend on the iconSpacing */
7464 LISTVIEW_UpdateItemSize(infoPtr);
7466 return oldspacing;
7469 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
7471 INT cx, cy;
7473 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
7475 size->cx = cx;
7476 size->cy = cy;
7478 else
7480 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
7481 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7485 /***
7486 * DESCRIPTION:
7487 * Sets image lists.
7489 * PARAMETER(S):
7490 * [I] infoPtr : valid pointer to the listview structure
7491 * [I] nType : image list type
7492 * [I] himl : image list handle
7494 * RETURN:
7495 * SUCCESS : old image list
7496 * FAILURE : NULL
7498 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7500 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7501 INT oldHeight = infoPtr->nItemHeight;
7502 HIMAGELIST himlOld = 0;
7504 TRACE("(nType=%d, himl=%p\n", nType, himl);
7506 switch (nType)
7508 case LVSIL_NORMAL:
7509 himlOld = infoPtr->himlNormal;
7510 infoPtr->himlNormal = himl;
7511 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7512 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7513 break;
7515 case LVSIL_SMALL:
7516 himlOld = infoPtr->himlSmall;
7517 infoPtr->himlSmall = himl;
7518 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7519 break;
7521 case LVSIL_STATE:
7522 himlOld = infoPtr->himlState;
7523 infoPtr->himlState = himl;
7524 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7525 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7526 break;
7528 default:
7529 ERR("Unknown icon type=%d\n", nType);
7530 return NULL;
7533 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7534 if (infoPtr->nItemHeight != oldHeight)
7535 LISTVIEW_UpdateScroll(infoPtr);
7537 return himlOld;
7540 /***
7541 * DESCRIPTION:
7542 * Preallocates memory (does *not* set the actual count of items !)
7544 * PARAMETER(S):
7545 * [I] infoPtr : valid pointer to the listview structure
7546 * [I] nItems : item count (projected number of items to allocate)
7547 * [I] dwFlags : update flags
7549 * RETURN:
7550 * SUCCESS : TRUE
7551 * FAILURE : FALSE
7553 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7555 TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
7557 if (infoPtr->dwStyle & LVS_OWNERDATA)
7559 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7560 INT nOldCount = infoPtr->nItemCount;
7562 if (nItems < nOldCount)
7564 RANGE range = { nItems, nOldCount };
7565 ranges_del(infoPtr->selectionRanges, range);
7566 if (infoPtr->nFocusedItem >= nItems)
7568 LISTVIEW_SetItemFocus(infoPtr, -1);
7569 SetRectEmpty(&infoPtr->rcFocus);
7573 infoPtr->nItemCount = nItems;
7574 LISTVIEW_UpdateScroll(infoPtr);
7576 /* the flags are valid only in ownerdata report and list modes */
7577 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7579 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7580 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7582 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7583 LISTVIEW_InvalidateList(infoPtr);
7584 else
7586 INT nFrom, nTo;
7587 POINT Origin;
7588 RECT rcErase;
7590 LISTVIEW_GetOrigin(infoPtr, &Origin);
7591 nFrom = min(nOldCount, nItems);
7592 nTo = max(nOldCount, nItems);
7594 if (uView == LVS_REPORT)
7596 rcErase.left = 0;
7597 rcErase.top = nFrom * infoPtr->nItemHeight;
7598 rcErase.right = infoPtr->nItemWidth;
7599 rcErase.bottom = nTo * infoPtr->nItemHeight;
7600 OffsetRect(&rcErase, Origin.x, Origin.y);
7601 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7602 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7604 else /* LVS_LIST */
7606 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7608 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7609 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7610 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7611 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7612 OffsetRect(&rcErase, Origin.x, Origin.y);
7613 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7614 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7616 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7617 rcErase.top = 0;
7618 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7619 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7620 OffsetRect(&rcErase, Origin.x, Origin.y);
7621 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7622 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7626 else
7628 /* According to MSDN for non-LVS_OWNERDATA this is just
7629 * a performance issue. The control allocates its internal
7630 * data structures for the number of items specified. It
7631 * cuts down on the number of memory allocations. Therefore
7632 * we will just issue a WARN here
7634 WARN("for non-ownerdata performance option not implemented.\n");
7637 return TRUE;
7640 /***
7641 * DESCRIPTION:
7642 * Sets the position of an item.
7644 * PARAMETER(S):
7645 * [I] infoPtr : valid pointer to the listview structure
7646 * [I] nItem : item index
7647 * [I] pt : coordinate
7649 * RETURN:
7650 * SUCCESS : TRUE
7651 * FAILURE : FALSE
7653 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7655 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7656 POINT Origin;
7658 TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7660 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7661 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7663 LISTVIEW_GetOrigin(infoPtr, &Origin);
7665 /* This point value seems to be an undocumented feature.
7666 * The best guess is that it means either at the origin,
7667 * or at true beginning of the list. I will assume the origin. */
7668 if ((pt.x == -1) && (pt.y == -1))
7669 pt = Origin;
7671 if (uView == LVS_ICON)
7673 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7674 pt.y -= ICON_TOP_PADDING;
7676 pt.x -= Origin.x;
7677 pt.y -= Origin.y;
7679 infoPtr->bAutoarrange = FALSE;
7681 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7684 /***
7685 * DESCRIPTION:
7686 * Sets the state of one or many items.
7688 * PARAMETER(S):
7689 * [I] infoPtr : valid pointer to the listview structure
7690 * [I] nItem : item index
7691 * [I] lpLVItem : item or subitem info
7693 * RETURN:
7694 * SUCCESS : TRUE
7695 * FAILURE : FALSE
7697 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7699 BOOL bResult = TRUE;
7700 LVITEMW lvItem;
7702 lvItem.iItem = nItem;
7703 lvItem.iSubItem = 0;
7704 lvItem.mask = LVIF_STATE;
7705 lvItem.state = lpLVItem->state;
7706 lvItem.stateMask = lpLVItem->stateMask;
7707 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7709 if (nItem == -1)
7711 /* apply to all items */
7712 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7713 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7715 else
7716 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7719 * Update selection mark
7721 * Investigation on windows 2k showed that selection mark was updated
7722 * whenever a new selection was made, but if the selected item was
7723 * unselected it was not updated.
7725 * we are probably still not 100% accurate, but this at least sets the
7726 * proper selection mark when it is needed
7729 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7730 (infoPtr->nSelectionMark == -1))
7732 int i;
7733 for (i = 0; i < infoPtr->nItemCount; i++)
7735 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7737 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7739 infoPtr->nSelectionMark = i;
7740 break;
7743 else if (ranges_contain(infoPtr->selectionRanges, i))
7745 infoPtr->nSelectionMark = i;
7746 break;
7751 return bResult;
7754 /***
7755 * DESCRIPTION:
7756 * Sets the text of an item or subitem.
7758 * PARAMETER(S):
7759 * [I] hwnd : window handle
7760 * [I] nItem : item index
7761 * [I] lpLVItem : item or subitem info
7762 * [I] isW : TRUE if input is Unicode
7764 * RETURN:
7765 * SUCCESS : TRUE
7766 * FAILURE : FALSE
7768 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7770 LVITEMW lvItem;
7772 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7774 lvItem.iItem = nItem;
7775 lvItem.iSubItem = lpLVItem->iSubItem;
7776 lvItem.mask = LVIF_TEXT;
7777 lvItem.pszText = lpLVItem->pszText;
7778 lvItem.cchTextMax = lpLVItem->cchTextMax;
7780 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7782 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7785 /***
7786 * DESCRIPTION:
7787 * Set item index that marks the start of a multiple selection.
7789 * PARAMETER(S):
7790 * [I] infoPtr : valid pointer to the listview structure
7791 * [I] nIndex : index
7793 * RETURN:
7794 * Index number or -1 if there is no selection mark.
7796 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7798 INT nOldIndex = infoPtr->nSelectionMark;
7800 TRACE("(nIndex=%d)\n", nIndex);
7802 infoPtr->nSelectionMark = nIndex;
7804 return nOldIndex;
7807 /***
7808 * DESCRIPTION:
7809 * Sets the text background color.
7811 * PARAMETER(S):
7812 * [I] infoPtr : valid pointer to the listview structure
7813 * [I] clrTextBk : text background color
7815 * RETURN:
7816 * SUCCESS : TRUE
7817 * FAILURE : FALSE
7819 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7821 TRACE("(clrTextBk=%x)\n", clrTextBk);
7823 if (infoPtr->clrTextBk != clrTextBk)
7825 infoPtr->clrTextBk = clrTextBk;
7826 LISTVIEW_InvalidateList(infoPtr);
7829 return TRUE;
7832 /***
7833 * DESCRIPTION:
7834 * Sets the text foreground color.
7836 * PARAMETER(S):
7837 * [I] infoPtr : valid pointer to the listview structure
7838 * [I] clrText : text color
7840 * RETURN:
7841 * SUCCESS : TRUE
7842 * FAILURE : FALSE
7844 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7846 TRACE("(clrText=%x)\n", clrText);
7848 if (infoPtr->clrText != clrText)
7850 infoPtr->clrText = clrText;
7851 LISTVIEW_InvalidateList(infoPtr);
7854 return TRUE;
7857 /***
7858 * DESCRIPTION:
7859 * Determines which listview item is located at the specified position.
7861 * PARAMETER(S):
7862 * [I] infoPtr : valid pointer to the listview structure
7863 * [I] hwndNewToolTip : handle to new ToolTip
7865 * RETURN:
7866 * old tool tip
7868 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7870 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7871 infoPtr->hwndToolTip = hwndNewToolTip;
7872 return hwndOldToolTip;
7876 * DESCRIPTION:
7877 * sets the Unicode character format flag for the control
7878 * PARAMETER(S):
7879 * [I] infoPtr :valid pointer to the listview structure
7880 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
7882 * RETURN:
7883 * Old Unicode Format
7885 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL fUnicode)
7887 BOOL rc = infoPtr->notifyFormat;
7888 infoPtr->notifyFormat = (fUnicode)?NFR_UNICODE:NFR_ANSI;
7889 return rc;
7892 /* LISTVIEW_SetWorkAreas */
7894 /***
7895 * DESCRIPTION:
7896 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMS
7898 * PARAMETER(S):
7899 * [I] first : pointer to first ITEM_INFO to compare
7900 * [I] second : pointer to second ITEM_INFO to compare
7901 * [I] lParam : HWND of control
7903 * RETURN:
7904 * if first comes before second : negative
7905 * if first comes after second : positive
7906 * if first and second are equivalent : zero
7908 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7910 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7911 ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
7912 ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
7914 /* Forward the call to the client defined callback */
7915 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7918 /***
7919 * DESCRIPTION:
7920 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMSEX
7922 * PARAMETER(S):
7923 * [I] first : pointer to first ITEM_INFO to compare
7924 * [I] second : pointer to second ITEM_INFO to compare
7925 * [I] lParam : HWND of control
7927 * RETURN:
7928 * if first comes before second : negative
7929 * if first comes after second : positive
7930 * if first and second are equivalent : zero
7932 static INT WINAPI LISTVIEW_CallBackCompareEx(LPVOID first, LPVOID second, LPARAM lParam)
7934 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7935 INT first_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, first );
7936 INT second_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, second );
7938 /* Forward the call to the client defined callback */
7939 return (infoPtr->pfnCompare)( first_idx, second_idx, infoPtr->lParamSort );
7942 /***
7943 * DESCRIPTION:
7944 * Sorts the listview items.
7946 * PARAMETER(S):
7947 * [I] infoPtr : valid pointer to the listview structure
7948 * [I] pfnCompare : application-defined value
7949 * [I] lParamSort : pointer to comparison callback
7950 * [I] IsEx : TRUE when LVM_SORTITEMSEX used
7952 * RETURN:
7953 * SUCCESS : TRUE
7954 * FAILURE : FALSE
7956 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare,
7957 LPARAM lParamSort, BOOL IsEx)
7959 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7960 HDPA hdpaSubItems;
7961 ITEM_INFO *lpItem;
7962 LPVOID selectionMarkItem = NULL;
7963 LPVOID focusedItem = NULL;
7964 int i;
7966 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7968 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7970 if (!pfnCompare) return FALSE;
7971 if (!infoPtr->hdpaItems) return FALSE;
7973 /* if there are 0 or 1 items, there is no need to sort */
7974 if (infoPtr->nItemCount < 2) return TRUE;
7976 /* clear selection */
7977 ranges_clear(infoPtr->selectionRanges);
7979 /* save selection mark and focused item */
7980 if (infoPtr->nSelectionMark >= 0)
7981 selectionMarkItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark);
7982 if (infoPtr->nFocusedItem >= 0)
7983 focusedItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7985 infoPtr->pfnCompare = pfnCompare;
7986 infoPtr->lParamSort = lParamSort;
7987 if (IsEx)
7988 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompareEx, (LPARAM)infoPtr);
7989 else
7990 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7992 /* restore selection ranges */
7993 for (i=0; i < infoPtr->nItemCount; i++)
7995 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
7996 lpItem = DPA_GetPtr(hdpaSubItems, 0);
7998 if (lpItem->state & LVIS_SELECTED)
7999 ranges_additem(infoPtr->selectionRanges, i);
8001 /* restore selection mark and focused item */
8002 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
8003 infoPtr->nFocusedItem = DPA_GetPtrIndex(infoPtr->hdpaItems, focusedItem);
8005 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
8007 /* refresh the display */
8008 if (uView != LVS_ICON && uView != LVS_SMALLICON)
8009 LISTVIEW_InvalidateList(infoPtr);
8011 return TRUE;
8014 /***
8015 * DESCRIPTION:
8016 * Update theme handle after a theme change.
8018 * PARAMETER(S):
8019 * [I] infoPtr : valid pointer to the listview structure
8021 * RETURN:
8022 * SUCCESS : 0
8023 * FAILURE : something else
8025 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr)
8027 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8028 CloseThemeData(theme);
8029 OpenThemeData(infoPtr->hwndSelf, themeClass);
8030 return 0;
8033 /***
8034 * DESCRIPTION:
8035 * Updates an items or rearranges the listview control.
8037 * PARAMETER(S):
8038 * [I] infoPtr : valid pointer to the listview structure
8039 * [I] nItem : item index
8041 * RETURN:
8042 * SUCCESS : TRUE
8043 * FAILURE : FALSE
8045 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
8047 TRACE("(nItem=%d)\n", nItem);
8049 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
8051 /* rearrange with default alignment style */
8052 if (is_autoarrange(infoPtr))
8053 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8054 else
8055 LISTVIEW_InvalidateItem(infoPtr, nItem);
8057 return TRUE;
8060 /***
8061 * DESCRIPTION:
8062 * Draw the track line at the place defined in the infoPtr structure.
8063 * The line is drawn with a XOR pen so drawing the line for the second time
8064 * in the same place erases the line.
8066 * PARAMETER(S):
8067 * [I] infoPtr : valid pointer to the listview structure
8069 * RETURN:
8070 * SUCCESS : TRUE
8071 * FAILURE : FALSE
8073 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
8075 HPEN hOldPen;
8076 HDC hdc;
8077 INT oldROP;
8079 if (infoPtr->xTrackLine == -1)
8080 return FALSE;
8082 if (!(hdc = GetDC(infoPtr->hwndSelf)))
8083 return FALSE;
8084 hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
8085 oldROP = SetROP2(hdc, R2_XORPEN);
8086 MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
8087 LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
8088 SetROP2(hdc, oldROP);
8089 SelectObject(hdc, hOldPen);
8090 ReleaseDC(infoPtr->hwndSelf, hdc);
8091 return TRUE;
8094 /***
8095 * DESCRIPTION:
8096 * Called when an edit control should be displayed. This function is called after
8097 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
8099 * PARAMETER(S):
8100 * [I] hwnd : Handle to the listview
8101 * [I] uMsg : WM_TIMER (ignored)
8102 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
8103 * [I] dwTimer : The elapsed time (ignored)
8105 * RETURN:
8106 * None.
8108 static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
8110 DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
8111 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8113 KillTimer(hwnd, idEvent);
8114 editItem->fEnabled = FALSE;
8115 /* check if the item is still selected */
8116 if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
8117 LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
8120 /***
8121 * DESCRIPTION:
8122 * Creates the listview control - the WM_NCCREATE phase.
8124 * PARAMETER(S):
8125 * [I] hwnd : window handle
8126 * [I] lpcs : the create parameters
8128 * RETURN:
8129 * Success: TRUE
8130 * Failure: FALSE
8132 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
8134 LISTVIEW_INFO *infoPtr;
8135 LOGFONTW logFont;
8137 TRACE("(lpcs=%p)\n", lpcs);
8139 /* initialize info pointer */
8140 infoPtr = Alloc(sizeof(LISTVIEW_INFO));
8141 if (!infoPtr) return FALSE;
8143 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
8145 infoPtr->hwndSelf = hwnd;
8146 infoPtr->dwStyle = lpcs->style; /* Note: may be changed in WM_CREATE */
8147 /* determine the type of structures to use */
8148 infoPtr->hwndNotify = lpcs->hwndParent;
8149 /* infoPtr->notifyFormat will be filled in WM_CREATE */
8151 /* initialize color information */
8152 infoPtr->clrBk = CLR_NONE;
8153 infoPtr->clrText = CLR_DEFAULT;
8154 infoPtr->clrTextBk = CLR_DEFAULT;
8155 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
8157 /* set default values */
8158 infoPtr->nFocusedItem = -1;
8159 infoPtr->nSelectionMark = -1;
8160 infoPtr->nHotItem = -1;
8161 infoPtr->bRedraw = TRUE;
8162 infoPtr->bNoItemMetrics = TRUE;
8163 infoPtr->bDoChangeNotify = TRUE;
8164 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
8165 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
8166 infoPtr->nEditLabelItem = -1;
8167 infoPtr->dwHoverTime = -1; /* default system hover time */
8168 infoPtr->nMeasureItemHeight = 0;
8169 infoPtr->xTrackLine = -1; /* no track line */
8170 infoPtr->itemEdit.fEnabled = FALSE;
8171 infoPtr->iVersion = COMCTL32_VERSION;
8173 /* get default font (icon title) */
8174 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
8175 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
8176 infoPtr->hFont = infoPtr->hDefaultFont;
8177 LISTVIEW_SaveTextMetrics(infoPtr);
8179 /* allocate memory for the data structure */
8180 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
8181 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
8182 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
8183 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
8184 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
8185 return TRUE;
8187 fail:
8188 DestroyWindow(infoPtr->hwndHeader);
8189 ranges_destroy(infoPtr->selectionRanges);
8190 DPA_Destroy(infoPtr->hdpaItems);
8191 DPA_Destroy(infoPtr->hdpaPosX);
8192 DPA_Destroy(infoPtr->hdpaPosY);
8193 DPA_Destroy(infoPtr->hdpaColumns);
8194 Free(infoPtr);
8195 return FALSE;
8198 /***
8199 * DESCRIPTION:
8200 * Creates the listview control - the WM_CREATE phase. Most of the data is
8201 * already set up in LISTVIEW_NCCreate
8203 * PARAMETER(S):
8204 * [I] hwnd : window handle
8205 * [I] lpcs : the create parameters
8207 * RETURN:
8208 * Success: 0
8209 * Failure: -1
8211 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
8213 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8214 UINT uView = lpcs->style & LVS_TYPEMASK;
8216 TRACE("(lpcs=%p)\n", lpcs);
8218 infoPtr->dwStyle = lpcs->style;
8219 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
8220 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
8222 if ((uView == LVS_REPORT) && (lpcs->style & WS_VISIBLE))
8224 if (LISTVIEW_CreateHeader(infoPtr) < 0) return -1;
8226 else
8227 infoPtr->hwndHeader = 0;
8229 /* init item size to avoid division by 0 */
8230 LISTVIEW_UpdateItemSize (infoPtr);
8232 if (uView == LVS_REPORT)
8234 if (!(LVS_NOCOLUMNHEADER & lpcs->style) && (WS_VISIBLE & lpcs->style))
8236 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8238 LISTVIEW_UpdateScroll(infoPtr);
8241 OpenThemeData(hwnd, themeClass);
8243 /* initialize the icon sizes */
8244 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
8245 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
8246 return 0;
8249 /***
8250 * DESCRIPTION:
8251 * Destroys the listview control.
8253 * PARAMETER(S):
8254 * [I] infoPtr : valid pointer to the listview structure
8256 * RETURN:
8257 * Success: 0
8258 * Failure: -1
8260 static LRESULT LISTVIEW_Destroy(const LISTVIEW_INFO *infoPtr)
8262 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8263 CloseThemeData(theme);
8264 return 0;
8267 /***
8268 * DESCRIPTION:
8269 * Enables the listview control.
8271 * PARAMETER(S):
8272 * [I] infoPtr : valid pointer to the listview structure
8273 * [I] bEnable : specifies whether to enable or disable the window
8275 * RETURN:
8276 * SUCCESS : TRUE
8277 * FAILURE : FALSE
8279 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr, BOOL bEnable)
8281 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
8282 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8283 return TRUE;
8286 /***
8287 * DESCRIPTION:
8288 * Erases the background of the listview control.
8290 * PARAMETER(S):
8291 * [I] infoPtr : valid pointer to the listview structure
8292 * [I] hdc : device context handle
8294 * RETURN:
8295 * SUCCESS : TRUE
8296 * FAILURE : FALSE
8298 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
8300 RECT rc;
8302 TRACE("(hdc=%p)\n", hdc);
8304 if (!GetClipBox(hdc, &rc)) return FALSE;
8306 /* for double buffered controls we need to do this during refresh */
8307 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
8309 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
8313 /***
8314 * DESCRIPTION:
8315 * Helper function for LISTVIEW_[HV]Scroll *only*.
8316 * Performs vertical/horizontal scrolling by a give amount.
8318 * PARAMETER(S):
8319 * [I] infoPtr : valid pointer to the listview structure
8320 * [I] dx : amount of horizontal scroll
8321 * [I] dy : amount of vertical scroll
8323 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
8325 /* now we can scroll the list */
8326 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
8327 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
8328 /* if we have focus, adjust rect */
8329 OffsetRect(&infoPtr->rcFocus, dx, dy);
8330 UpdateWindow(infoPtr->hwndSelf);
8333 /***
8334 * DESCRIPTION:
8335 * Performs vertical scrolling.
8337 * PARAMETER(S):
8338 * [I] infoPtr : valid pointer to the listview structure
8339 * [I] nScrollCode : scroll code
8340 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8341 * [I] hScrollWnd : scrollbar control window handle
8343 * RETURN:
8344 * Zero
8346 * NOTES:
8347 * SB_LINEUP/SB_LINEDOWN:
8348 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8349 * for LVS_REPORT is 1 line
8350 * for LVS_LIST cannot occur
8353 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8354 INT nScrollDiff, HWND hScrollWnd)
8356 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8357 INT nOldScrollPos, nNewScrollPos;
8358 SCROLLINFO scrollInfo;
8359 BOOL is_an_icon;
8361 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8362 debugscrollcode(nScrollCode), nScrollDiff);
8364 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8366 scrollInfo.cbSize = sizeof(SCROLLINFO);
8367 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8369 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
8371 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
8373 nOldScrollPos = scrollInfo.nPos;
8374 switch (nScrollCode)
8376 case SB_INTERNAL:
8377 break;
8379 case SB_LINEUP:
8380 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
8381 break;
8383 case SB_LINEDOWN:
8384 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
8385 break;
8387 case SB_PAGEUP:
8388 nScrollDiff = -scrollInfo.nPage;
8389 break;
8391 case SB_PAGEDOWN:
8392 nScrollDiff = scrollInfo.nPage;
8393 break;
8395 case SB_THUMBPOSITION:
8396 case SB_THUMBTRACK:
8397 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8398 break;
8400 default:
8401 nScrollDiff = 0;
8404 /* quit right away if pos isn't changing */
8405 if (nScrollDiff == 0) return 0;
8407 /* calculate new position, and handle overflows */
8408 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8409 if (nScrollDiff > 0) {
8410 if (nNewScrollPos < nOldScrollPos ||
8411 nNewScrollPos > scrollInfo.nMax)
8412 nNewScrollPos = scrollInfo.nMax;
8413 } else {
8414 if (nNewScrollPos > nOldScrollPos ||
8415 nNewScrollPos < scrollInfo.nMin)
8416 nNewScrollPos = scrollInfo.nMin;
8419 /* set the new position, and reread in case it changed */
8420 scrollInfo.fMask = SIF_POS;
8421 scrollInfo.nPos = nNewScrollPos;
8422 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
8424 /* carry on only if it really changed */
8425 if (nNewScrollPos == nOldScrollPos) return 0;
8427 /* now adjust to client coordinates */
8428 nScrollDiff = nOldScrollPos - nNewScrollPos;
8429 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
8431 /* and scroll the window */
8432 scroll_list(infoPtr, 0, nScrollDiff);
8434 return 0;
8437 /***
8438 * DESCRIPTION:
8439 * Performs horizontal scrolling.
8441 * PARAMETER(S):
8442 * [I] infoPtr : valid pointer to the listview structure
8443 * [I] nScrollCode : scroll code
8444 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8445 * [I] hScrollWnd : scrollbar control window handle
8447 * RETURN:
8448 * Zero
8450 * NOTES:
8451 * SB_LINELEFT/SB_LINERIGHT:
8452 * for LVS_ICON, LVS_SMALLICON 1 pixel
8453 * for LVS_REPORT is 1 pixel
8454 * for LVS_LIST is 1 column --> which is a 1 because the
8455 * scroll is based on columns not pixels
8458 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8459 INT nScrollDiff, HWND hScrollWnd)
8461 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8462 INT nOldScrollPos, nNewScrollPos;
8463 SCROLLINFO scrollInfo;
8465 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8466 debugscrollcode(nScrollCode), nScrollDiff);
8468 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8470 scrollInfo.cbSize = sizeof(SCROLLINFO);
8471 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8473 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
8475 nOldScrollPos = scrollInfo.nPos;
8477 switch (nScrollCode)
8479 case SB_INTERNAL:
8480 break;
8482 case SB_LINELEFT:
8483 nScrollDiff = -1;
8484 break;
8486 case SB_LINERIGHT:
8487 nScrollDiff = 1;
8488 break;
8490 case SB_PAGELEFT:
8491 nScrollDiff = -scrollInfo.nPage;
8492 break;
8494 case SB_PAGERIGHT:
8495 nScrollDiff = scrollInfo.nPage;
8496 break;
8498 case SB_THUMBPOSITION:
8499 case SB_THUMBTRACK:
8500 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8501 break;
8503 default:
8504 nScrollDiff = 0;
8507 /* quit right away if pos isn't changing */
8508 if (nScrollDiff == 0) return 0;
8510 /* calculate new position, and handle overflows */
8511 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8512 if (nScrollDiff > 0) {
8513 if (nNewScrollPos < nOldScrollPos ||
8514 nNewScrollPos > scrollInfo.nMax)
8515 nNewScrollPos = scrollInfo.nMax;
8516 } else {
8517 if (nNewScrollPos > nOldScrollPos ||
8518 nNewScrollPos < scrollInfo.nMin)
8519 nNewScrollPos = scrollInfo.nMin;
8522 /* set the new position, and reread in case it changed */
8523 scrollInfo.fMask = SIF_POS;
8524 scrollInfo.nPos = nNewScrollPos;
8525 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
8527 /* carry on only if it really changed */
8528 if (nNewScrollPos == nOldScrollPos) return 0;
8530 if(uView == LVS_REPORT)
8531 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
8533 /* now adjust to client coordinates */
8534 nScrollDiff = nOldScrollPos - nNewScrollPos;
8535 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
8537 /* and scroll the window */
8538 scroll_list(infoPtr, nScrollDiff, 0);
8540 return 0;
8543 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
8545 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8546 INT gcWheelDelta = 0;
8547 INT pulScrollLines = 3;
8548 SCROLLINFO scrollInfo;
8550 TRACE("(wheelDelta=%d)\n", wheelDelta);
8552 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8553 gcWheelDelta -= wheelDelta;
8555 scrollInfo.cbSize = sizeof(SCROLLINFO);
8556 scrollInfo.fMask = SIF_POS;
8558 switch(uView)
8560 case LVS_ICON:
8561 case LVS_SMALLICON:
8563 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8564 * should be fixed in the future.
8566 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
8567 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8568 break;
8570 case LVS_REPORT:
8571 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8573 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
8574 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8575 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
8577 break;
8579 case LVS_LIST:
8580 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8581 break;
8583 return 0;
8586 /***
8587 * DESCRIPTION:
8588 * ???
8590 * PARAMETER(S):
8591 * [I] infoPtr : valid pointer to the listview structure
8592 * [I] nVirtualKey : virtual key
8593 * [I] lKeyData : key data
8595 * RETURN:
8596 * Zero
8598 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8600 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8601 HWND hwndSelf = infoPtr->hwndSelf;
8602 INT nItem = -1;
8603 NMLVKEYDOWN nmKeyDown;
8605 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
8607 /* send LVN_KEYDOWN notification */
8608 nmKeyDown.wVKey = nVirtualKey;
8609 nmKeyDown.flags = 0;
8610 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8611 if (!IsWindow(hwndSelf))
8612 return 0;
8614 switch (nVirtualKey)
8616 case VK_SPACE:
8617 nItem = infoPtr->nFocusedItem;
8618 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
8619 toggle_checkbox_state(infoPtr, infoPtr->nFocusedItem);
8620 break;
8622 case VK_RETURN:
8623 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8625 if (!notify(infoPtr, NM_RETURN)) return 0;
8626 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8628 break;
8630 case VK_HOME:
8631 if (infoPtr->nItemCount > 0)
8632 nItem = 0;
8633 break;
8635 case VK_END:
8636 if (infoPtr->nItemCount > 0)
8637 nItem = infoPtr->nItemCount - 1;
8638 break;
8640 case VK_LEFT:
8641 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8642 break;
8644 case VK_UP:
8645 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8646 break;
8648 case VK_RIGHT:
8649 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8650 break;
8652 case VK_DOWN:
8653 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8654 break;
8656 case VK_PRIOR:
8657 if (uView == LVS_REPORT)
8659 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8660 if (infoPtr->nFocusedItem == topidx)
8661 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8662 else
8663 nItem = topidx;
8665 else
8666 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8667 * LISTVIEW_GetCountPerRow(infoPtr);
8668 if(nItem < 0) nItem = 0;
8669 break;
8671 case VK_NEXT:
8672 if (uView == LVS_REPORT)
8674 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8675 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8676 if (infoPtr->nFocusedItem == topidx + cnt - 1)
8677 nItem = infoPtr->nFocusedItem + cnt - 1;
8678 else
8679 nItem = topidx + cnt - 1;
8681 else
8682 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8683 * LISTVIEW_GetCountPerRow(infoPtr);
8684 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8685 break;
8688 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8689 LISTVIEW_KeySelection(infoPtr, nItem, nVirtualKey == VK_SPACE);
8691 return 0;
8694 /***
8695 * DESCRIPTION:
8696 * Kills the focus.
8698 * PARAMETER(S):
8699 * [I] infoPtr : valid pointer to the listview structure
8701 * RETURN:
8702 * Zero
8704 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8706 TRACE("()\n");
8708 /* if we did not have the focus, there's nothing to do */
8709 if (!infoPtr->bFocus) return 0;
8711 /* send NM_KILLFOCUS notification */
8712 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8714 /* if we have a focus rectangle, get rid of it */
8715 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8717 /* set window focus flag */
8718 infoPtr->bFocus = FALSE;
8720 /* invalidate the selected items before resetting focus flag */
8721 LISTVIEW_InvalidateSelectedItems(infoPtr);
8723 return 0;
8726 /***
8727 * DESCRIPTION:
8728 * Processes double click messages (left mouse button).
8730 * PARAMETER(S):
8731 * [I] infoPtr : valid pointer to the listview structure
8732 * [I] wKey : key flag
8733 * [I] x,y : mouse coordinate
8735 * RETURN:
8736 * Zero
8738 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8740 LVHITTESTINFO htInfo;
8742 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8744 /* Cancel the item edition if any */
8745 if (infoPtr->itemEdit.fEnabled)
8747 KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
8748 infoPtr->itemEdit.fEnabled = FALSE;
8751 /* send NM_RELEASEDCAPTURE notification */
8752 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8754 htInfo.pt.x = x;
8755 htInfo.pt.y = y;
8757 /* send NM_DBLCLK notification */
8758 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8759 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8761 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8762 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8764 return 0;
8767 /***
8768 * DESCRIPTION:
8769 * Processes mouse down messages (left mouse button).
8771 * PARAMETERS:
8772 * infoPtr [I ] valid pointer to the listview structure
8773 * wKey [I ] key flag
8774 * x,y [I ] mouse coordinate
8776 * RETURN:
8777 * Zero
8779 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8781 LVHITTESTINFO lvHitTestInfo;
8782 static BOOL bGroupSelect = TRUE;
8783 POINT pt = { x, y };
8784 INT nItem;
8786 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8788 /* send NM_RELEASEDCAPTURE notification */
8789 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8791 /* set left button down flag and record the click position */
8792 infoPtr->bLButtonDown = TRUE;
8793 infoPtr->ptClickPos = pt;
8794 infoPtr->bDragging = FALSE;
8796 lvHitTestInfo.pt.x = x;
8797 lvHitTestInfo.pt.y = y;
8799 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8800 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
8801 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8803 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8805 toggle_checkbox_state(infoPtr, nItem);
8806 return 0;
8809 if (infoPtr->dwStyle & LVS_SINGLESEL)
8811 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8812 infoPtr->nEditLabelItem = nItem;
8813 else
8814 LISTVIEW_SetSelection(infoPtr, nItem);
8816 else
8818 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8820 if (bGroupSelect)
8822 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8823 LISTVIEW_SetItemFocus(infoPtr, nItem);
8824 infoPtr->nSelectionMark = nItem;
8826 else
8828 LVITEMW item;
8830 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8831 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8833 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8834 infoPtr->nSelectionMark = nItem;
8837 else if (wKey & MK_CONTROL)
8839 LVITEMW item;
8841 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8843 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8844 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8845 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8846 infoPtr->nSelectionMark = nItem;
8848 else if (wKey & MK_SHIFT)
8850 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8852 else
8854 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8855 infoPtr->nEditLabelItem = nItem;
8857 /* set selection (clears other pre-existing selections) */
8858 LISTVIEW_SetSelection(infoPtr, nItem);
8862 if (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE)
8863 if(lvHitTestInfo.iItem != -1) notify_itemactivate(infoPtr,&lvHitTestInfo);
8865 else
8867 /* remove all selections */
8868 LISTVIEW_DeselectAll(infoPtr);
8869 ReleaseCapture();
8872 return 0;
8875 /***
8876 * DESCRIPTION:
8877 * Processes mouse up messages (left mouse button).
8879 * PARAMETERS:
8880 * infoPtr [I ] valid pointer to the listview structure
8881 * wKey [I ] key flag
8882 * x,y [I ] mouse coordinate
8884 * RETURN:
8885 * Zero
8887 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8889 LVHITTESTINFO lvHitTestInfo;
8891 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8893 if (!infoPtr->bLButtonDown) return 0;
8895 lvHitTestInfo.pt.x = x;
8896 lvHitTestInfo.pt.y = y;
8898 /* send NM_CLICK notification */
8899 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8900 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8902 /* set left button flag */
8903 infoPtr->bLButtonDown = FALSE;
8905 if (infoPtr->bDragging)
8907 infoPtr->bDragging = FALSE;
8908 return 0;
8911 /* if we clicked on a selected item, edit the label */
8912 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8914 /* we want to make sure the user doesn't want to do a double click. So we will
8915 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
8917 infoPtr->itemEdit.fEnabled = TRUE;
8918 infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
8919 SetTimer(infoPtr->hwndSelf,
8920 (UINT_PTR)&infoPtr->itemEdit,
8921 GetDoubleClickTime(),
8922 LISTVIEW_DelayedEditItem);
8925 if (!infoPtr->bFocus)
8926 SetFocus(infoPtr->hwndSelf);
8928 return 0;
8931 /***
8932 * DESCRIPTION:
8933 * Destroys the listview control (called after WM_DESTROY).
8935 * PARAMETER(S):
8936 * [I] infoPtr : valid pointer to the listview structure
8938 * RETURN:
8939 * Zero
8941 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8943 TRACE("()\n");
8945 /* delete all items */
8946 LISTVIEW_DeleteAllItems(infoPtr, TRUE);
8948 /* destroy data structure */
8949 DPA_Destroy(infoPtr->hdpaItems);
8950 DPA_Destroy(infoPtr->hdpaPosX);
8951 DPA_Destroy(infoPtr->hdpaPosY);
8952 DPA_Destroy(infoPtr->hdpaColumns);
8953 ranges_destroy(infoPtr->selectionRanges);
8955 /* destroy image lists */
8956 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8958 if (infoPtr->himlNormal)
8959 ImageList_Destroy(infoPtr->himlNormal);
8960 if (infoPtr->himlSmall)
8961 ImageList_Destroy(infoPtr->himlSmall);
8962 if (infoPtr->himlState)
8963 ImageList_Destroy(infoPtr->himlState);
8966 /* destroy font, bkgnd brush */
8967 infoPtr->hFont = 0;
8968 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8969 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8971 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8973 /* free listview info pointer*/
8974 Free(infoPtr);
8976 return 0;
8979 /***
8980 * DESCRIPTION:
8981 * Handles notifications from header.
8983 * PARAMETER(S):
8984 * [I] infoPtr : valid pointer to the listview structure
8985 * [I] nCtrlId : control identifier
8986 * [I] lpnmh : notification information
8988 * RETURN:
8989 * Zero
8991 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8993 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8994 HWND hwndSelf = infoPtr->hwndSelf;
8996 TRACE("(lpnmh=%p)\n", lpnmh);
8998 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
9000 switch (lpnmh->hdr.code)
9002 case HDN_TRACKW:
9003 case HDN_TRACKA:
9005 COLUMN_INFO *lpColumnInfo;
9006 POINT ptOrigin;
9007 INT x;
9009 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
9010 break;
9012 /* remove the old line (if any) */
9013 LISTVIEW_DrawTrackLine(infoPtr);
9015 /* compute & draw the new line */
9016 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
9017 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
9018 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
9019 infoPtr->xTrackLine = x + ptOrigin.x;
9020 LISTVIEW_DrawTrackLine(infoPtr);
9021 break;
9024 case HDN_ENDTRACKA:
9025 case HDN_ENDTRACKW:
9026 /* remove the track line (if any) */
9027 LISTVIEW_DrawTrackLine(infoPtr);
9028 infoPtr->xTrackLine = -1;
9029 break;
9031 case HDN_ENDDRAG:
9032 FIXME("Changing column order not implemented\n");
9033 return TRUE;
9035 case HDN_ITEMCHANGINGW:
9036 case HDN_ITEMCHANGINGA:
9037 return notify_forward_header(infoPtr, lpnmh);
9039 case HDN_ITEMCHANGEDW:
9040 case HDN_ITEMCHANGEDA:
9042 COLUMN_INFO *lpColumnInfo;
9043 INT dx, cxy;
9045 notify_forward_header(infoPtr, lpnmh);
9046 if (!IsWindow(hwndSelf))
9047 break;
9049 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
9051 HDITEMW hdi;
9053 hdi.mask = HDI_WIDTH;
9054 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, &hdi)) return 0;
9055 cxy = hdi.cxy;
9057 else
9058 cxy = lpnmh->pitem->cxy;
9060 /* determine how much we change since the last know position */
9061 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
9062 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
9063 if (dx != 0)
9065 lpColumnInfo->rcHeader.right += dx;
9066 if (lpnmh->iItem + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
9067 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
9068 else
9070 /* only needs to update the scrolls */
9071 infoPtr->nItemWidth += dx;
9072 LISTVIEW_UpdateScroll(infoPtr);
9074 LISTVIEW_UpdateItemSize(infoPtr);
9075 if (uView == LVS_REPORT && is_redrawing(infoPtr))
9077 POINT ptOrigin;
9078 RECT rcCol = lpColumnInfo->rcHeader;
9080 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
9081 OffsetRect(&rcCol, ptOrigin.x, 0);
9083 rcCol.top = infoPtr->rcList.top;
9084 rcCol.bottom = infoPtr->rcList.bottom;
9086 /* resizing left-aligned columns leaves most of the left side untouched */
9087 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
9089 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
9090 if (dx > 0)
9091 nMaxDirty += dx;
9092 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
9095 /* when shrinking the last column clear the now unused field */
9096 if (lpnmh->iItem == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
9098 RECT right;
9100 rcCol.right -= dx;
9102 /* deal with right from rightmost column area */
9103 right.left = rcCol.right;
9104 right.top = rcCol.top;
9105 right.bottom = rcCol.bottom;
9106 right.right = infoPtr->rcList.right;
9108 LISTVIEW_InvalidateRect(infoPtr, &right);
9111 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
9115 break;
9117 case HDN_ITEMCLICKW:
9118 case HDN_ITEMCLICKA:
9120 /* Handle sorting by Header Column */
9121 NMLISTVIEW nmlv;
9123 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
9124 nmlv.iItem = -1;
9125 nmlv.iSubItem = lpnmh->iItem;
9126 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
9127 notify_forward_header(infoPtr, lpnmh);
9129 break;
9131 case HDN_DIVIDERDBLCLICKW:
9132 case HDN_DIVIDERDBLCLICKA:
9133 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
9134 break;
9137 return 0;
9140 /***
9141 * DESCRIPTION:
9142 * Paint non-client area of control.
9144 * PARAMETER(S):
9145 * [I] infoPtr : valid pointer to the listview structureof the sender
9146 * [I] region : update region
9148 * RETURN:
9149 * TRUE - frame was painted
9150 * FALSE - call default window proc
9152 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
9154 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
9155 HDC dc;
9156 RECT r;
9157 HRGN cliprgn;
9158 int cxEdge = GetSystemMetrics (SM_CXEDGE),
9159 cyEdge = GetSystemMetrics (SM_CYEDGE);
9161 if (!theme) return FALSE;
9163 GetWindowRect(infoPtr->hwndSelf, &r);
9165 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
9166 r.right - cxEdge, r.bottom - cyEdge);
9167 if (region != (HRGN)1)
9168 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
9169 OffsetRect(&r, -r.left, -r.top);
9171 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
9172 OffsetRect(&r, -r.left, -r.top);
9174 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
9175 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
9176 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
9177 ReleaseDC(infoPtr->hwndSelf, dc);
9179 /* Call default proc to get the scrollbars etc. painted */
9180 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
9182 return TRUE;
9185 /***
9186 * DESCRIPTION:
9187 * Determines the type of structure to use.
9189 * PARAMETER(S):
9190 * [I] infoPtr : valid pointer to the listview structureof the sender
9191 * [I] hwndFrom : listview window handle
9192 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
9194 * RETURN:
9195 * Zero
9197 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
9199 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
9201 if (nCommand == NF_REQUERY)
9202 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
9204 return infoPtr->notifyFormat;
9207 /***
9208 * DESCRIPTION:
9209 * Paints/Repaints the listview control.
9211 * PARAMETER(S):
9212 * [I] infoPtr : valid pointer to the listview structure
9213 * [I] hdc : device context handle
9215 * RETURN:
9216 * Zero
9218 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
9220 TRACE("(hdc=%p)\n", hdc);
9222 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
9224 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9226 infoPtr->bNoItemMetrics = FALSE;
9227 LISTVIEW_UpdateItemSize(infoPtr);
9228 if (uView == LVS_ICON || uView == LVS_SMALLICON)
9229 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9230 LISTVIEW_UpdateScroll(infoPtr);
9233 if (infoPtr->hwndHeader) UpdateWindow(infoPtr->hwndHeader);
9235 if (hdc)
9236 LISTVIEW_Refresh(infoPtr, hdc, NULL);
9237 else
9239 PAINTSTRUCT ps;
9241 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
9242 if (!hdc) return 1;
9243 LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
9244 EndPaint(infoPtr->hwndSelf, &ps);
9247 return 0;
9251 /***
9252 * DESCRIPTION:
9253 * Paints/Repaints the listview control.
9255 * PARAMETER(S):
9256 * [I] infoPtr : valid pointer to the listview structure
9257 * [I] hdc : device context handle
9258 * [I] options : drawing options
9260 * RETURN:
9261 * Zero
9263 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
9265 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
9267 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
9268 return 0;
9270 if (options & PRF_ERASEBKGND)
9271 LISTVIEW_EraseBkgnd(infoPtr, hdc);
9273 if (options & PRF_CLIENT)
9274 LISTVIEW_Paint(infoPtr, hdc);
9276 return 0;
9280 /***
9281 * DESCRIPTION:
9282 * Processes double click messages (right mouse button).
9284 * PARAMETER(S):
9285 * [I] infoPtr : valid pointer to the listview structure
9286 * [I] wKey : key flag
9287 * [I] x,y : mouse coordinate
9289 * RETURN:
9290 * Zero
9292 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9294 LVHITTESTINFO lvHitTestInfo;
9296 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9298 /* send NM_RELEASEDCAPTURE notification */
9299 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9301 /* send NM_RDBLCLK notification */
9302 lvHitTestInfo.pt.x = x;
9303 lvHitTestInfo.pt.y = y;
9304 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9305 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
9307 return 0;
9310 /***
9311 * DESCRIPTION:
9312 * Processes mouse down messages (right mouse button).
9314 * PARAMETER(S):
9315 * [I] infoPtr : valid pointer to the listview structure
9316 * [I] wKey : key flag
9317 * [I] x,y : mouse coordinate
9319 * RETURN:
9320 * Zero
9322 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9324 LVHITTESTINFO lvHitTestInfo;
9325 INT nItem;
9327 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9329 /* send NM_RELEASEDCAPTURE notification */
9330 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9332 /* make sure the listview control window has the focus */
9333 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
9335 /* set right button down flag */
9336 infoPtr->bRButtonDown = TRUE;
9338 /* determine the index of the selected item */
9339 lvHitTestInfo.pt.x = x;
9340 lvHitTestInfo.pt.y = y;
9341 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
9343 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9345 LISTVIEW_SetItemFocus(infoPtr, nItem);
9346 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9347 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9348 LISTVIEW_SetSelection(infoPtr, nItem);
9350 else
9352 LISTVIEW_DeselectAll(infoPtr);
9355 return 0;
9358 /***
9359 * DESCRIPTION:
9360 * Processes mouse up messages (right mouse button).
9362 * PARAMETER(S):
9363 * [I] infoPtr : valid pointer to the listview structure
9364 * [I] wKey : key flag
9365 * [I] x,y : mouse coordinate
9367 * RETURN:
9368 * Zero
9370 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9372 LVHITTESTINFO lvHitTestInfo;
9373 POINT pt;
9375 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9377 if (!infoPtr->bRButtonDown) return 0;
9379 /* set button flag */
9380 infoPtr->bRButtonDown = FALSE;
9382 /* Send NM_RCLICK notification */
9383 lvHitTestInfo.pt.x = x;
9384 lvHitTestInfo.pt.y = y;
9385 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9386 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
9388 /* Change to screen coordinate for WM_CONTEXTMENU */
9389 pt = lvHitTestInfo.pt;
9390 ClientToScreen(infoPtr->hwndSelf, &pt);
9392 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9393 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
9394 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
9396 return 0;
9400 /***
9401 * DESCRIPTION:
9402 * Sets the cursor.
9404 * PARAMETER(S):
9405 * [I] infoPtr : valid pointer to the listview structure
9406 * [I] hwnd : window handle of window containing the cursor
9407 * [I] nHittest : hit-test code
9408 * [I] wMouseMsg : ideintifier of the mouse message
9410 * RETURN:
9411 * TRUE if cursor is set
9412 * FALSE otherwise
9414 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
9416 LVHITTESTINFO lvHitTestInfo;
9418 if(!(LISTVIEW_isHotTracking(infoPtr))) return FALSE;
9420 if(!infoPtr->hHotCursor) return FALSE;
9422 GetCursorPos(&lvHitTestInfo.pt);
9423 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
9425 SetCursor(infoPtr->hHotCursor);
9427 return TRUE;
9430 /***
9431 * DESCRIPTION:
9432 * Sets the focus.
9434 * PARAMETER(S):
9435 * [I] infoPtr : valid pointer to the listview structure
9436 * [I] hwndLoseFocus : handle of previously focused window
9438 * RETURN:
9439 * Zero
9441 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
9443 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
9445 /* if we have the focus already, there's nothing to do */
9446 if (infoPtr->bFocus) return 0;
9448 /* send NM_SETFOCUS notification */
9449 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
9451 /* set window focus flag */
9452 infoPtr->bFocus = TRUE;
9454 /* put the focus rect back on */
9455 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
9457 /* redraw all visible selected items */
9458 LISTVIEW_InvalidateSelectedItems(infoPtr);
9460 return 0;
9463 /***
9464 * DESCRIPTION:
9465 * Sets the font.
9467 * PARAMETER(S):
9468 * [I] infoPtr : valid pointer to the listview structure
9469 * [I] fRedraw : font handle
9470 * [I] fRedraw : redraw flag
9472 * RETURN:
9473 * Zero
9475 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
9477 HFONT oldFont = infoPtr->hFont;
9479 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
9481 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9482 if (infoPtr->hFont == oldFont) return 0;
9484 LISTVIEW_SaveTextMetrics(infoPtr);
9486 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
9488 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
9489 LISTVIEW_UpdateSize(infoPtr);
9490 LISTVIEW_UpdateScroll(infoPtr);
9493 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
9495 return 0;
9498 /***
9499 * DESCRIPTION:
9500 * Message handling for WM_SETREDRAW.
9501 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9503 * PARAMETER(S):
9504 * [I] infoPtr : valid pointer to the listview structure
9505 * [I] bRedraw: state of redraw flag
9507 * RETURN:
9508 * DefWinProc return value
9510 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
9512 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
9514 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9515 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
9517 infoPtr->bRedraw = bRedraw;
9519 if(!bRedraw) return 0;
9521 if (is_autoarrange(infoPtr))
9522 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9523 LISTVIEW_UpdateScroll(infoPtr);
9525 /* despite what the WM_SETREDRAW docs says, apps expect us
9526 * to invalidate the listview here... stupid! */
9527 LISTVIEW_InvalidateList(infoPtr);
9529 return 0;
9532 /***
9533 * DESCRIPTION:
9534 * Resizes the listview control. This function processes WM_SIZE
9535 * messages. At this time, the width and height are not used.
9537 * PARAMETER(S):
9538 * [I] infoPtr : valid pointer to the listview structure
9539 * [I] Width : new width
9540 * [I] Height : new height
9542 * RETURN:
9543 * Zero
9545 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
9547 RECT rcOld = infoPtr->rcList;
9549 TRACE("(width=%d, height=%d)\n", Width, Height);
9551 LISTVIEW_UpdateSize(infoPtr);
9552 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
9554 /* do not bother with display related stuff if we're not redrawing */
9555 if (!is_redrawing(infoPtr)) return 0;
9557 if (is_autoarrange(infoPtr))
9558 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9560 LISTVIEW_UpdateScroll(infoPtr);
9562 /* refresh all only for lists whose height changed significantly */
9563 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
9564 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
9565 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
9566 LISTVIEW_InvalidateList(infoPtr);
9568 return 0;
9571 /***
9572 * DESCRIPTION:
9573 * Sets the size information.
9575 * PARAMETER(S):
9576 * [I] infoPtr : valid pointer to the listview structure
9578 * RETURN:
9579 * None
9581 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
9583 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9585 TRACE("uView=%d, rcList(old)=%s\n", uView, wine_dbgstr_rect(&infoPtr->rcList));
9587 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
9589 if (uView == LVS_LIST)
9591 /* Apparently the "LIST" style is supposed to have the same
9592 * number of items in a column even if there is no scroll bar.
9593 * Since if a scroll bar already exists then the bottom is already
9594 * reduced, only reduce if the scroll bar does not currently exist.
9595 * The "2" is there to mimic the native control. I think it may be
9596 * related to either padding or edges. (GLA 7/2002)
9598 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
9599 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
9600 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
9602 else if (uView == LVS_REPORT)
9604 HDLAYOUT hl;
9605 WINDOWPOS wp;
9607 hl.prc = &infoPtr->rcList;
9608 hl.pwpos = &wp;
9609 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9610 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp.flags, wp.x, wp.y, wp.cx, wp.cy);
9611 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy,
9612 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9613 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9614 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy);
9616 infoPtr->rcList.top = max(wp.cy, 0);
9617 infoPtr->rcList.top += (infoPtr->dwLvExStyle & LVS_EX_GRIDLINES) ? 2 : 0;
9620 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
9623 /***
9624 * DESCRIPTION:
9625 * Processes WM_STYLECHANGED messages.
9627 * PARAMETER(S):
9628 * [I] infoPtr : valid pointer to the listview structure
9629 * [I] wStyleType : window style type (normal or extended)
9630 * [I] lpss : window style information
9632 * RETURN:
9633 * Zero
9635 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9636 const STYLESTRUCT *lpss)
9638 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9639 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9640 UINT style;
9642 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9643 wStyleType, lpss->styleOld, lpss->styleNew);
9645 if (wStyleType != GWL_STYLE) return 0;
9647 /* FIXME: if LVS_NOSORTHEADER changed, update header */
9648 /* or LVS_SORT{AS,DES}CENDING */
9650 infoPtr->dwStyle = lpss->styleNew;
9652 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9653 ((lpss->styleNew & WS_HSCROLL) == 0))
9654 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9656 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9657 ((lpss->styleNew & WS_VSCROLL) == 0))
9658 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9660 if (uNewView != uOldView)
9662 SIZE oldIconSize = infoPtr->iconSize;
9663 HIMAGELIST himl;
9665 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9666 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9668 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9669 SetRectEmpty(&infoPtr->rcFocus);
9671 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9672 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
9674 if (uNewView == LVS_ICON)
9676 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
9678 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9679 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9680 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
9683 else if (uNewView == LVS_REPORT)
9685 HDLAYOUT hl;
9686 WINDOWPOS wp;
9688 LISTVIEW_CreateHeader( infoPtr );
9690 hl.prc = &infoPtr->rcList;
9691 hl.pwpos = &wp;
9692 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9693 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
9694 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9695 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9698 LISTVIEW_UpdateItemSize(infoPtr);
9701 if (uNewView == LVS_REPORT)
9703 if ((lpss->styleOld ^ lpss->styleNew) & LVS_NOCOLUMNHEADER)
9705 if (lpss->styleNew & LVS_NOCOLUMNHEADER)
9707 /* Turn off the header control */
9708 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
9709 TRACE("Hide header control, was 0x%08x\n", style);
9710 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style | HDS_HIDDEN);
9711 } else {
9712 /* Turn on the header control */
9713 if ((style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE)) & HDS_HIDDEN)
9715 TRACE("Show header control, was 0x%08x\n", style);
9716 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, (style & ~HDS_HIDDEN) | WS_VISIBLE);
9722 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9723 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9724 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9726 /* update the size of the client area */
9727 LISTVIEW_UpdateSize(infoPtr);
9729 /* add scrollbars if needed */
9730 LISTVIEW_UpdateScroll(infoPtr);
9732 /* invalidate client area + erase background */
9733 LISTVIEW_InvalidateList(infoPtr);
9735 return 0;
9738 /***
9739 * DESCRIPTION:
9740 * Processes WM_STYLECHANGING messages.
9742 * PARAMETER(S):
9743 * [I] infoPtr : valid pointer to the listview structure
9744 * [I] wStyleType : window style type (normal or extended)
9745 * [I0] lpss : window style information
9747 * RETURN:
9748 * Zero
9750 static INT LISTVIEW_StyleChanging(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9751 STYLESTRUCT *lpss)
9753 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9754 wStyleType, lpss->styleOld, lpss->styleNew);
9756 /* don't forward LVS_OWNERDATA only if not already set to */
9757 if ((lpss->styleNew ^ lpss->styleOld) & LVS_OWNERDATA)
9759 if (lpss->styleOld & LVS_OWNERDATA)
9760 lpss->styleNew |= LVS_OWNERDATA;
9761 else
9762 lpss->styleNew &= ~LVS_OWNERDATA;
9765 return 0;
9768 /***
9769 * DESCRIPTION:
9770 * Processes WM_SHOWWINDOW messages.
9772 * PARAMETER(S):
9773 * [I] infoPtr : valid pointer to the listview structure
9774 * [I] bShown : window is being shown (FALSE when hidden)
9775 * [I] iStatus : window show status
9777 * RETURN:
9778 * Zero
9780 static LRESULT LISTVIEW_ShowWindow(LISTVIEW_INFO *infoPtr, BOOL bShown, INT iStatus)
9782 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9784 /* header delayed creation */
9785 if ((uView == LVS_REPORT) && bShown)
9787 LISTVIEW_CreateHeader(infoPtr);
9789 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle))
9790 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9793 return 0;
9796 /***
9797 * DESCRIPTION:
9798 * Processes CCM_GETVERSION messages.
9800 * PARAMETER(S):
9801 * [I] infoPtr : valid pointer to the listview structure
9803 * RETURN:
9804 * Current version
9806 static inline LRESULT LISTVIEW_GetVersion(LISTVIEW_INFO *infoPtr)
9808 return infoPtr->iVersion;
9811 /***
9812 * DESCRIPTION:
9813 * Processes CCM_SETVERSION messages.
9815 * PARAMETER(S):
9816 * [I] infoPtr : valid pointer to the listview structure
9817 * [I] iVersion : version to be set
9819 * RETURN:
9820 * -1 when requested version is greater then DLL version;
9821 * previous version otherwise
9823 static LRESULT LISTVIEW_SetVersion(LISTVIEW_INFO *infoPtr, DWORD iVersion)
9825 INT iOldVersion = infoPtr->iVersion;
9827 if (iVersion > COMCTL32_VERSION)
9828 return -1;
9830 infoPtr->iVersion = iVersion;
9832 TRACE("new version %d\n", iVersion);
9834 return iOldVersion;
9837 /***
9838 * DESCRIPTION:
9839 * Window procedure of the listview control.
9842 static LRESULT WINAPI
9843 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9845 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9847 TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg, wParam, lParam);
9849 if (!infoPtr && (uMsg != WM_NCCREATE))
9850 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9852 switch (uMsg)
9854 case LVM_APPROXIMATEVIEWRECT:
9855 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9856 LOWORD(lParam), HIWORD(lParam));
9857 case LVM_ARRANGE:
9858 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9860 /* case LVM_CANCELEDITLABEL: */
9862 case LVM_CREATEDRAGIMAGE:
9863 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9865 case LVM_DELETEALLITEMS:
9866 return LISTVIEW_DeleteAllItems(infoPtr, FALSE);
9868 case LVM_DELETECOLUMN:
9869 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9871 case LVM_DELETEITEM:
9872 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9874 case LVM_EDITLABELW:
9875 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9877 case LVM_EDITLABELA:
9878 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9880 /* case LVM_ENABLEGROUPVIEW: */
9882 case LVM_ENSUREVISIBLE:
9883 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9885 case LVM_FINDITEMW:
9886 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9888 case LVM_FINDITEMA:
9889 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9891 case LVM_GETBKCOLOR:
9892 return infoPtr->clrBk;
9894 /* case LVM_GETBKIMAGE: */
9896 case LVM_GETCALLBACKMASK:
9897 return infoPtr->uCallbackMask;
9899 case LVM_GETCOLUMNA:
9900 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9902 case LVM_GETCOLUMNW:
9903 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9905 case LVM_GETCOLUMNORDERARRAY:
9906 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9908 case LVM_GETCOLUMNWIDTH:
9909 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9911 case LVM_GETCOUNTPERPAGE:
9912 return LISTVIEW_GetCountPerPage(infoPtr);
9914 case LVM_GETEDITCONTROL:
9915 return (LRESULT)infoPtr->hwndEdit;
9917 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9918 return infoPtr->dwLvExStyle;
9920 /* case LVM_GETGROUPINFO: */
9922 /* case LVM_GETGROUPMETRICS: */
9924 case LVM_GETHEADER:
9925 return (LRESULT)infoPtr->hwndHeader;
9927 case LVM_GETHOTCURSOR:
9928 return (LRESULT)infoPtr->hHotCursor;
9930 case LVM_GETHOTITEM:
9931 return infoPtr->nHotItem;
9933 case LVM_GETHOVERTIME:
9934 return infoPtr->dwHoverTime;
9936 case LVM_GETIMAGELIST:
9937 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9939 /* case LVM_GETINSERTMARK: */
9941 /* case LVM_GETINSERTMARKCOLOR: */
9943 /* case LVM_GETINSERTMARKRECT: */
9945 case LVM_GETISEARCHSTRINGA:
9946 case LVM_GETISEARCHSTRINGW:
9947 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9948 return FALSE;
9950 case LVM_GETITEMA:
9951 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9953 case LVM_GETITEMW:
9954 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9956 case LVM_GETITEMCOUNT:
9957 return infoPtr->nItemCount;
9959 case LVM_GETITEMPOSITION:
9960 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9962 case LVM_GETITEMRECT:
9963 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9965 case LVM_GETITEMSPACING:
9966 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9968 case LVM_GETITEMSTATE:
9969 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9971 case LVM_GETITEMTEXTA:
9972 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9974 case LVM_GETITEMTEXTW:
9975 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9977 case LVM_GETNEXTITEM:
9978 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9980 case LVM_GETNUMBEROFWORKAREAS:
9981 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9982 return 1;
9984 case LVM_GETORIGIN:
9985 if (!lParam) return FALSE;
9986 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT ||
9987 (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST) return FALSE;
9988 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9989 return TRUE;
9991 /* case LVM_GETOUTLINECOLOR: */
9993 /* case LVM_GETSELECTEDCOLUMN: */
9995 case LVM_GETSELECTEDCOUNT:
9996 return LISTVIEW_GetSelectedCount(infoPtr);
9998 case LVM_GETSELECTIONMARK:
9999 return infoPtr->nSelectionMark;
10001 case LVM_GETSTRINGWIDTHA:
10002 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
10004 case LVM_GETSTRINGWIDTHW:
10005 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
10007 case LVM_GETSUBITEMRECT:
10008 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
10010 case LVM_GETTEXTBKCOLOR:
10011 return infoPtr->clrTextBk;
10013 case LVM_GETTEXTCOLOR:
10014 return infoPtr->clrText;
10016 /* case LVM_GETTILEINFO: */
10018 /* case LVM_GETTILEVIEWINFO: */
10020 case LVM_GETTOOLTIPS:
10021 if( !infoPtr->hwndToolTip )
10022 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
10023 return (LRESULT)infoPtr->hwndToolTip;
10025 case LVM_GETTOPINDEX:
10026 return LISTVIEW_GetTopIndex(infoPtr);
10028 case LVM_GETUNICODEFORMAT:
10029 return (infoPtr->notifyFormat == NFR_UNICODE);
10031 /* case LVM_GETVIEW: */
10033 case LVM_GETVIEWRECT:
10034 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
10036 case LVM_GETWORKAREAS:
10037 FIXME("LVM_GETWORKAREAS: unimplemented\n");
10038 return FALSE;
10040 /* case LVM_HASGROUP: */
10042 case LVM_HITTEST:
10043 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
10045 case LVM_INSERTCOLUMNA:
10046 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10048 case LVM_INSERTCOLUMNW:
10049 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10051 /* case LVM_INSERTGROUP: */
10053 /* case LVM_INSERTGROUPSORTED: */
10055 case LVM_INSERTITEMA:
10056 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
10058 case LVM_INSERTITEMW:
10059 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
10061 /* case LVM_INSERTMARKHITTEST: */
10063 /* case LVM_ISGROUPVIEWENABLED: */
10065 /* case LVM_MAPIDTOINDEX: */
10067 /* case LVM_MAPINDEXTOID: */
10069 /* case LVM_MOVEGROUP: */
10071 /* case LVM_MOVEITEMTOGROUP: */
10073 case LVM_REDRAWITEMS:
10074 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
10076 /* case LVM_REMOVEALLGROUPS: */
10078 /* case LVM_REMOVEGROUP: */
10080 case LVM_SCROLL:
10081 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
10083 case LVM_SETBKCOLOR:
10084 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
10086 /* case LVM_SETBKIMAGE: */
10088 case LVM_SETCALLBACKMASK:
10089 infoPtr->uCallbackMask = (UINT)wParam;
10090 return TRUE;
10092 case LVM_SETCOLUMNA:
10093 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10095 case LVM_SETCOLUMNW:
10096 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10098 case LVM_SETCOLUMNORDERARRAY:
10099 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
10101 case LVM_SETCOLUMNWIDTH:
10102 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
10104 case LVM_SETEXTENDEDLISTVIEWSTYLE:
10105 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
10107 /* case LVM_SETGROUPINFO: */
10109 /* case LVM_SETGROUPMETRICS: */
10111 case LVM_SETHOTCURSOR:
10112 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
10114 case LVM_SETHOTITEM:
10115 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
10117 case LVM_SETHOVERTIME:
10118 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
10120 case LVM_SETICONSPACING:
10121 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10123 case LVM_SETIMAGELIST:
10124 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
10126 /* case LVM_SETINFOTIP: */
10128 /* case LVM_SETINSERTMARK: */
10130 /* case LVM_SETINSERTMARKCOLOR: */
10132 case LVM_SETITEMA:
10133 case LVM_SETITEMW:
10135 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
10136 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, (uMsg == LVM_SETITEMW));
10139 case LVM_SETITEMCOUNT:
10140 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
10142 case LVM_SETITEMPOSITION:
10144 POINT pt;
10145 pt.x = (short)LOWORD(lParam);
10146 pt.y = (short)HIWORD(lParam);
10147 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
10150 case LVM_SETITEMPOSITION32:
10151 if (lParam == 0) return FALSE;
10152 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
10154 case LVM_SETITEMSTATE:
10155 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
10157 case LVM_SETITEMTEXTA:
10158 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
10160 case LVM_SETITEMTEXTW:
10161 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
10163 /* case LVM_SETOUTLINECOLOR: */
10165 /* case LVM_SETSELECTEDCOLUMN: */
10167 case LVM_SETSELECTIONMARK:
10168 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
10170 case LVM_SETTEXTBKCOLOR:
10171 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
10173 case LVM_SETTEXTCOLOR:
10174 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
10176 /* case LVM_SETTILEINFO: */
10178 /* case LVM_SETTILEVIEWINFO: */
10180 /* case LVM_SETTILEWIDTH: */
10182 case LVM_SETTOOLTIPS:
10183 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
10185 case LVM_SETUNICODEFORMAT:
10186 return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
10188 /* case LVM_SETVIEW: */
10190 /* case LVM_SETWORKAREAS: */
10192 /* case LVM_SORTGROUPS: */
10194 case LVM_SORTITEMS:
10195 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam, FALSE);
10197 case LVM_SORTITEMSEX:
10198 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam, TRUE);
10200 case LVM_SUBITEMHITTEST:
10201 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
10203 case LVM_UPDATE:
10204 return LISTVIEW_Update(infoPtr, (INT)wParam);
10206 case CCM_GETVERSION:
10207 return LISTVIEW_GetVersion(infoPtr);
10209 case CCM_SETVERSION:
10210 return LISTVIEW_SetVersion(infoPtr, wParam);
10212 case WM_CHAR:
10213 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
10215 case WM_COMMAND:
10216 return LISTVIEW_Command(infoPtr, wParam, lParam);
10218 case WM_NCCREATE:
10219 return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
10221 case WM_CREATE:
10222 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
10224 case WM_DESTROY:
10225 return LISTVIEW_Destroy(infoPtr);
10227 case WM_ENABLE:
10228 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
10230 case WM_ERASEBKGND:
10231 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
10233 case WM_GETDLGCODE:
10234 return DLGC_WANTCHARS | DLGC_WANTARROWS;
10236 case WM_GETFONT:
10237 return (LRESULT)infoPtr->hFont;
10239 case WM_HSCROLL:
10240 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10242 case WM_KEYDOWN:
10243 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
10245 case WM_KILLFOCUS:
10246 return LISTVIEW_KillFocus(infoPtr);
10248 case WM_LBUTTONDBLCLK:
10249 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10251 case WM_LBUTTONDOWN:
10252 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10254 case WM_LBUTTONUP:
10255 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10257 case WM_MOUSEMOVE:
10258 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10260 case WM_MOUSEHOVER:
10261 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10263 case WM_NCDESTROY:
10264 return LISTVIEW_NCDestroy(infoPtr);
10266 case WM_NCPAINT:
10267 if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
10268 return 0;
10269 goto fwd_msg;
10271 case WM_NOTIFY:
10272 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
10273 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
10274 else return 0;
10276 case WM_NOTIFYFORMAT:
10277 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
10279 case WM_PRINTCLIENT:
10280 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
10282 case WM_PAINT:
10283 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
10285 case WM_RBUTTONDBLCLK:
10286 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10288 case WM_RBUTTONDOWN:
10289 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10291 case WM_RBUTTONUP:
10292 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10294 case WM_SETCURSOR:
10295 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
10296 return TRUE;
10297 goto fwd_msg;
10299 case WM_SETFOCUS:
10300 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
10302 case WM_SETFONT:
10303 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
10305 case WM_SETREDRAW:
10306 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
10308 case WM_SHOWWINDOW:
10309 LISTVIEW_ShowWindow(infoPtr, (BOOL)wParam, (INT)lParam);
10310 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10312 case WM_SIZE:
10313 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10315 case WM_STYLECHANGED:
10316 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10318 case WM_STYLECHANGING:
10319 return LISTVIEW_StyleChanging(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10321 case WM_SYSCOLORCHANGE:
10322 COMCTL32_RefreshSysColors();
10323 return 0;
10325 /* case WM_TIMER: */
10326 case WM_THEMECHANGED:
10327 return LISTVIEW_ThemeChanged(infoPtr);
10329 case WM_VSCROLL:
10330 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10332 case WM_MOUSEWHEEL:
10333 if (wParam & (MK_SHIFT | MK_CONTROL))
10334 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10335 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
10337 case WM_WINDOWPOSCHANGED:
10338 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
10340 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
10341 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
10342 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
10344 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
10346 MEASUREITEMSTRUCT mis;
10347 mis.CtlType = ODT_LISTVIEW;
10348 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
10349 mis.itemID = -1;
10350 mis.itemWidth = 0;
10351 mis.itemData = 0;
10352 mis.itemHeight= infoPtr->nItemHeight;
10353 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
10354 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
10355 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
10358 LISTVIEW_UpdateSize(infoPtr);
10359 LISTVIEW_UpdateScroll(infoPtr);
10361 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10363 /* case WM_WININICHANGE: */
10365 default:
10366 if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
10367 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
10369 fwd_msg:
10370 /* call default window procedure */
10371 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10376 /***
10377 * DESCRIPTION:
10378 * Registers the window class.
10380 * PARAMETER(S):
10381 * None
10383 * RETURN:
10384 * None
10386 void LISTVIEW_Register(void)
10388 WNDCLASSW wndClass;
10390 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
10391 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
10392 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
10393 wndClass.cbClsExtra = 0;
10394 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
10395 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
10396 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
10397 wndClass.lpszClassName = WC_LISTVIEWW;
10398 RegisterClassW(&wndClass);
10401 /***
10402 * DESCRIPTION:
10403 * Unregisters the window class.
10405 * PARAMETER(S):
10406 * None
10408 * RETURN:
10409 * None
10411 void LISTVIEW_Unregister(void)
10413 UnregisterClassW(WC_LISTVIEWW, NULL);
10416 /***
10417 * DESCRIPTION:
10418 * Handle any WM_COMMAND messages
10420 * PARAMETER(S):
10421 * [I] infoPtr : valid pointer to the listview structure
10422 * [I] wParam : the first message parameter
10423 * [I] lParam : the second message parameter
10425 * RETURN:
10426 * Zero.
10428 static LRESULT LISTVIEW_Command(const LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
10430 switch (HIWORD(wParam))
10432 case EN_UPDATE:
10435 * Adjust the edit window size
10437 WCHAR buffer[1024];
10438 HDC hdc = GetDC(infoPtr->hwndEdit);
10439 HFONT hFont, hOldFont = 0;
10440 RECT rect;
10441 SIZE sz;
10443 if (!infoPtr->hwndEdit || !hdc) return 0;
10444 GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
10445 GetWindowRect(infoPtr->hwndEdit, &rect);
10447 /* Select font to get the right dimension of the string */
10448 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
10449 if(hFont != 0)
10451 hOldFont = SelectObject(hdc, hFont);
10454 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
10456 TEXTMETRICW textMetric;
10458 /* Add Extra spacing for the next character */
10459 GetTextMetricsW(hdc, &textMetric);
10460 sz.cx += (textMetric.tmMaxCharWidth * 2);
10462 SetWindowPos (
10463 infoPtr->hwndEdit,
10464 HWND_TOP,
10467 sz.cx,
10468 rect.bottom - rect.top,
10469 SWP_DRAWFRAME|SWP_NOMOVE);
10471 if(hFont != 0)
10472 SelectObject(hdc, hOldFont);
10474 ReleaseDC(infoPtr->hwndEdit, hdc);
10476 break;
10479 default:
10480 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
10483 return 0;
10487 /***
10488 * DESCRIPTION:
10489 * Subclassed edit control windproc function
10491 * PARAMETER(S):
10492 * [I] hwnd : the edit window handle
10493 * [I] uMsg : the message that is to be processed
10494 * [I] wParam : first message parameter
10495 * [I] lParam : second message parameter
10496 * [I] isW : TRUE if input is Unicode
10498 * RETURN:
10499 * Zero.
10501 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
10503 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
10504 BOOL cancel = FALSE;
10506 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10507 hwnd, uMsg, wParam, lParam, isW);
10509 switch (uMsg)
10511 case WM_GETDLGCODE:
10512 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
10514 case WM_KILLFOCUS:
10515 break;
10517 case WM_DESTROY:
10519 WNDPROC editProc = infoPtr->EditWndProc;
10520 infoPtr->EditWndProc = 0;
10521 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
10522 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
10525 case WM_KEYDOWN:
10526 if (VK_ESCAPE == (INT)wParam)
10528 cancel = TRUE;
10529 break;
10531 else if (VK_RETURN == (INT)wParam)
10532 break;
10534 default:
10535 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
10538 /* kill the edit */
10539 if (infoPtr->hwndEdit)
10541 LPWSTR buffer = NULL;
10543 infoPtr->hwndEdit = 0;
10544 if (!cancel)
10546 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
10548 if (len)
10550 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
10552 if (isW) GetWindowTextW(hwnd, buffer, len+1);
10553 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
10557 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
10559 Free(buffer);
10562 SendMessageW(hwnd, WM_CLOSE, 0, 0);
10563 return 0;
10566 /***
10567 * DESCRIPTION:
10568 * Subclassed edit control Unicode windproc function
10570 * PARAMETER(S):
10571 * [I] hwnd : the edit window handle
10572 * [I] uMsg : the message that is to be processed
10573 * [I] wParam : first message parameter
10574 * [I] lParam : second message parameter
10576 * RETURN:
10578 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10580 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
10583 /***
10584 * DESCRIPTION:
10585 * Subclassed edit control ANSI windproc function
10587 * PARAMETER(S):
10588 * [I] hwnd : the edit window handle
10589 * [I] uMsg : the message that is to be processed
10590 * [I] wParam : first message parameter
10591 * [I] lParam : second message parameter
10593 * RETURN:
10595 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10597 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
10600 /***
10601 * DESCRIPTION:
10602 * Creates a subclassed edit control
10604 * PARAMETER(S):
10605 * [I] infoPtr : valid pointer to the listview structure
10606 * [I] text : initial text for the edit
10607 * [I] style : the window style
10608 * [I] isW : TRUE if input is Unicode
10610 * RETURN:
10612 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
10613 INT x, INT y, INT width, INT height, BOOL isW)
10615 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
10616 HWND hedit;
10617 SIZE sz;
10618 HDC hdc;
10619 HDC hOldFont=0;
10620 TEXTMETRICW textMetric;
10621 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
10623 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
10625 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
10626 hdc = GetDC(infoPtr->hwndSelf);
10628 /* Select the font to get appropriate metric dimensions */
10629 if(infoPtr->hFont != 0)
10630 hOldFont = SelectObject(hdc, infoPtr->hFont);
10632 /*Get String Length in pixels */
10633 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
10635 /*Add Extra spacing for the next character */
10636 GetTextMetricsW(hdc, &textMetric);
10637 sz.cx += (textMetric.tmMaxCharWidth * 2);
10639 if(infoPtr->hFont != 0)
10640 SelectObject(hdc, hOldFont);
10642 ReleaseDC(infoPtr->hwndSelf, hdc);
10643 if (isW)
10644 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10645 else
10646 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10648 if (!hedit) return 0;
10650 infoPtr->EditWndProc = (WNDPROC)
10651 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
10652 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
10654 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
10656 return hedit;