comctl32/listview: Fix LVM_INSERTITEM handling on LVS_SORTxxx styles.
[wine.git] / dlls / comctl32 / listview.c
blob75556e6cab286759e8d63d459a449fbc12e131f9
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 * -- if list is sorted by item text LISTVIEW_InsertItemT could use
67 * binary search to calculate item index (e.g. DPA_Search()).
68 * This requires sorted state to be reliably tracked in item modifiers.
69 * -- we should keep an ordered array of coordinates in iconic mode
70 * this would allow to frame items (iterator_frameditems),
71 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
73 * Flags
74 * -- LVIF_COLUMNS
75 * -- LVIF_GROUPID
77 * States
78 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
79 * -- LVIS_CUT
80 * -- LVIS_DROPHILITED
81 * -- LVIS_OVERLAYMASK
83 * Styles
84 * -- LVS_NOLABELWRAP
85 * -- LVS_NOSCROLL (see Q137520)
86 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
87 * -- LVS_ALIGNTOP
88 * -- LVS_TYPESTYLEMASK
90 * Extended Styles
91 * -- LVS_EX_BORDERSELECT
92 * -- LVS_EX_FLATSB
93 * -- LVS_EX_HEADERDRAGDROP
94 * -- LVS_EX_INFOTIP
95 * -- LVS_EX_LABELTIP
96 * -- LVS_EX_MULTIWORKAREAS
97 * -- LVS_EX_REGIONAL
98 * -- LVS_EX_SIMPLESELECT
99 * -- LVS_EX_TWOCLICKACTIVATE
100 * -- LVS_EX_UNDERLINECOLD
101 * -- LVS_EX_UNDERLINEHOT
103 * Notifications:
104 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
105 * -- LVN_GETINFOTIP
106 * -- LVN_HOTTRACK
107 * -- LVN_MARQUEEBEGIN
108 * -- LVN_SETDISPINFO
109 * -- NM_HOVER
110 * -- LVN_BEGINRDRAG
112 * Messages:
113 * -- LVM_CANCELEDITLABEL
114 * -- LVM_ENABLEGROUPVIEW
115 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
116 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
117 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
118 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
119 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
120 * -- LVM_GETINSERTMARKRECT
121 * -- LVM_GETNUMBEROFWORKAREAS
122 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
123 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
124 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
125 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
126 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
127 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
128 * -- LVM_GETVIEW, LVM_SETVIEW
129 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
130 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
131 * -- LVM_INSERTGROUPSORTED
132 * -- LVM_INSERTMARKHITTEST
133 * -- LVM_ISGROUPVIEWENABLED
134 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
135 * -- LVM_MOVEGROUP
136 * -- LVM_MOVEITEMTOGROUP
137 * -- LVM_SETINFOTIP
138 * -- LVM_SETTILEWIDTH
139 * -- LVM_SORTGROUPS
141 * Macros:
142 * -- ListView_GetCheckSate, ListView_SetCheckState
143 * -- ListView_GetHoverTime, ListView_SetHoverTime
144 * -- ListView_GetISearchString
145 * -- ListView_GetNumberOfWorkAreas
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;
6596 /***
6597 * DESCRIPTION:
6598 * Inserts a new item in the listview control.
6600 * PARAMETER(S):
6601 * [I] infoPtr : valid pointer to the listview structure
6602 * [I] lpLVItem : item information
6603 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6605 * RETURN:
6606 * SUCCESS : new item index
6607 * FAILURE : -1
6609 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6611 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6612 INT nItem;
6613 HDPA hdpaSubItems;
6614 NMLISTVIEW nmlv;
6615 ITEM_INFO *lpItem;
6616 BOOL is_sorted, has_changed;
6617 LVITEMW item;
6618 HWND hwndSelf = infoPtr->hwndSelf;
6620 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6622 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6624 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6625 if (!lpLVItem || lpLVItem->iSubItem) return -1;
6627 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6629 if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
6631 /* insert item in listview control data structure */
6632 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6633 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6635 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6636 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6638 if (lpLVItem->iItem < 0 && !is_sorted) return -1;
6640 /* calculate new item index */
6641 if (is_sorted)
6643 HDPA hItem;
6644 ITEM_INFO *item_s;
6645 INT i = 0, cmpv;
6647 while (i < infoPtr->nItemCount)
6649 hItem = DPA_GetPtr( infoPtr->hdpaItems, i);
6650 item_s = (ITEM_INFO*)DPA_GetPtr(hItem, 0);
6652 cmpv = textcmpWT(item_s->hdr.pszText, lpLVItem->pszText, TRUE);
6653 if (infoPtr->dwStyle & LVS_SORTDESCENDING) cmpv *= -1;
6655 if (cmpv >= 0) break;
6656 i++;
6658 nItem = i;
6660 else
6661 nItem = min(lpLVItem->iItem, infoPtr->nItemCount);
6663 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6664 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6665 if (nItem == -1) goto fail;
6666 infoPtr->nItemCount++;
6668 /* shift indices first so they don't get tangled */
6669 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6671 /* set the item attributes */
6672 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6674 /* full size structure expected - _WIN32IE >= 0x560 */
6675 item = *lpLVItem;
6677 else if (lpLVItem->mask & LVIF_INDENT)
6679 /* indent member expected - _WIN32IE >= 0x300 */
6680 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6682 else
6684 /* minimal structure expected */
6685 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6687 item.iItem = nItem;
6688 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6690 item.mask |= LVIF_STATE;
6691 item.stateMask |= LVIS_STATEIMAGEMASK;
6692 item.state &= ~LVIS_STATEIMAGEMASK;
6693 item.state |= INDEXTOSTATEIMAGEMASK(1);
6695 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6697 /* make room for the position, if we are in the right mode */
6698 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6700 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6701 goto undo;
6702 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6704 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6705 goto undo;
6709 /* send LVN_INSERTITEM notification */
6710 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6711 nmlv.iItem = nItem;
6712 nmlv.lParam = lpItem->lParam;
6713 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6714 if (!IsWindow(hwndSelf))
6715 return -1;
6717 /* align items (set position of each item) */
6718 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6720 POINT pt;
6722 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6723 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6724 else
6725 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6727 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6730 /* now is the invalidation fun */
6731 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6732 return nItem;
6734 undo:
6735 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6736 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6737 infoPtr->nItemCount--;
6738 fail:
6739 DPA_DeletePtr(hdpaSubItems, 0);
6740 DPA_Destroy (hdpaSubItems);
6741 Free (lpItem);
6742 return -1;
6745 /***
6746 * DESCRIPTION:
6747 * Redraws a range of items.
6749 * PARAMETER(S):
6750 * [I] infoPtr : valid pointer to the listview structure
6751 * [I] nFirst : first item
6752 * [I] nLast : last item
6754 * RETURN:
6755 * SUCCESS : TRUE
6756 * FAILURE : FALSE
6758 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6760 INT i;
6762 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6763 max(nFirst, nLast) >= infoPtr->nItemCount)
6764 return FALSE;
6766 for (i = nFirst; i <= nLast; i++)
6767 LISTVIEW_InvalidateItem(infoPtr, i);
6769 return TRUE;
6772 /***
6773 * DESCRIPTION:
6774 * Scroll the content of a listview.
6776 * PARAMETER(S):
6777 * [I] infoPtr : valid pointer to the listview structure
6778 * [I] dx : horizontal scroll amount in pixels
6779 * [I] dy : vertical scroll amount in pixels
6781 * RETURN:
6782 * SUCCESS : TRUE
6783 * FAILURE : FALSE
6785 * COMMENTS:
6786 * If the control is in report mode (LVS_REPORT) the control can
6787 * be scrolled only in line increments. "dy" will be rounded to the
6788 * nearest number of pixels that are a whole line. Ex: if line height
6789 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6790 * is passed, then the scroll will be 0. (per MSDN 7/2002)
6792 * For: (per experimentation with native control and CSpy ListView)
6793 * LVS_ICON dy=1 = 1 pixel (vertical only)
6794 * dx ignored
6795 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6796 * dx ignored
6797 * LVS_LIST dx=1 = 1 column (horizontal only)
6798 * but will only scroll 1 column per message
6799 * no matter what the value.
6800 * dy must be 0 or FALSE returned.
6801 * LVS_REPORT dx=1 = 1 pixel
6802 * dy= see above
6805 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6807 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6808 case LVS_REPORT:
6809 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6810 dy /= infoPtr->nItemHeight;
6811 break;
6812 case LVS_LIST:
6813 if (dy != 0) return FALSE;
6814 break;
6815 default: /* icon */
6816 dx = 0;
6817 break;
6820 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6821 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6823 return TRUE;
6826 /***
6827 * DESCRIPTION:
6828 * Sets the background color.
6830 * PARAMETER(S):
6831 * [I] infoPtr : valid pointer to the listview structure
6832 * [I] clrBk : background color
6834 * RETURN:
6835 * SUCCESS : TRUE
6836 * FAILURE : FALSE
6838 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6840 TRACE("(clrBk=%x)\n", clrBk);
6842 if(infoPtr->clrBk != clrBk) {
6843 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6844 infoPtr->clrBk = clrBk;
6845 if (clrBk == CLR_NONE)
6846 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6847 else
6848 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6849 LISTVIEW_InvalidateList(infoPtr);
6852 return TRUE;
6855 /* LISTVIEW_SetBkImage */
6857 /*** Helper for {Insert,Set}ColumnT *only* */
6858 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
6859 const LVCOLUMNW *lpColumn, BOOL isW)
6861 if (lpColumn->mask & LVCF_FMT)
6863 /* format member is valid */
6864 lphdi->mask |= HDI_FORMAT;
6866 /* set text alignment (leftmost column must be left-aligned) */
6867 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6868 lphdi->fmt |= HDF_LEFT;
6869 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6870 lphdi->fmt |= HDF_RIGHT;
6871 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6872 lphdi->fmt |= HDF_CENTER;
6874 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6875 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6877 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6879 lphdi->fmt |= HDF_IMAGE;
6880 lphdi->iImage = I_IMAGECALLBACK;
6884 if (lpColumn->mask & LVCF_WIDTH)
6886 lphdi->mask |= HDI_WIDTH;
6887 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6889 /* make it fill the remainder of the controls width */
6890 RECT rcHeader;
6891 INT item_index;
6893 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6895 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6896 lphdi->cxy += rcHeader.right - rcHeader.left;
6899 /* retrieve the layout of the header */
6900 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6901 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
6903 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6905 else
6906 lphdi->cxy = lpColumn->cx;
6909 if (lpColumn->mask & LVCF_TEXT)
6911 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6912 lphdi->fmt |= HDF_STRING;
6913 lphdi->pszText = lpColumn->pszText;
6914 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6917 if (lpColumn->mask & LVCF_IMAGE)
6919 lphdi->mask |= HDI_IMAGE;
6920 lphdi->iImage = lpColumn->iImage;
6923 if (lpColumn->mask & LVCF_ORDER)
6925 lphdi->mask |= HDI_ORDER;
6926 lphdi->iOrder = lpColumn->iOrder;
6931 /***
6932 * DESCRIPTION:
6933 * Inserts a new column.
6935 * PARAMETER(S):
6936 * [I] infoPtr : valid pointer to the listview structure
6937 * [I] nColumn : column index
6938 * [I] lpColumn : column information
6939 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6941 * RETURN:
6942 * SUCCESS : new column index
6943 * FAILURE : -1
6945 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6946 const LVCOLUMNW *lpColumn, BOOL isW)
6948 COLUMN_INFO *lpColumnInfo;
6949 INT nNewColumn;
6950 HDITEMW hdi;
6951 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6953 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6955 if (!lpColumn || nColumn < 0) return -1;
6956 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6958 ZeroMemory(&hdi, sizeof(HDITEMW));
6959 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6962 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
6963 * (can be seen in SPY) otherwise column never gets added.
6965 if (!(lpColumn->mask & LVCF_WIDTH)) {
6966 hdi.mask |= HDI_WIDTH;
6967 hdi.cxy = 10;
6971 * when the iSubItem is available Windows copies it to the header lParam. It seems
6972 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
6974 if (lpColumn->mask & LVCF_SUBITEM)
6976 hdi.mask |= HDI_LPARAM;
6977 hdi.lParam = lpColumn->iSubItem;
6980 /* create header if not present */
6981 LISTVIEW_CreateHeader(infoPtr);
6982 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle) &&
6983 (LVS_REPORT == uView) && (WS_VISIBLE & infoPtr->dwStyle))
6985 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
6988 /* insert item in header control */
6989 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6990 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6991 (WPARAM)nColumn, (LPARAM)&hdi);
6992 if (nNewColumn == -1) return -1;
6993 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6995 /* create our own column info */
6996 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6997 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6999 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
7000 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
7002 /* now we have to actually adjust the data */
7003 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
7005 SUBITEM_INFO *lpSubItem;
7006 HDPA hdpaSubItems;
7007 INT nItem, i;
7009 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
7011 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
7012 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
7014 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
7015 if (lpSubItem->iSubItem >= nNewColumn)
7016 lpSubItem->iSubItem++;
7021 /* make space for the new column */
7022 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7023 LISTVIEW_UpdateItemSize(infoPtr);
7025 return nNewColumn;
7027 fail:
7028 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
7029 if (lpColumnInfo)
7031 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
7032 Free(lpColumnInfo);
7034 return -1;
7037 /***
7038 * DESCRIPTION:
7039 * Sets the attributes of a header item.
7041 * PARAMETER(S):
7042 * [I] infoPtr : valid pointer to the listview structure
7043 * [I] nColumn : column index
7044 * [I] lpColumn : column attributes
7045 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
7047 * RETURN:
7048 * SUCCESS : TRUE
7049 * FAILURE : FALSE
7051 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
7052 const LVCOLUMNW *lpColumn, BOOL isW)
7054 HDITEMW hdi, hdiget;
7055 BOOL bResult;
7057 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
7059 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7061 ZeroMemory(&hdi, sizeof(HDITEMW));
7062 if (lpColumn->mask & LVCF_FMT)
7064 hdi.mask |= HDI_FORMAT;
7065 hdiget.mask = HDI_FORMAT;
7066 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
7067 hdi.fmt = hdiget.fmt & HDF_STRING;
7069 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
7071 /* set header item attributes */
7072 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
7073 if (!bResult) return FALSE;
7075 if (lpColumn->mask & LVCF_FMT)
7077 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
7078 int oldFmt = lpColumnInfo->fmt;
7080 lpColumnInfo->fmt = lpColumn->fmt;
7081 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
7083 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7084 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
7088 return TRUE;
7091 /***
7092 * DESCRIPTION:
7093 * Sets the column order array
7095 * PARAMETERS:
7096 * [I] infoPtr : valid pointer to the listview structure
7097 * [I] iCount : number of elements in column order array
7098 * [I] lpiArray : pointer to column order array
7100 * RETURN:
7101 * SUCCESS : TRUE
7102 * FAILURE : FALSE
7104 static BOOL LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
7106 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7108 if (!lpiArray)
7109 return FALSE;
7111 return TRUE;
7115 /***
7116 * DESCRIPTION:
7117 * Sets the width of a column
7119 * PARAMETERS:
7120 * [I] infoPtr : valid pointer to the listview structure
7121 * [I] nColumn : column index
7122 * [I] cx : column width
7124 * RETURN:
7125 * SUCCESS : TRUE
7126 * FAILURE : FALSE
7128 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
7130 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7131 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
7132 INT max_cx = 0;
7133 HDITEMW hdi;
7135 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
7137 /* set column width only if in report or list mode */
7138 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
7140 /* take care of invalid cx values */
7141 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
7142 else if (uView == LVS_LIST && cx < 1) return FALSE;
7144 /* resize all columns if in LVS_LIST mode */
7145 if(uView == LVS_LIST)
7147 infoPtr->nItemWidth = cx;
7148 LISTVIEW_InvalidateList(infoPtr);
7149 return TRUE;
7152 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7154 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
7156 INT nLabelWidth;
7157 LVITEMW lvItem;
7159 lvItem.mask = LVIF_TEXT;
7160 lvItem.iItem = 0;
7161 lvItem.iSubItem = nColumn;
7162 lvItem.pszText = szDispText;
7163 lvItem.cchTextMax = DISP_TEXT_SIZE;
7164 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7166 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
7167 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
7168 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
7170 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
7171 max_cx += infoPtr->iconSize.cx;
7172 max_cx += TRAILING_LABEL_PADDING;
7175 /* autosize based on listview items width */
7176 if(cx == LVSCW_AUTOSIZE)
7177 cx = max_cx;
7178 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7180 /* if iCol is the last column make it fill the remainder of the controls width */
7181 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
7183 RECT rcHeader;
7184 POINT Origin;
7186 LISTVIEW_GetOrigin(infoPtr, &Origin);
7187 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
7189 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
7191 else
7193 /* Despite what the MS docs say, if this is not the last
7194 column, then MS resizes the column to the width of the
7195 largest text string in the column, including headers
7196 and items. This is different from LVSCW_AUTOSIZE in that
7197 LVSCW_AUTOSIZE ignores the header string length. */
7198 cx = 0;
7200 /* retrieve header text */
7201 hdi.mask = HDI_TEXT;
7202 hdi.cchTextMax = DISP_TEXT_SIZE;
7203 hdi.pszText = szDispText;
7204 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
7206 HDC hdc = GetDC(infoPtr->hwndSelf);
7207 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
7208 SIZE size;
7210 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
7211 cx = size.cx + TRAILING_HEADER_PADDING;
7212 /* FIXME: Take into account the header image, if one is present */
7213 SelectObject(hdc, old_font);
7214 ReleaseDC(infoPtr->hwndSelf, hdc);
7216 cx = max (cx, max_cx);
7220 if (cx < 0) return FALSE;
7222 /* call header to update the column change */
7223 hdi.mask = HDI_WIDTH;
7224 hdi.cxy = cx;
7225 TRACE("hdi.cxy=%d\n", hdi.cxy);
7226 return Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7229 /***
7230 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
7233 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
7235 HDC hdc_wnd, hdc;
7236 HBITMAP hbm_im, hbm_mask, hbm_orig;
7237 RECT rc;
7238 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
7239 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
7240 HIMAGELIST himl;
7242 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
7243 ILC_COLOR | ILC_MASK, 2, 2);
7244 hdc_wnd = GetDC(infoPtr->hwndSelf);
7245 hdc = CreateCompatibleDC(hdc_wnd);
7246 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
7247 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
7248 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
7250 rc.left = rc.top = 0;
7251 rc.right = GetSystemMetrics(SM_CXSMICON);
7252 rc.bottom = GetSystemMetrics(SM_CYSMICON);
7254 hbm_orig = SelectObject(hdc, hbm_mask);
7255 FillRect(hdc, &rc, hbr_white);
7256 InflateRect(&rc, -2, -2);
7257 FillRect(hdc, &rc, hbr_black);
7259 SelectObject(hdc, hbm_im);
7260 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
7261 SelectObject(hdc, hbm_orig);
7262 ImageList_Add(himl, hbm_im, hbm_mask);
7264 SelectObject(hdc, hbm_im);
7265 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
7266 SelectObject(hdc, hbm_orig);
7267 ImageList_Add(himl, hbm_im, hbm_mask);
7269 DeleteObject(hbm_mask);
7270 DeleteObject(hbm_im);
7271 DeleteDC(hdc);
7273 return himl;
7276 /***
7277 * DESCRIPTION:
7278 * Sets the extended listview style.
7280 * PARAMETERS:
7281 * [I] infoPtr : valid pointer to the listview structure
7282 * [I] dwMask : mask
7283 * [I] dwStyle : style
7285 * RETURN:
7286 * SUCCESS : previous style
7287 * FAILURE : 0
7289 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwExStyle)
7291 DWORD dwOldExStyle = infoPtr->dwLvExStyle;
7293 /* set new style */
7294 if (dwMask)
7295 infoPtr->dwLvExStyle = (dwOldExStyle & ~dwMask) | (dwExStyle & dwMask);
7296 else
7297 infoPtr->dwLvExStyle = dwExStyle;
7299 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_CHECKBOXES)
7301 HIMAGELIST himl = 0;
7302 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7304 LVITEMW item;
7305 item.mask = LVIF_STATE;
7306 item.stateMask = LVIS_STATEIMAGEMASK;
7307 item.state = INDEXTOSTATEIMAGEMASK(1);
7308 LISTVIEW_SetItemState(infoPtr, -1, &item);
7310 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
7312 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
7315 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_HEADERDRAGDROP)
7317 DWORD dwStyle;
7319 /* if not already created */
7320 LISTVIEW_CreateHeader(infoPtr);
7322 dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
7323 if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
7324 dwStyle |= HDS_DRAGDROP;
7325 else
7326 dwStyle &= ~HDS_DRAGDROP;
7327 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
7330 /* GRIDLINES adds decoration at top so changes sizes */
7331 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_GRIDLINES)
7333 LISTVIEW_UpdateSize(infoPtr);
7337 LISTVIEW_InvalidateList(infoPtr);
7338 return dwOldExStyle;
7341 /***
7342 * DESCRIPTION:
7343 * Sets the new hot cursor used during hot tracking and hover selection.
7345 * PARAMETER(S):
7346 * [I] infoPtr : valid pointer to the listview structure
7347 * [I] hCursor : the new hot cursor handle
7349 * RETURN:
7350 * Returns the previous hot cursor
7352 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
7354 HCURSOR oldCursor = infoPtr->hHotCursor;
7356 infoPtr->hHotCursor = hCursor;
7358 return oldCursor;
7362 /***
7363 * DESCRIPTION:
7364 * Sets the hot item index.
7366 * PARAMETERS:
7367 * [I] infoPtr : valid pointer to the listview structure
7368 * [I] iIndex : index
7370 * RETURN:
7371 * SUCCESS : previous hot item index
7372 * FAILURE : -1 (no hot item)
7374 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
7376 INT iOldIndex = infoPtr->nHotItem;
7378 infoPtr->nHotItem = iIndex;
7380 return iOldIndex;
7384 /***
7385 * DESCRIPTION:
7386 * Sets the amount of time the cursor must hover over an item before it is selected.
7388 * PARAMETER(S):
7389 * [I] infoPtr : valid pointer to the listview structure
7390 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7392 * RETURN:
7393 * Returns the previous hover time
7395 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
7397 DWORD oldHoverTime = infoPtr->dwHoverTime;
7399 infoPtr->dwHoverTime = dwHoverTime;
7401 return oldHoverTime;
7404 /***
7405 * DESCRIPTION:
7406 * Sets spacing for icons of LVS_ICON style.
7408 * PARAMETER(S):
7409 * [I] infoPtr : valid pointer to the listview structure
7410 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7411 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7413 * RETURN:
7414 * MAKELONG(oldcx, oldcy)
7416 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
7418 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7419 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7421 TRACE("requested=(%d,%d)\n", cx, cy);
7423 /* this is supported only for LVS_ICON style */
7424 if (uView != LVS_ICON) return oldspacing;
7426 /* set to defaults, if instructed to */
7427 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
7428 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
7430 /* if 0 then compute width
7431 * FIXME: Should scan each item and determine max width of
7432 * icon or label, then make that the width */
7433 if (cx == 0)
7434 cx = infoPtr->iconSpacing.cx;
7436 /* if 0 then compute height */
7437 if (cy == 0)
7438 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
7439 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
7442 infoPtr->iconSpacing.cx = cx;
7443 infoPtr->iconSpacing.cy = cy;
7445 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7446 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
7447 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7448 infoPtr->ntmHeight);
7450 /* these depend on the iconSpacing */
7451 LISTVIEW_UpdateItemSize(infoPtr);
7453 return oldspacing;
7456 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
7458 INT cx, cy;
7460 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
7462 size->cx = cx;
7463 size->cy = cy;
7465 else
7467 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
7468 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7472 /***
7473 * DESCRIPTION:
7474 * Sets image lists.
7476 * PARAMETER(S):
7477 * [I] infoPtr : valid pointer to the listview structure
7478 * [I] nType : image list type
7479 * [I] himl : image list handle
7481 * RETURN:
7482 * SUCCESS : old image list
7483 * FAILURE : NULL
7485 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7487 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7488 INT oldHeight = infoPtr->nItemHeight;
7489 HIMAGELIST himlOld = 0;
7491 TRACE("(nType=%d, himl=%p\n", nType, himl);
7493 switch (nType)
7495 case LVSIL_NORMAL:
7496 himlOld = infoPtr->himlNormal;
7497 infoPtr->himlNormal = himl;
7498 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7499 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7500 break;
7502 case LVSIL_SMALL:
7503 himlOld = infoPtr->himlSmall;
7504 infoPtr->himlSmall = himl;
7505 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7506 break;
7508 case LVSIL_STATE:
7509 himlOld = infoPtr->himlState;
7510 infoPtr->himlState = himl;
7511 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7512 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7513 break;
7515 default:
7516 ERR("Unknown icon type=%d\n", nType);
7517 return NULL;
7520 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7521 if (infoPtr->nItemHeight != oldHeight)
7522 LISTVIEW_UpdateScroll(infoPtr);
7524 return himlOld;
7527 /***
7528 * DESCRIPTION:
7529 * Preallocates memory (does *not* set the actual count of items !)
7531 * PARAMETER(S):
7532 * [I] infoPtr : valid pointer to the listview structure
7533 * [I] nItems : item count (projected number of items to allocate)
7534 * [I] dwFlags : update flags
7536 * RETURN:
7537 * SUCCESS : TRUE
7538 * FAILURE : FALSE
7540 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7542 TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
7544 if (infoPtr->dwStyle & LVS_OWNERDATA)
7546 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7547 INT nOldCount = infoPtr->nItemCount;
7549 if (nItems < nOldCount)
7551 RANGE range = { nItems, nOldCount };
7552 ranges_del(infoPtr->selectionRanges, range);
7553 if (infoPtr->nFocusedItem >= nItems)
7555 LISTVIEW_SetItemFocus(infoPtr, -1);
7556 SetRectEmpty(&infoPtr->rcFocus);
7560 infoPtr->nItemCount = nItems;
7561 LISTVIEW_UpdateScroll(infoPtr);
7563 /* the flags are valid only in ownerdata report and list modes */
7564 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7566 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7567 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7569 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7570 LISTVIEW_InvalidateList(infoPtr);
7571 else
7573 INT nFrom, nTo;
7574 POINT Origin;
7575 RECT rcErase;
7577 LISTVIEW_GetOrigin(infoPtr, &Origin);
7578 nFrom = min(nOldCount, nItems);
7579 nTo = max(nOldCount, nItems);
7581 if (uView == LVS_REPORT)
7583 rcErase.left = 0;
7584 rcErase.top = nFrom * infoPtr->nItemHeight;
7585 rcErase.right = infoPtr->nItemWidth;
7586 rcErase.bottom = nTo * infoPtr->nItemHeight;
7587 OffsetRect(&rcErase, Origin.x, Origin.y);
7588 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7589 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7591 else /* LVS_LIST */
7593 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7595 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7596 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7597 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7598 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7599 OffsetRect(&rcErase, Origin.x, Origin.y);
7600 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7601 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7603 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7604 rcErase.top = 0;
7605 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7606 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7607 OffsetRect(&rcErase, Origin.x, Origin.y);
7608 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7609 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7613 else
7615 /* According to MSDN for non-LVS_OWNERDATA this is just
7616 * a performance issue. The control allocates its internal
7617 * data structures for the number of items specified. It
7618 * cuts down on the number of memory allocations. Therefore
7619 * we will just issue a WARN here
7621 WARN("for non-ownerdata performance option not implemented.\n");
7624 return TRUE;
7627 /***
7628 * DESCRIPTION:
7629 * Sets the position of an item.
7631 * PARAMETER(S):
7632 * [I] infoPtr : valid pointer to the listview structure
7633 * [I] nItem : item index
7634 * [I] pt : coordinate
7636 * RETURN:
7637 * SUCCESS : TRUE
7638 * FAILURE : FALSE
7640 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7642 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7643 POINT Origin;
7645 TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7647 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7648 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7650 LISTVIEW_GetOrigin(infoPtr, &Origin);
7652 /* This point value seems to be an undocumented feature.
7653 * The best guess is that it means either at the origin,
7654 * or at true beginning of the list. I will assume the origin. */
7655 if ((pt.x == -1) && (pt.y == -1))
7656 pt = Origin;
7658 if (uView == LVS_ICON)
7660 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7661 pt.y -= ICON_TOP_PADDING;
7663 pt.x -= Origin.x;
7664 pt.y -= Origin.y;
7666 infoPtr->bAutoarrange = FALSE;
7668 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7671 /***
7672 * DESCRIPTION:
7673 * Sets the state of one or many items.
7675 * PARAMETER(S):
7676 * [I] infoPtr : valid pointer to the listview structure
7677 * [I] nItem : item index
7678 * [I] lpLVItem : item or subitem info
7680 * RETURN:
7681 * SUCCESS : TRUE
7682 * FAILURE : FALSE
7684 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7686 BOOL bResult = TRUE;
7687 LVITEMW lvItem;
7689 lvItem.iItem = nItem;
7690 lvItem.iSubItem = 0;
7691 lvItem.mask = LVIF_STATE;
7692 lvItem.state = lpLVItem->state;
7693 lvItem.stateMask = lpLVItem->stateMask;
7694 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7696 if (nItem == -1)
7698 /* apply to all items */
7699 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7700 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7702 else
7703 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7706 * Update selection mark
7708 * Investigation on windows 2k showed that selection mark was updated
7709 * whenever a new selection was made, but if the selected item was
7710 * unselected it was not updated.
7712 * we are probably still not 100% accurate, but this at least sets the
7713 * proper selection mark when it is needed
7716 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7717 (infoPtr->nSelectionMark == -1))
7719 int i;
7720 for (i = 0; i < infoPtr->nItemCount; i++)
7722 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7724 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7726 infoPtr->nSelectionMark = i;
7727 break;
7730 else if (ranges_contain(infoPtr->selectionRanges, i))
7732 infoPtr->nSelectionMark = i;
7733 break;
7738 return bResult;
7741 /***
7742 * DESCRIPTION:
7743 * Sets the text of an item or subitem.
7745 * PARAMETER(S):
7746 * [I] hwnd : window handle
7747 * [I] nItem : item index
7748 * [I] lpLVItem : item or subitem info
7749 * [I] isW : TRUE if input is Unicode
7751 * RETURN:
7752 * SUCCESS : TRUE
7753 * FAILURE : FALSE
7755 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7757 LVITEMW lvItem;
7759 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7761 lvItem.iItem = nItem;
7762 lvItem.iSubItem = lpLVItem->iSubItem;
7763 lvItem.mask = LVIF_TEXT;
7764 lvItem.pszText = lpLVItem->pszText;
7765 lvItem.cchTextMax = lpLVItem->cchTextMax;
7767 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7769 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7772 /***
7773 * DESCRIPTION:
7774 * Set item index that marks the start of a multiple selection.
7776 * PARAMETER(S):
7777 * [I] infoPtr : valid pointer to the listview structure
7778 * [I] nIndex : index
7780 * RETURN:
7781 * Index number or -1 if there is no selection mark.
7783 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7785 INT nOldIndex = infoPtr->nSelectionMark;
7787 TRACE("(nIndex=%d)\n", nIndex);
7789 infoPtr->nSelectionMark = nIndex;
7791 return nOldIndex;
7794 /***
7795 * DESCRIPTION:
7796 * Sets the text background color.
7798 * PARAMETER(S):
7799 * [I] infoPtr : valid pointer to the listview structure
7800 * [I] clrTextBk : text background color
7802 * RETURN:
7803 * SUCCESS : TRUE
7804 * FAILURE : FALSE
7806 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7808 TRACE("(clrTextBk=%x)\n", clrTextBk);
7810 if (infoPtr->clrTextBk != clrTextBk)
7812 infoPtr->clrTextBk = clrTextBk;
7813 LISTVIEW_InvalidateList(infoPtr);
7816 return TRUE;
7819 /***
7820 * DESCRIPTION:
7821 * Sets the text foreground color.
7823 * PARAMETER(S):
7824 * [I] infoPtr : valid pointer to the listview structure
7825 * [I] clrText : text color
7827 * RETURN:
7828 * SUCCESS : TRUE
7829 * FAILURE : FALSE
7831 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7833 TRACE("(clrText=%x)\n", clrText);
7835 if (infoPtr->clrText != clrText)
7837 infoPtr->clrText = clrText;
7838 LISTVIEW_InvalidateList(infoPtr);
7841 return TRUE;
7844 /***
7845 * DESCRIPTION:
7846 * Sets new ToolTip window to ListView control.
7848 * PARAMETER(S):
7849 * [I] infoPtr : valid pointer to the listview structure
7850 * [I] hwndNewToolTip : handle to new ToolTip
7852 * RETURN:
7853 * old tool tip
7855 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7857 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7858 infoPtr->hwndToolTip = hwndNewToolTip;
7859 return hwndOldToolTip;
7863 * DESCRIPTION:
7864 * sets the Unicode character format flag for the control
7865 * PARAMETER(S):
7866 * [I] infoPtr :valid pointer to the listview structure
7867 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
7869 * RETURN:
7870 * Old Unicode Format
7872 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL fUnicode)
7874 BOOL rc = infoPtr->notifyFormat;
7875 infoPtr->notifyFormat = (fUnicode)?NFR_UNICODE:NFR_ANSI;
7876 return rc;
7879 /* LISTVIEW_SetWorkAreas */
7881 /***
7882 * DESCRIPTION:
7883 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMS
7885 * PARAMETER(S):
7886 * [I] first : pointer to first ITEM_INFO to compare
7887 * [I] second : pointer to second ITEM_INFO to compare
7888 * [I] lParam : HWND of control
7890 * RETURN:
7891 * if first comes before second : negative
7892 * if first comes after second : positive
7893 * if first and second are equivalent : zero
7895 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7897 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7898 ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
7899 ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
7901 /* Forward the call to the client defined callback */
7902 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7905 /***
7906 * DESCRIPTION:
7907 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMSEX
7909 * PARAMETER(S):
7910 * [I] first : pointer to first ITEM_INFO to compare
7911 * [I] second : pointer to second ITEM_INFO to compare
7912 * [I] lParam : HWND of control
7914 * RETURN:
7915 * if first comes before second : negative
7916 * if first comes after second : positive
7917 * if first and second are equivalent : zero
7919 static INT WINAPI LISTVIEW_CallBackCompareEx(LPVOID first, LPVOID second, LPARAM lParam)
7921 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7922 INT first_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, first );
7923 INT second_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, second );
7925 /* Forward the call to the client defined callback */
7926 return (infoPtr->pfnCompare)( first_idx, second_idx, infoPtr->lParamSort );
7929 /***
7930 * DESCRIPTION:
7931 * Sorts the listview items.
7933 * PARAMETER(S):
7934 * [I] infoPtr : valid pointer to the listview structure
7935 * [I] pfnCompare : application-defined value
7936 * [I] lParamSort : pointer to comparison callback
7937 * [I] IsEx : TRUE when LVM_SORTITEMSEX used
7939 * RETURN:
7940 * SUCCESS : TRUE
7941 * FAILURE : FALSE
7943 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare,
7944 LPARAM lParamSort, BOOL IsEx)
7946 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7947 HDPA hdpaSubItems;
7948 ITEM_INFO *lpItem;
7949 LPVOID selectionMarkItem = NULL;
7950 LPVOID focusedItem = NULL;
7951 int i;
7953 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7955 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7957 if (!pfnCompare) return FALSE;
7958 if (!infoPtr->hdpaItems) return FALSE;
7960 /* if there are 0 or 1 items, there is no need to sort */
7961 if (infoPtr->nItemCount < 2) return TRUE;
7963 /* clear selection */
7964 ranges_clear(infoPtr->selectionRanges);
7966 /* save selection mark and focused item */
7967 if (infoPtr->nSelectionMark >= 0)
7968 selectionMarkItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark);
7969 if (infoPtr->nFocusedItem >= 0)
7970 focusedItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7972 infoPtr->pfnCompare = pfnCompare;
7973 infoPtr->lParamSort = lParamSort;
7974 if (IsEx)
7975 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompareEx, (LPARAM)infoPtr);
7976 else
7977 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7979 /* restore selection ranges */
7980 for (i=0; i < infoPtr->nItemCount; i++)
7982 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
7983 lpItem = DPA_GetPtr(hdpaSubItems, 0);
7985 if (lpItem->state & LVIS_SELECTED)
7986 ranges_additem(infoPtr->selectionRanges, i);
7988 /* restore selection mark and focused item */
7989 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7990 infoPtr->nFocusedItem = DPA_GetPtrIndex(infoPtr->hdpaItems, focusedItem);
7992 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7994 /* refresh the display */
7995 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7996 LISTVIEW_InvalidateList(infoPtr);
7998 return TRUE;
8001 /***
8002 * DESCRIPTION:
8003 * Update theme handle after a theme change.
8005 * PARAMETER(S):
8006 * [I] infoPtr : valid pointer to the listview structure
8008 * RETURN:
8009 * SUCCESS : 0
8010 * FAILURE : something else
8012 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr)
8014 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8015 CloseThemeData(theme);
8016 OpenThemeData(infoPtr->hwndSelf, themeClass);
8017 return 0;
8020 /***
8021 * DESCRIPTION:
8022 * Updates an items or rearranges the listview control.
8024 * PARAMETER(S):
8025 * [I] infoPtr : valid pointer to the listview structure
8026 * [I] nItem : item index
8028 * RETURN:
8029 * SUCCESS : TRUE
8030 * FAILURE : FALSE
8032 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
8034 TRACE("(nItem=%d)\n", nItem);
8036 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
8038 /* rearrange with default alignment style */
8039 if (is_autoarrange(infoPtr))
8040 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8041 else
8042 LISTVIEW_InvalidateItem(infoPtr, nItem);
8044 return TRUE;
8047 /***
8048 * DESCRIPTION:
8049 * Draw the track line at the place defined in the infoPtr structure.
8050 * The line is drawn with a XOR pen so drawing the line for the second time
8051 * in the same place erases the line.
8053 * PARAMETER(S):
8054 * [I] infoPtr : valid pointer to the listview structure
8056 * RETURN:
8057 * SUCCESS : TRUE
8058 * FAILURE : FALSE
8060 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
8062 HPEN hOldPen;
8063 HDC hdc;
8064 INT oldROP;
8066 if (infoPtr->xTrackLine == -1)
8067 return FALSE;
8069 if (!(hdc = GetDC(infoPtr->hwndSelf)))
8070 return FALSE;
8071 hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
8072 oldROP = SetROP2(hdc, R2_XORPEN);
8073 MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
8074 LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
8075 SetROP2(hdc, oldROP);
8076 SelectObject(hdc, hOldPen);
8077 ReleaseDC(infoPtr->hwndSelf, hdc);
8078 return TRUE;
8081 /***
8082 * DESCRIPTION:
8083 * Called when an edit control should be displayed. This function is called after
8084 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
8086 * PARAMETER(S):
8087 * [I] hwnd : Handle to the listview
8088 * [I] uMsg : WM_TIMER (ignored)
8089 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
8090 * [I] dwTimer : The elapsed time (ignored)
8092 * RETURN:
8093 * None.
8095 static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
8097 DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
8098 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8100 KillTimer(hwnd, idEvent);
8101 editItem->fEnabled = FALSE;
8102 /* check if the item is still selected */
8103 if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
8104 LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
8107 /***
8108 * DESCRIPTION:
8109 * Creates the listview control - the WM_NCCREATE phase.
8111 * PARAMETER(S):
8112 * [I] hwnd : window handle
8113 * [I] lpcs : the create parameters
8115 * RETURN:
8116 * Success: TRUE
8117 * Failure: FALSE
8119 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
8121 LISTVIEW_INFO *infoPtr;
8122 LOGFONTW logFont;
8124 TRACE("(lpcs=%p)\n", lpcs);
8126 /* initialize info pointer */
8127 infoPtr = Alloc(sizeof(LISTVIEW_INFO));
8128 if (!infoPtr) return FALSE;
8130 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
8132 infoPtr->hwndSelf = hwnd;
8133 infoPtr->dwStyle = lpcs->style; /* Note: may be changed in WM_CREATE */
8134 /* determine the type of structures to use */
8135 infoPtr->hwndNotify = lpcs->hwndParent;
8136 /* infoPtr->notifyFormat will be filled in WM_CREATE */
8138 /* initialize color information */
8139 infoPtr->clrBk = CLR_NONE;
8140 infoPtr->clrText = CLR_DEFAULT;
8141 infoPtr->clrTextBk = CLR_DEFAULT;
8142 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
8144 /* set default values */
8145 infoPtr->nFocusedItem = -1;
8146 infoPtr->nSelectionMark = -1;
8147 infoPtr->nHotItem = -1;
8148 infoPtr->bRedraw = TRUE;
8149 infoPtr->bNoItemMetrics = TRUE;
8150 infoPtr->bDoChangeNotify = TRUE;
8151 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
8152 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
8153 infoPtr->nEditLabelItem = -1;
8154 infoPtr->dwHoverTime = -1; /* default system hover time */
8155 infoPtr->nMeasureItemHeight = 0;
8156 infoPtr->xTrackLine = -1; /* no track line */
8157 infoPtr->itemEdit.fEnabled = FALSE;
8158 infoPtr->iVersion = COMCTL32_VERSION;
8160 /* get default font (icon title) */
8161 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
8162 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
8163 infoPtr->hFont = infoPtr->hDefaultFont;
8164 LISTVIEW_SaveTextMetrics(infoPtr);
8166 /* allocate memory for the data structure */
8167 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
8168 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
8169 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
8170 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
8171 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
8172 return TRUE;
8174 fail:
8175 DestroyWindow(infoPtr->hwndHeader);
8176 ranges_destroy(infoPtr->selectionRanges);
8177 DPA_Destroy(infoPtr->hdpaItems);
8178 DPA_Destroy(infoPtr->hdpaPosX);
8179 DPA_Destroy(infoPtr->hdpaPosY);
8180 DPA_Destroy(infoPtr->hdpaColumns);
8181 Free(infoPtr);
8182 return FALSE;
8185 /***
8186 * DESCRIPTION:
8187 * Creates the listview control - the WM_CREATE phase. Most of the data is
8188 * already set up in LISTVIEW_NCCreate
8190 * PARAMETER(S):
8191 * [I] hwnd : window handle
8192 * [I] lpcs : the create parameters
8194 * RETURN:
8195 * Success: 0
8196 * Failure: -1
8198 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
8200 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8201 UINT uView = lpcs->style & LVS_TYPEMASK;
8203 TRACE("(lpcs=%p)\n", lpcs);
8205 infoPtr->dwStyle = lpcs->style;
8206 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
8207 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
8209 if ((uView == LVS_REPORT) && (lpcs->style & WS_VISIBLE))
8211 if (LISTVIEW_CreateHeader(infoPtr) < 0) return -1;
8213 else
8214 infoPtr->hwndHeader = 0;
8216 /* init item size to avoid division by 0 */
8217 LISTVIEW_UpdateItemSize (infoPtr);
8219 if (uView == LVS_REPORT)
8221 if (!(LVS_NOCOLUMNHEADER & lpcs->style) && (WS_VISIBLE & lpcs->style))
8223 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8225 LISTVIEW_UpdateScroll(infoPtr);
8228 OpenThemeData(hwnd, themeClass);
8230 /* initialize the icon sizes */
8231 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
8232 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
8233 return 0;
8236 /***
8237 * DESCRIPTION:
8238 * Destroys the listview control.
8240 * PARAMETER(S):
8241 * [I] infoPtr : valid pointer to the listview structure
8243 * RETURN:
8244 * Success: 0
8245 * Failure: -1
8247 static LRESULT LISTVIEW_Destroy(const LISTVIEW_INFO *infoPtr)
8249 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8250 CloseThemeData(theme);
8251 return 0;
8254 /***
8255 * DESCRIPTION:
8256 * Enables the listview control.
8258 * PARAMETER(S):
8259 * [I] infoPtr : valid pointer to the listview structure
8260 * [I] bEnable : specifies whether to enable or disable the window
8262 * RETURN:
8263 * SUCCESS : TRUE
8264 * FAILURE : FALSE
8266 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr, BOOL bEnable)
8268 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
8269 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8270 return TRUE;
8273 /***
8274 * DESCRIPTION:
8275 * Erases the background of the listview control.
8277 * PARAMETER(S):
8278 * [I] infoPtr : valid pointer to the listview structure
8279 * [I] hdc : device context handle
8281 * RETURN:
8282 * SUCCESS : TRUE
8283 * FAILURE : FALSE
8285 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
8287 RECT rc;
8289 TRACE("(hdc=%p)\n", hdc);
8291 if (!GetClipBox(hdc, &rc)) return FALSE;
8293 /* for double buffered controls we need to do this during refresh */
8294 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
8296 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
8300 /***
8301 * DESCRIPTION:
8302 * Helper function for LISTVIEW_[HV]Scroll *only*.
8303 * Performs vertical/horizontal scrolling by a give amount.
8305 * PARAMETER(S):
8306 * [I] infoPtr : valid pointer to the listview structure
8307 * [I] dx : amount of horizontal scroll
8308 * [I] dy : amount of vertical scroll
8310 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
8312 /* now we can scroll the list */
8313 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
8314 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
8315 /* if we have focus, adjust rect */
8316 OffsetRect(&infoPtr->rcFocus, dx, dy);
8317 UpdateWindow(infoPtr->hwndSelf);
8320 /***
8321 * DESCRIPTION:
8322 * Performs vertical scrolling.
8324 * PARAMETER(S):
8325 * [I] infoPtr : valid pointer to the listview structure
8326 * [I] nScrollCode : scroll code
8327 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8328 * [I] hScrollWnd : scrollbar control window handle
8330 * RETURN:
8331 * Zero
8333 * NOTES:
8334 * SB_LINEUP/SB_LINEDOWN:
8335 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8336 * for LVS_REPORT is 1 line
8337 * for LVS_LIST cannot occur
8340 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8341 INT nScrollDiff, HWND hScrollWnd)
8343 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8344 INT nOldScrollPos, nNewScrollPos;
8345 SCROLLINFO scrollInfo;
8346 BOOL is_an_icon;
8348 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8349 debugscrollcode(nScrollCode), nScrollDiff);
8351 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8353 scrollInfo.cbSize = sizeof(SCROLLINFO);
8354 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8356 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
8358 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
8360 nOldScrollPos = scrollInfo.nPos;
8361 switch (nScrollCode)
8363 case SB_INTERNAL:
8364 break;
8366 case SB_LINEUP:
8367 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
8368 break;
8370 case SB_LINEDOWN:
8371 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
8372 break;
8374 case SB_PAGEUP:
8375 nScrollDiff = -scrollInfo.nPage;
8376 break;
8378 case SB_PAGEDOWN:
8379 nScrollDiff = scrollInfo.nPage;
8380 break;
8382 case SB_THUMBPOSITION:
8383 case SB_THUMBTRACK:
8384 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8385 break;
8387 default:
8388 nScrollDiff = 0;
8391 /* quit right away if pos isn't changing */
8392 if (nScrollDiff == 0) return 0;
8394 /* calculate new position, and handle overflows */
8395 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8396 if (nScrollDiff > 0) {
8397 if (nNewScrollPos < nOldScrollPos ||
8398 nNewScrollPos > scrollInfo.nMax)
8399 nNewScrollPos = scrollInfo.nMax;
8400 } else {
8401 if (nNewScrollPos > nOldScrollPos ||
8402 nNewScrollPos < scrollInfo.nMin)
8403 nNewScrollPos = scrollInfo.nMin;
8406 /* set the new position, and reread in case it changed */
8407 scrollInfo.fMask = SIF_POS;
8408 scrollInfo.nPos = nNewScrollPos;
8409 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
8411 /* carry on only if it really changed */
8412 if (nNewScrollPos == nOldScrollPos) return 0;
8414 /* now adjust to client coordinates */
8415 nScrollDiff = nOldScrollPos - nNewScrollPos;
8416 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
8418 /* and scroll the window */
8419 scroll_list(infoPtr, 0, nScrollDiff);
8421 return 0;
8424 /***
8425 * DESCRIPTION:
8426 * Performs horizontal scrolling.
8428 * PARAMETER(S):
8429 * [I] infoPtr : valid pointer to the listview structure
8430 * [I] nScrollCode : scroll code
8431 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8432 * [I] hScrollWnd : scrollbar control window handle
8434 * RETURN:
8435 * Zero
8437 * NOTES:
8438 * SB_LINELEFT/SB_LINERIGHT:
8439 * for LVS_ICON, LVS_SMALLICON 1 pixel
8440 * for LVS_REPORT is 1 pixel
8441 * for LVS_LIST is 1 column --> which is a 1 because the
8442 * scroll is based on columns not pixels
8445 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8446 INT nScrollDiff, HWND hScrollWnd)
8448 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8449 INT nOldScrollPos, nNewScrollPos;
8450 SCROLLINFO scrollInfo;
8452 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8453 debugscrollcode(nScrollCode), nScrollDiff);
8455 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8457 scrollInfo.cbSize = sizeof(SCROLLINFO);
8458 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8460 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
8462 nOldScrollPos = scrollInfo.nPos;
8464 switch (nScrollCode)
8466 case SB_INTERNAL:
8467 break;
8469 case SB_LINELEFT:
8470 nScrollDiff = -1;
8471 break;
8473 case SB_LINERIGHT:
8474 nScrollDiff = 1;
8475 break;
8477 case SB_PAGELEFT:
8478 nScrollDiff = -scrollInfo.nPage;
8479 break;
8481 case SB_PAGERIGHT:
8482 nScrollDiff = scrollInfo.nPage;
8483 break;
8485 case SB_THUMBPOSITION:
8486 case SB_THUMBTRACK:
8487 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8488 break;
8490 default:
8491 nScrollDiff = 0;
8494 /* quit right away if pos isn't changing */
8495 if (nScrollDiff == 0) return 0;
8497 /* calculate new position, and handle overflows */
8498 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8499 if (nScrollDiff > 0) {
8500 if (nNewScrollPos < nOldScrollPos ||
8501 nNewScrollPos > scrollInfo.nMax)
8502 nNewScrollPos = scrollInfo.nMax;
8503 } else {
8504 if (nNewScrollPos > nOldScrollPos ||
8505 nNewScrollPos < scrollInfo.nMin)
8506 nNewScrollPos = scrollInfo.nMin;
8509 /* set the new position, and reread in case it changed */
8510 scrollInfo.fMask = SIF_POS;
8511 scrollInfo.nPos = nNewScrollPos;
8512 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
8514 /* carry on only if it really changed */
8515 if (nNewScrollPos == nOldScrollPos) return 0;
8517 if(uView == LVS_REPORT)
8518 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
8520 /* now adjust to client coordinates */
8521 nScrollDiff = nOldScrollPos - nNewScrollPos;
8522 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
8524 /* and scroll the window */
8525 scroll_list(infoPtr, nScrollDiff, 0);
8527 return 0;
8530 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
8532 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8533 INT gcWheelDelta = 0;
8534 INT pulScrollLines = 3;
8535 SCROLLINFO scrollInfo;
8537 TRACE("(wheelDelta=%d)\n", wheelDelta);
8539 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8540 gcWheelDelta -= wheelDelta;
8542 scrollInfo.cbSize = sizeof(SCROLLINFO);
8543 scrollInfo.fMask = SIF_POS;
8545 switch(uView)
8547 case LVS_ICON:
8548 case LVS_SMALLICON:
8550 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8551 * should be fixed in the future.
8553 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
8554 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8555 break;
8557 case LVS_REPORT:
8558 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8560 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
8561 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8562 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
8564 break;
8566 case LVS_LIST:
8567 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8568 break;
8570 return 0;
8573 /***
8574 * DESCRIPTION:
8575 * ???
8577 * PARAMETER(S):
8578 * [I] infoPtr : valid pointer to the listview structure
8579 * [I] nVirtualKey : virtual key
8580 * [I] lKeyData : key data
8582 * RETURN:
8583 * Zero
8585 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8587 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8588 HWND hwndSelf = infoPtr->hwndSelf;
8589 INT nItem = -1;
8590 NMLVKEYDOWN nmKeyDown;
8592 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
8594 /* send LVN_KEYDOWN notification */
8595 nmKeyDown.wVKey = nVirtualKey;
8596 nmKeyDown.flags = 0;
8597 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8598 if (!IsWindow(hwndSelf))
8599 return 0;
8601 switch (nVirtualKey)
8603 case VK_SPACE:
8604 nItem = infoPtr->nFocusedItem;
8605 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
8606 toggle_checkbox_state(infoPtr, infoPtr->nFocusedItem);
8607 break;
8609 case VK_RETURN:
8610 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8612 if (!notify(infoPtr, NM_RETURN)) return 0;
8613 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8615 break;
8617 case VK_HOME:
8618 if (infoPtr->nItemCount > 0)
8619 nItem = 0;
8620 break;
8622 case VK_END:
8623 if (infoPtr->nItemCount > 0)
8624 nItem = infoPtr->nItemCount - 1;
8625 break;
8627 case VK_LEFT:
8628 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8629 break;
8631 case VK_UP:
8632 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8633 break;
8635 case VK_RIGHT:
8636 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8637 break;
8639 case VK_DOWN:
8640 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8641 break;
8643 case VK_PRIOR:
8644 if (uView == LVS_REPORT)
8646 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8647 if (infoPtr->nFocusedItem == topidx)
8648 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8649 else
8650 nItem = topidx;
8652 else
8653 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8654 * LISTVIEW_GetCountPerRow(infoPtr);
8655 if(nItem < 0) nItem = 0;
8656 break;
8658 case VK_NEXT:
8659 if (uView == LVS_REPORT)
8661 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8662 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8663 if (infoPtr->nFocusedItem == topidx + cnt - 1)
8664 nItem = infoPtr->nFocusedItem + cnt - 1;
8665 else
8666 nItem = topidx + cnt - 1;
8668 else
8669 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8670 * LISTVIEW_GetCountPerRow(infoPtr);
8671 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8672 break;
8675 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8676 LISTVIEW_KeySelection(infoPtr, nItem, nVirtualKey == VK_SPACE);
8678 return 0;
8681 /***
8682 * DESCRIPTION:
8683 * Kills the focus.
8685 * PARAMETER(S):
8686 * [I] infoPtr : valid pointer to the listview structure
8688 * RETURN:
8689 * Zero
8691 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8693 TRACE("()\n");
8695 /* if we did not have the focus, there's nothing to do */
8696 if (!infoPtr->bFocus) return 0;
8698 /* send NM_KILLFOCUS notification */
8699 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8701 /* if we have a focus rectangle, get rid of it */
8702 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8704 /* set window focus flag */
8705 infoPtr->bFocus = FALSE;
8707 /* invalidate the selected items before resetting focus flag */
8708 LISTVIEW_InvalidateSelectedItems(infoPtr);
8710 return 0;
8713 /***
8714 * DESCRIPTION:
8715 * Processes double click messages (left mouse button).
8717 * PARAMETER(S):
8718 * [I] infoPtr : valid pointer to the listview structure
8719 * [I] wKey : key flag
8720 * [I] x,y : mouse coordinate
8722 * RETURN:
8723 * Zero
8725 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8727 LVHITTESTINFO htInfo;
8729 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8731 /* Cancel the item edition if any */
8732 if (infoPtr->itemEdit.fEnabled)
8734 KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
8735 infoPtr->itemEdit.fEnabled = FALSE;
8738 /* send NM_RELEASEDCAPTURE notification */
8739 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8741 htInfo.pt.x = x;
8742 htInfo.pt.y = y;
8744 /* send NM_DBLCLK notification */
8745 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8746 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8748 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8749 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8751 return 0;
8754 /***
8755 * DESCRIPTION:
8756 * Processes mouse down messages (left mouse button).
8758 * PARAMETERS:
8759 * infoPtr [I ] valid pointer to the listview structure
8760 * wKey [I ] key flag
8761 * x,y [I ] mouse coordinate
8763 * RETURN:
8764 * Zero
8766 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8768 LVHITTESTINFO lvHitTestInfo;
8769 static BOOL bGroupSelect = TRUE;
8770 POINT pt = { x, y };
8771 INT nItem;
8773 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8775 /* send NM_RELEASEDCAPTURE notification */
8776 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8778 /* set left button down flag and record the click position */
8779 infoPtr->bLButtonDown = TRUE;
8780 infoPtr->ptClickPos = pt;
8781 infoPtr->bDragging = FALSE;
8783 lvHitTestInfo.pt.x = x;
8784 lvHitTestInfo.pt.y = y;
8786 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8787 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
8788 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8790 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8792 toggle_checkbox_state(infoPtr, nItem);
8793 return 0;
8796 if (infoPtr->dwStyle & LVS_SINGLESEL)
8798 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8799 infoPtr->nEditLabelItem = nItem;
8800 else
8801 LISTVIEW_SetSelection(infoPtr, nItem);
8803 else
8805 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8807 if (bGroupSelect)
8809 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8810 LISTVIEW_SetItemFocus(infoPtr, nItem);
8811 infoPtr->nSelectionMark = nItem;
8813 else
8815 LVITEMW item;
8817 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8818 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8820 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8821 infoPtr->nSelectionMark = nItem;
8824 else if (wKey & MK_CONTROL)
8826 LVITEMW item;
8828 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8830 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8831 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8832 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8833 infoPtr->nSelectionMark = nItem;
8835 else if (wKey & MK_SHIFT)
8837 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8839 else
8841 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8842 infoPtr->nEditLabelItem = nItem;
8844 /* set selection (clears other pre-existing selections) */
8845 LISTVIEW_SetSelection(infoPtr, nItem);
8849 if (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE)
8850 if(lvHitTestInfo.iItem != -1) notify_itemactivate(infoPtr,&lvHitTestInfo);
8852 else
8854 /* remove all selections */
8855 LISTVIEW_DeselectAll(infoPtr);
8856 ReleaseCapture();
8859 return 0;
8862 /***
8863 * DESCRIPTION:
8864 * Processes mouse up messages (left mouse button).
8866 * PARAMETERS:
8867 * infoPtr [I ] valid pointer to the listview structure
8868 * wKey [I ] key flag
8869 * x,y [I ] mouse coordinate
8871 * RETURN:
8872 * Zero
8874 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8876 LVHITTESTINFO lvHitTestInfo;
8878 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8880 if (!infoPtr->bLButtonDown) return 0;
8882 lvHitTestInfo.pt.x = x;
8883 lvHitTestInfo.pt.y = y;
8885 /* send NM_CLICK notification */
8886 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8887 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8889 /* set left button flag */
8890 infoPtr->bLButtonDown = FALSE;
8892 if (infoPtr->bDragging)
8894 infoPtr->bDragging = FALSE;
8895 return 0;
8898 /* if we clicked on a selected item, edit the label */
8899 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8901 /* we want to make sure the user doesn't want to do a double click. So we will
8902 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
8904 infoPtr->itemEdit.fEnabled = TRUE;
8905 infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
8906 SetTimer(infoPtr->hwndSelf,
8907 (UINT_PTR)&infoPtr->itemEdit,
8908 GetDoubleClickTime(),
8909 LISTVIEW_DelayedEditItem);
8912 if (!infoPtr->bFocus)
8913 SetFocus(infoPtr->hwndSelf);
8915 return 0;
8918 /***
8919 * DESCRIPTION:
8920 * Destroys the listview control (called after WM_DESTROY).
8922 * PARAMETER(S):
8923 * [I] infoPtr : valid pointer to the listview structure
8925 * RETURN:
8926 * Zero
8928 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8930 TRACE("()\n");
8932 /* delete all items */
8933 LISTVIEW_DeleteAllItems(infoPtr, TRUE);
8935 /* destroy data structure */
8936 DPA_Destroy(infoPtr->hdpaItems);
8937 DPA_Destroy(infoPtr->hdpaPosX);
8938 DPA_Destroy(infoPtr->hdpaPosY);
8939 DPA_Destroy(infoPtr->hdpaColumns);
8940 ranges_destroy(infoPtr->selectionRanges);
8942 /* destroy image lists */
8943 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8945 if (infoPtr->himlNormal)
8946 ImageList_Destroy(infoPtr->himlNormal);
8947 if (infoPtr->himlSmall)
8948 ImageList_Destroy(infoPtr->himlSmall);
8949 if (infoPtr->himlState)
8950 ImageList_Destroy(infoPtr->himlState);
8953 /* destroy font, bkgnd brush */
8954 infoPtr->hFont = 0;
8955 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8956 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8958 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8960 /* free listview info pointer*/
8961 Free(infoPtr);
8963 return 0;
8966 /***
8967 * DESCRIPTION:
8968 * Handles notifications from header.
8970 * PARAMETER(S):
8971 * [I] infoPtr : valid pointer to the listview structure
8972 * [I] nCtrlId : control identifier
8973 * [I] lpnmh : notification information
8975 * RETURN:
8976 * Zero
8978 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8980 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8981 HWND hwndSelf = infoPtr->hwndSelf;
8983 TRACE("(lpnmh=%p)\n", lpnmh);
8985 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8987 switch (lpnmh->hdr.code)
8989 case HDN_TRACKW:
8990 case HDN_TRACKA:
8992 COLUMN_INFO *lpColumnInfo;
8993 POINT ptOrigin;
8994 INT x;
8996 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8997 break;
8999 /* remove the old line (if any) */
9000 LISTVIEW_DrawTrackLine(infoPtr);
9002 /* compute & draw the new line */
9003 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
9004 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
9005 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
9006 infoPtr->xTrackLine = x + ptOrigin.x;
9007 LISTVIEW_DrawTrackLine(infoPtr);
9008 break;
9011 case HDN_ENDTRACKA:
9012 case HDN_ENDTRACKW:
9013 /* remove the track line (if any) */
9014 LISTVIEW_DrawTrackLine(infoPtr);
9015 infoPtr->xTrackLine = -1;
9016 break;
9018 case HDN_ENDDRAG:
9019 FIXME("Changing column order not implemented\n");
9020 return TRUE;
9022 case HDN_ITEMCHANGINGW:
9023 case HDN_ITEMCHANGINGA:
9024 return notify_forward_header(infoPtr, lpnmh);
9026 case HDN_ITEMCHANGEDW:
9027 case HDN_ITEMCHANGEDA:
9029 COLUMN_INFO *lpColumnInfo;
9030 INT dx, cxy;
9032 notify_forward_header(infoPtr, lpnmh);
9033 if (!IsWindow(hwndSelf))
9034 break;
9036 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
9038 HDITEMW hdi;
9040 hdi.mask = HDI_WIDTH;
9041 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, &hdi)) return 0;
9042 cxy = hdi.cxy;
9044 else
9045 cxy = lpnmh->pitem->cxy;
9047 /* determine how much we change since the last know position */
9048 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
9049 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
9050 if (dx != 0)
9052 lpColumnInfo->rcHeader.right += dx;
9053 if (lpnmh->iItem + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
9054 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
9055 else
9057 /* only needs to update the scrolls */
9058 infoPtr->nItemWidth += dx;
9059 LISTVIEW_UpdateScroll(infoPtr);
9061 LISTVIEW_UpdateItemSize(infoPtr);
9062 if (uView == LVS_REPORT && is_redrawing(infoPtr))
9064 POINT ptOrigin;
9065 RECT rcCol = lpColumnInfo->rcHeader;
9067 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
9068 OffsetRect(&rcCol, ptOrigin.x, 0);
9070 rcCol.top = infoPtr->rcList.top;
9071 rcCol.bottom = infoPtr->rcList.bottom;
9073 /* resizing left-aligned columns leaves most of the left side untouched */
9074 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
9076 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
9077 if (dx > 0)
9078 nMaxDirty += dx;
9079 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
9082 /* when shrinking the last column clear the now unused field */
9083 if (lpnmh->iItem == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
9085 RECT right;
9087 rcCol.right -= dx;
9089 /* deal with right from rightmost column area */
9090 right.left = rcCol.right;
9091 right.top = rcCol.top;
9092 right.bottom = rcCol.bottom;
9093 right.right = infoPtr->rcList.right;
9095 LISTVIEW_InvalidateRect(infoPtr, &right);
9098 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
9102 break;
9104 case HDN_ITEMCLICKW:
9105 case HDN_ITEMCLICKA:
9107 /* Handle sorting by Header Column */
9108 NMLISTVIEW nmlv;
9110 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
9111 nmlv.iItem = -1;
9112 nmlv.iSubItem = lpnmh->iItem;
9113 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
9114 notify_forward_header(infoPtr, lpnmh);
9116 break;
9118 case HDN_DIVIDERDBLCLICKW:
9119 case HDN_DIVIDERDBLCLICKA:
9120 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
9121 break;
9124 return 0;
9127 /***
9128 * DESCRIPTION:
9129 * Paint non-client area of control.
9131 * PARAMETER(S):
9132 * [I] infoPtr : valid pointer to the listview structureof the sender
9133 * [I] region : update region
9135 * RETURN:
9136 * TRUE - frame was painted
9137 * FALSE - call default window proc
9139 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
9141 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
9142 HDC dc;
9143 RECT r;
9144 HRGN cliprgn;
9145 int cxEdge = GetSystemMetrics (SM_CXEDGE),
9146 cyEdge = GetSystemMetrics (SM_CYEDGE);
9148 if (!theme) return FALSE;
9150 GetWindowRect(infoPtr->hwndSelf, &r);
9152 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
9153 r.right - cxEdge, r.bottom - cyEdge);
9154 if (region != (HRGN)1)
9155 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
9156 OffsetRect(&r, -r.left, -r.top);
9158 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
9159 OffsetRect(&r, -r.left, -r.top);
9161 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
9162 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
9163 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
9164 ReleaseDC(infoPtr->hwndSelf, dc);
9166 /* Call default proc to get the scrollbars etc. painted */
9167 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
9169 return TRUE;
9172 /***
9173 * DESCRIPTION:
9174 * Determines the type of structure to use.
9176 * PARAMETER(S):
9177 * [I] infoPtr : valid pointer to the listview structureof the sender
9178 * [I] hwndFrom : listview window handle
9179 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
9181 * RETURN:
9182 * Zero
9184 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
9186 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
9188 if (nCommand == NF_REQUERY)
9189 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
9191 return infoPtr->notifyFormat;
9194 /***
9195 * DESCRIPTION:
9196 * Paints/Repaints the listview control.
9198 * PARAMETER(S):
9199 * [I] infoPtr : valid pointer to the listview structure
9200 * [I] hdc : device context handle
9202 * RETURN:
9203 * Zero
9205 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
9207 TRACE("(hdc=%p)\n", hdc);
9209 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
9211 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9213 infoPtr->bNoItemMetrics = FALSE;
9214 LISTVIEW_UpdateItemSize(infoPtr);
9215 if (uView == LVS_ICON || uView == LVS_SMALLICON)
9216 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9217 LISTVIEW_UpdateScroll(infoPtr);
9220 if (infoPtr->hwndHeader) UpdateWindow(infoPtr->hwndHeader);
9222 if (hdc)
9223 LISTVIEW_Refresh(infoPtr, hdc, NULL);
9224 else
9226 PAINTSTRUCT ps;
9228 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
9229 if (!hdc) return 1;
9230 LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
9231 EndPaint(infoPtr->hwndSelf, &ps);
9234 return 0;
9238 /***
9239 * DESCRIPTION:
9240 * Paints/Repaints the listview control.
9242 * PARAMETER(S):
9243 * [I] infoPtr : valid pointer to the listview structure
9244 * [I] hdc : device context handle
9245 * [I] options : drawing options
9247 * RETURN:
9248 * Zero
9250 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
9252 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
9254 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
9255 return 0;
9257 if (options & PRF_ERASEBKGND)
9258 LISTVIEW_EraseBkgnd(infoPtr, hdc);
9260 if (options & PRF_CLIENT)
9261 LISTVIEW_Paint(infoPtr, hdc);
9263 return 0;
9267 /***
9268 * DESCRIPTION:
9269 * Processes double click messages (right mouse button).
9271 * PARAMETER(S):
9272 * [I] infoPtr : valid pointer to the listview structure
9273 * [I] wKey : key flag
9274 * [I] x,y : mouse coordinate
9276 * RETURN:
9277 * Zero
9279 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9281 LVHITTESTINFO lvHitTestInfo;
9283 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9285 /* send NM_RELEASEDCAPTURE notification */
9286 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9288 /* send NM_RDBLCLK notification */
9289 lvHitTestInfo.pt.x = x;
9290 lvHitTestInfo.pt.y = y;
9291 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9292 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
9294 return 0;
9297 /***
9298 * DESCRIPTION:
9299 * Processes mouse down messages (right mouse button).
9301 * PARAMETER(S):
9302 * [I] infoPtr : valid pointer to the listview structure
9303 * [I] wKey : key flag
9304 * [I] x,y : mouse coordinate
9306 * RETURN:
9307 * Zero
9309 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9311 LVHITTESTINFO lvHitTestInfo;
9312 INT nItem;
9314 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9316 /* send NM_RELEASEDCAPTURE notification */
9317 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9319 /* make sure the listview control window has the focus */
9320 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
9322 /* set right button down flag */
9323 infoPtr->bRButtonDown = TRUE;
9325 /* determine the index of the selected item */
9326 lvHitTestInfo.pt.x = x;
9327 lvHitTestInfo.pt.y = y;
9328 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
9330 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9332 LISTVIEW_SetItemFocus(infoPtr, nItem);
9333 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9334 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9335 LISTVIEW_SetSelection(infoPtr, nItem);
9337 else
9339 LISTVIEW_DeselectAll(infoPtr);
9342 return 0;
9345 /***
9346 * DESCRIPTION:
9347 * Processes mouse up messages (right mouse button).
9349 * PARAMETER(S):
9350 * [I] infoPtr : valid pointer to the listview structure
9351 * [I] wKey : key flag
9352 * [I] x,y : mouse coordinate
9354 * RETURN:
9355 * Zero
9357 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9359 LVHITTESTINFO lvHitTestInfo;
9360 POINT pt;
9362 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9364 if (!infoPtr->bRButtonDown) return 0;
9366 /* set button flag */
9367 infoPtr->bRButtonDown = FALSE;
9369 /* Send NM_RCLICK notification */
9370 lvHitTestInfo.pt.x = x;
9371 lvHitTestInfo.pt.y = y;
9372 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9373 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
9375 /* Change to screen coordinate for WM_CONTEXTMENU */
9376 pt = lvHitTestInfo.pt;
9377 ClientToScreen(infoPtr->hwndSelf, &pt);
9379 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9380 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
9381 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
9383 return 0;
9387 /***
9388 * DESCRIPTION:
9389 * Sets the cursor.
9391 * PARAMETER(S):
9392 * [I] infoPtr : valid pointer to the listview structure
9393 * [I] hwnd : window handle of window containing the cursor
9394 * [I] nHittest : hit-test code
9395 * [I] wMouseMsg : ideintifier of the mouse message
9397 * RETURN:
9398 * TRUE if cursor is set
9399 * FALSE otherwise
9401 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
9403 LVHITTESTINFO lvHitTestInfo;
9405 if(!(LISTVIEW_isHotTracking(infoPtr))) return FALSE;
9407 if(!infoPtr->hHotCursor) return FALSE;
9409 GetCursorPos(&lvHitTestInfo.pt);
9410 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
9412 SetCursor(infoPtr->hHotCursor);
9414 return TRUE;
9417 /***
9418 * DESCRIPTION:
9419 * Sets the focus.
9421 * PARAMETER(S):
9422 * [I] infoPtr : valid pointer to the listview structure
9423 * [I] hwndLoseFocus : handle of previously focused window
9425 * RETURN:
9426 * Zero
9428 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
9430 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
9432 /* if we have the focus already, there's nothing to do */
9433 if (infoPtr->bFocus) return 0;
9435 /* send NM_SETFOCUS notification */
9436 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
9438 /* set window focus flag */
9439 infoPtr->bFocus = TRUE;
9441 /* put the focus rect back on */
9442 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
9444 /* redraw all visible selected items */
9445 LISTVIEW_InvalidateSelectedItems(infoPtr);
9447 return 0;
9450 /***
9451 * DESCRIPTION:
9452 * Sets the font.
9454 * PARAMETER(S):
9455 * [I] infoPtr : valid pointer to the listview structure
9456 * [I] fRedraw : font handle
9457 * [I] fRedraw : redraw flag
9459 * RETURN:
9460 * Zero
9462 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
9464 HFONT oldFont = infoPtr->hFont;
9466 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
9468 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9469 if (infoPtr->hFont == oldFont) return 0;
9471 LISTVIEW_SaveTextMetrics(infoPtr);
9473 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
9475 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
9476 LISTVIEW_UpdateSize(infoPtr);
9477 LISTVIEW_UpdateScroll(infoPtr);
9480 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
9482 return 0;
9485 /***
9486 * DESCRIPTION:
9487 * Message handling for WM_SETREDRAW.
9488 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9490 * PARAMETER(S):
9491 * [I] infoPtr : valid pointer to the listview structure
9492 * [I] bRedraw: state of redraw flag
9494 * RETURN:
9495 * DefWinProc return value
9497 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
9499 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
9501 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9502 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
9504 infoPtr->bRedraw = bRedraw;
9506 if(!bRedraw) return 0;
9508 if (is_autoarrange(infoPtr))
9509 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9510 LISTVIEW_UpdateScroll(infoPtr);
9512 /* despite what the WM_SETREDRAW docs says, apps expect us
9513 * to invalidate the listview here... stupid! */
9514 LISTVIEW_InvalidateList(infoPtr);
9516 return 0;
9519 /***
9520 * DESCRIPTION:
9521 * Resizes the listview control. This function processes WM_SIZE
9522 * messages. At this time, the width and height are not used.
9524 * PARAMETER(S):
9525 * [I] infoPtr : valid pointer to the listview structure
9526 * [I] Width : new width
9527 * [I] Height : new height
9529 * RETURN:
9530 * Zero
9532 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
9534 RECT rcOld = infoPtr->rcList;
9536 TRACE("(width=%d, height=%d)\n", Width, Height);
9538 LISTVIEW_UpdateSize(infoPtr);
9539 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
9541 /* do not bother with display related stuff if we're not redrawing */
9542 if (!is_redrawing(infoPtr)) return 0;
9544 if (is_autoarrange(infoPtr))
9545 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9547 LISTVIEW_UpdateScroll(infoPtr);
9549 /* refresh all only for lists whose height changed significantly */
9550 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
9551 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
9552 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
9553 LISTVIEW_InvalidateList(infoPtr);
9555 return 0;
9558 /***
9559 * DESCRIPTION:
9560 * Sets the size information.
9562 * PARAMETER(S):
9563 * [I] infoPtr : valid pointer to the listview structure
9565 * RETURN:
9566 * None
9568 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
9570 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9572 TRACE("uView=%d, rcList(old)=%s\n", uView, wine_dbgstr_rect(&infoPtr->rcList));
9574 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
9576 if (uView == LVS_LIST)
9578 /* Apparently the "LIST" style is supposed to have the same
9579 * number of items in a column even if there is no scroll bar.
9580 * Since if a scroll bar already exists then the bottom is already
9581 * reduced, only reduce if the scroll bar does not currently exist.
9582 * The "2" is there to mimic the native control. I think it may be
9583 * related to either padding or edges. (GLA 7/2002)
9585 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
9586 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
9587 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
9589 else if (uView == LVS_REPORT)
9591 HDLAYOUT hl;
9592 WINDOWPOS wp;
9594 hl.prc = &infoPtr->rcList;
9595 hl.pwpos = &wp;
9596 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9597 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp.flags, wp.x, wp.y, wp.cx, wp.cy);
9598 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy,
9599 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9600 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9601 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy);
9603 infoPtr->rcList.top = max(wp.cy, 0);
9604 infoPtr->rcList.top += (infoPtr->dwLvExStyle & LVS_EX_GRIDLINES) ? 2 : 0;
9607 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
9610 /***
9611 * DESCRIPTION:
9612 * Processes WM_STYLECHANGED messages.
9614 * PARAMETER(S):
9615 * [I] infoPtr : valid pointer to the listview structure
9616 * [I] wStyleType : window style type (normal or extended)
9617 * [I] lpss : window style information
9619 * RETURN:
9620 * Zero
9622 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9623 const STYLESTRUCT *lpss)
9625 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9626 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9627 UINT style;
9629 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9630 wStyleType, lpss->styleOld, lpss->styleNew);
9632 if (wStyleType != GWL_STYLE) return 0;
9634 /* FIXME: if LVS_NOSORTHEADER changed, update header */
9635 /* or LVS_SORT{AS,DES}CENDING */
9637 infoPtr->dwStyle = lpss->styleNew;
9639 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9640 ((lpss->styleNew & WS_HSCROLL) == 0))
9641 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9643 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9644 ((lpss->styleNew & WS_VSCROLL) == 0))
9645 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9647 if (uNewView != uOldView)
9649 SIZE oldIconSize = infoPtr->iconSize;
9650 HIMAGELIST himl;
9652 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9653 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9655 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9656 SetRectEmpty(&infoPtr->rcFocus);
9658 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9659 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
9661 if (uNewView == LVS_ICON)
9663 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
9665 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9666 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9667 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
9670 else if (uNewView == LVS_REPORT)
9672 HDLAYOUT hl;
9673 WINDOWPOS wp;
9675 LISTVIEW_CreateHeader( infoPtr );
9677 hl.prc = &infoPtr->rcList;
9678 hl.pwpos = &wp;
9679 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9680 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
9681 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9682 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9685 LISTVIEW_UpdateItemSize(infoPtr);
9688 if (uNewView == LVS_REPORT)
9690 if ((lpss->styleOld ^ lpss->styleNew) & LVS_NOCOLUMNHEADER)
9692 if (lpss->styleNew & LVS_NOCOLUMNHEADER)
9694 /* Turn off the header control */
9695 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
9696 TRACE("Hide header control, was 0x%08x\n", style);
9697 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style | HDS_HIDDEN);
9698 } else {
9699 /* Turn on the header control */
9700 if ((style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE)) & HDS_HIDDEN)
9702 TRACE("Show header control, was 0x%08x\n", style);
9703 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, (style & ~HDS_HIDDEN) | WS_VISIBLE);
9709 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9710 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9711 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9713 /* update the size of the client area */
9714 LISTVIEW_UpdateSize(infoPtr);
9716 /* add scrollbars if needed */
9717 LISTVIEW_UpdateScroll(infoPtr);
9719 /* invalidate client area + erase background */
9720 LISTVIEW_InvalidateList(infoPtr);
9722 return 0;
9725 /***
9726 * DESCRIPTION:
9727 * Processes WM_STYLECHANGING messages.
9729 * PARAMETER(S):
9730 * [I] infoPtr : valid pointer to the listview structure
9731 * [I] wStyleType : window style type (normal or extended)
9732 * [I0] lpss : window style information
9734 * RETURN:
9735 * Zero
9737 static INT LISTVIEW_StyleChanging(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9738 STYLESTRUCT *lpss)
9740 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9741 wStyleType, lpss->styleOld, lpss->styleNew);
9743 /* don't forward LVS_OWNERDATA only if not already set to */
9744 if ((lpss->styleNew ^ lpss->styleOld) & LVS_OWNERDATA)
9746 if (lpss->styleOld & LVS_OWNERDATA)
9747 lpss->styleNew |= LVS_OWNERDATA;
9748 else
9749 lpss->styleNew &= ~LVS_OWNERDATA;
9752 return 0;
9755 /***
9756 * DESCRIPTION:
9757 * Processes WM_SHOWWINDOW messages.
9759 * PARAMETER(S):
9760 * [I] infoPtr : valid pointer to the listview structure
9761 * [I] bShown : window is being shown (FALSE when hidden)
9762 * [I] iStatus : window show status
9764 * RETURN:
9765 * Zero
9767 static LRESULT LISTVIEW_ShowWindow(LISTVIEW_INFO *infoPtr, BOOL bShown, INT iStatus)
9769 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9771 /* header delayed creation */
9772 if ((uView == LVS_REPORT) && bShown)
9774 LISTVIEW_CreateHeader(infoPtr);
9776 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle))
9777 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9780 return 0;
9783 /***
9784 * DESCRIPTION:
9785 * Processes CCM_GETVERSION messages.
9787 * PARAMETER(S):
9788 * [I] infoPtr : valid pointer to the listview structure
9790 * RETURN:
9791 * Current version
9793 static inline LRESULT LISTVIEW_GetVersion(LISTVIEW_INFO *infoPtr)
9795 return infoPtr->iVersion;
9798 /***
9799 * DESCRIPTION:
9800 * Processes CCM_SETVERSION messages.
9802 * PARAMETER(S):
9803 * [I] infoPtr : valid pointer to the listview structure
9804 * [I] iVersion : version to be set
9806 * RETURN:
9807 * -1 when requested version is greater than DLL version;
9808 * previous version otherwise
9810 static LRESULT LISTVIEW_SetVersion(LISTVIEW_INFO *infoPtr, DWORD iVersion)
9812 INT iOldVersion = infoPtr->iVersion;
9814 if (iVersion > COMCTL32_VERSION)
9815 return -1;
9817 infoPtr->iVersion = iVersion;
9819 TRACE("new version %d\n", iVersion);
9821 return iOldVersion;
9824 /***
9825 * DESCRIPTION:
9826 * Window procedure of the listview control.
9829 static LRESULT WINAPI
9830 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9832 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9834 TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg, wParam, lParam);
9836 if (!infoPtr && (uMsg != WM_NCCREATE))
9837 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9839 switch (uMsg)
9841 case LVM_APPROXIMATEVIEWRECT:
9842 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9843 LOWORD(lParam), HIWORD(lParam));
9844 case LVM_ARRANGE:
9845 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9847 /* case LVM_CANCELEDITLABEL: */
9849 case LVM_CREATEDRAGIMAGE:
9850 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9852 case LVM_DELETEALLITEMS:
9853 return LISTVIEW_DeleteAllItems(infoPtr, FALSE);
9855 case LVM_DELETECOLUMN:
9856 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9858 case LVM_DELETEITEM:
9859 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9861 case LVM_EDITLABELW:
9862 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9864 case LVM_EDITLABELA:
9865 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9867 /* case LVM_ENABLEGROUPVIEW: */
9869 case LVM_ENSUREVISIBLE:
9870 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9872 case LVM_FINDITEMW:
9873 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9875 case LVM_FINDITEMA:
9876 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9878 case LVM_GETBKCOLOR:
9879 return infoPtr->clrBk;
9881 /* case LVM_GETBKIMAGE: */
9883 case LVM_GETCALLBACKMASK:
9884 return infoPtr->uCallbackMask;
9886 case LVM_GETCOLUMNA:
9887 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9889 case LVM_GETCOLUMNW:
9890 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9892 case LVM_GETCOLUMNORDERARRAY:
9893 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9895 case LVM_GETCOLUMNWIDTH:
9896 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9898 case LVM_GETCOUNTPERPAGE:
9899 return LISTVIEW_GetCountPerPage(infoPtr);
9901 case LVM_GETEDITCONTROL:
9902 return (LRESULT)infoPtr->hwndEdit;
9904 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9905 return infoPtr->dwLvExStyle;
9907 /* case LVM_GETGROUPINFO: */
9909 /* case LVM_GETGROUPMETRICS: */
9911 case LVM_GETHEADER:
9912 return (LRESULT)infoPtr->hwndHeader;
9914 case LVM_GETHOTCURSOR:
9915 return (LRESULT)infoPtr->hHotCursor;
9917 case LVM_GETHOTITEM:
9918 return infoPtr->nHotItem;
9920 case LVM_GETHOVERTIME:
9921 return infoPtr->dwHoverTime;
9923 case LVM_GETIMAGELIST:
9924 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9926 /* case LVM_GETINSERTMARK: */
9928 /* case LVM_GETINSERTMARKCOLOR: */
9930 /* case LVM_GETINSERTMARKRECT: */
9932 case LVM_GETISEARCHSTRINGA:
9933 case LVM_GETISEARCHSTRINGW:
9934 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9935 return FALSE;
9937 case LVM_GETITEMA:
9938 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9940 case LVM_GETITEMW:
9941 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9943 case LVM_GETITEMCOUNT:
9944 return infoPtr->nItemCount;
9946 case LVM_GETITEMPOSITION:
9947 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9949 case LVM_GETITEMRECT:
9950 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9952 case LVM_GETITEMSPACING:
9953 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9955 case LVM_GETITEMSTATE:
9956 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9958 case LVM_GETITEMTEXTA:
9959 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9961 case LVM_GETITEMTEXTW:
9962 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9964 case LVM_GETNEXTITEM:
9965 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9967 case LVM_GETNUMBEROFWORKAREAS:
9968 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9969 return 1;
9971 case LVM_GETORIGIN:
9972 if (!lParam) return FALSE;
9973 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT ||
9974 (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST) return FALSE;
9975 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9976 return TRUE;
9978 /* case LVM_GETOUTLINECOLOR: */
9980 /* case LVM_GETSELECTEDCOLUMN: */
9982 case LVM_GETSELECTEDCOUNT:
9983 return LISTVIEW_GetSelectedCount(infoPtr);
9985 case LVM_GETSELECTIONMARK:
9986 return infoPtr->nSelectionMark;
9988 case LVM_GETSTRINGWIDTHA:
9989 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9991 case LVM_GETSTRINGWIDTHW:
9992 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9994 case LVM_GETSUBITEMRECT:
9995 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
9997 case LVM_GETTEXTBKCOLOR:
9998 return infoPtr->clrTextBk;
10000 case LVM_GETTEXTCOLOR:
10001 return infoPtr->clrText;
10003 /* case LVM_GETTILEINFO: */
10005 /* case LVM_GETTILEVIEWINFO: */
10007 case LVM_GETTOOLTIPS:
10008 if( !infoPtr->hwndToolTip )
10009 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
10010 return (LRESULT)infoPtr->hwndToolTip;
10012 case LVM_GETTOPINDEX:
10013 return LISTVIEW_GetTopIndex(infoPtr);
10015 case LVM_GETUNICODEFORMAT:
10016 return (infoPtr->notifyFormat == NFR_UNICODE);
10018 /* case LVM_GETVIEW: */
10020 case LVM_GETVIEWRECT:
10021 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
10023 case LVM_GETWORKAREAS:
10024 FIXME("LVM_GETWORKAREAS: unimplemented\n");
10025 return FALSE;
10027 /* case LVM_HASGROUP: */
10029 case LVM_HITTEST:
10030 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
10032 case LVM_INSERTCOLUMNA:
10033 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10035 case LVM_INSERTCOLUMNW:
10036 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10038 /* case LVM_INSERTGROUP: */
10040 /* case LVM_INSERTGROUPSORTED: */
10042 case LVM_INSERTITEMA:
10043 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
10045 case LVM_INSERTITEMW:
10046 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
10048 /* case LVM_INSERTMARKHITTEST: */
10050 /* case LVM_ISGROUPVIEWENABLED: */
10052 /* case LVM_MAPIDTOINDEX: */
10054 /* case LVM_MAPINDEXTOID: */
10056 /* case LVM_MOVEGROUP: */
10058 /* case LVM_MOVEITEMTOGROUP: */
10060 case LVM_REDRAWITEMS:
10061 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
10063 /* case LVM_REMOVEALLGROUPS: */
10065 /* case LVM_REMOVEGROUP: */
10067 case LVM_SCROLL:
10068 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
10070 case LVM_SETBKCOLOR:
10071 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
10073 /* case LVM_SETBKIMAGE: */
10075 case LVM_SETCALLBACKMASK:
10076 infoPtr->uCallbackMask = (UINT)wParam;
10077 return TRUE;
10079 case LVM_SETCOLUMNA:
10080 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10082 case LVM_SETCOLUMNW:
10083 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10085 case LVM_SETCOLUMNORDERARRAY:
10086 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
10088 case LVM_SETCOLUMNWIDTH:
10089 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
10091 case LVM_SETEXTENDEDLISTVIEWSTYLE:
10092 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
10094 /* case LVM_SETGROUPINFO: */
10096 /* case LVM_SETGROUPMETRICS: */
10098 case LVM_SETHOTCURSOR:
10099 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
10101 case LVM_SETHOTITEM:
10102 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
10104 case LVM_SETHOVERTIME:
10105 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
10107 case LVM_SETICONSPACING:
10108 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10110 case LVM_SETIMAGELIST:
10111 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
10113 /* case LVM_SETINFOTIP: */
10115 /* case LVM_SETINSERTMARK: */
10117 /* case LVM_SETINSERTMARKCOLOR: */
10119 case LVM_SETITEMA:
10120 case LVM_SETITEMW:
10122 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
10123 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, (uMsg == LVM_SETITEMW));
10126 case LVM_SETITEMCOUNT:
10127 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
10129 case LVM_SETITEMPOSITION:
10131 POINT pt;
10132 pt.x = (short)LOWORD(lParam);
10133 pt.y = (short)HIWORD(lParam);
10134 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
10137 case LVM_SETITEMPOSITION32:
10138 if (lParam == 0) return FALSE;
10139 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
10141 case LVM_SETITEMSTATE:
10142 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
10144 case LVM_SETITEMTEXTA:
10145 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
10147 case LVM_SETITEMTEXTW:
10148 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
10150 /* case LVM_SETOUTLINECOLOR: */
10152 /* case LVM_SETSELECTEDCOLUMN: */
10154 case LVM_SETSELECTIONMARK:
10155 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
10157 case LVM_SETTEXTBKCOLOR:
10158 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
10160 case LVM_SETTEXTCOLOR:
10161 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
10163 /* case LVM_SETTILEINFO: */
10165 /* case LVM_SETTILEVIEWINFO: */
10167 /* case LVM_SETTILEWIDTH: */
10169 case LVM_SETTOOLTIPS:
10170 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
10172 case LVM_SETUNICODEFORMAT:
10173 return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
10175 /* case LVM_SETVIEW: */
10177 /* case LVM_SETWORKAREAS: */
10179 /* case LVM_SORTGROUPS: */
10181 case LVM_SORTITEMS:
10182 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam, FALSE);
10184 case LVM_SORTITEMSEX:
10185 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam, TRUE);
10187 case LVM_SUBITEMHITTEST:
10188 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
10190 case LVM_UPDATE:
10191 return LISTVIEW_Update(infoPtr, (INT)wParam);
10193 case CCM_GETVERSION:
10194 return LISTVIEW_GetVersion(infoPtr);
10196 case CCM_SETVERSION:
10197 return LISTVIEW_SetVersion(infoPtr, wParam);
10199 case WM_CHAR:
10200 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
10202 case WM_COMMAND:
10203 return LISTVIEW_Command(infoPtr, wParam, lParam);
10205 case WM_NCCREATE:
10206 return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
10208 case WM_CREATE:
10209 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
10211 case WM_DESTROY:
10212 return LISTVIEW_Destroy(infoPtr);
10214 case WM_ENABLE:
10215 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
10217 case WM_ERASEBKGND:
10218 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
10220 case WM_GETDLGCODE:
10221 return DLGC_WANTCHARS | DLGC_WANTARROWS;
10223 case WM_GETFONT:
10224 return (LRESULT)infoPtr->hFont;
10226 case WM_HSCROLL:
10227 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10229 case WM_KEYDOWN:
10230 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
10232 case WM_KILLFOCUS:
10233 return LISTVIEW_KillFocus(infoPtr);
10235 case WM_LBUTTONDBLCLK:
10236 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10238 case WM_LBUTTONDOWN:
10239 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10241 case WM_LBUTTONUP:
10242 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10244 case WM_MOUSEMOVE:
10245 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10247 case WM_MOUSEHOVER:
10248 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10250 case WM_NCDESTROY:
10251 return LISTVIEW_NCDestroy(infoPtr);
10253 case WM_NCPAINT:
10254 if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
10255 return 0;
10256 goto fwd_msg;
10258 case WM_NOTIFY:
10259 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
10260 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
10261 else return 0;
10263 case WM_NOTIFYFORMAT:
10264 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
10266 case WM_PRINTCLIENT:
10267 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
10269 case WM_PAINT:
10270 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
10272 case WM_RBUTTONDBLCLK:
10273 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10275 case WM_RBUTTONDOWN:
10276 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10278 case WM_RBUTTONUP:
10279 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10281 case WM_SETCURSOR:
10282 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
10283 return TRUE;
10284 goto fwd_msg;
10286 case WM_SETFOCUS:
10287 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
10289 case WM_SETFONT:
10290 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
10292 case WM_SETREDRAW:
10293 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
10295 case WM_SHOWWINDOW:
10296 LISTVIEW_ShowWindow(infoPtr, (BOOL)wParam, (INT)lParam);
10297 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10299 case WM_SIZE:
10300 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10302 case WM_STYLECHANGED:
10303 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10305 case WM_STYLECHANGING:
10306 return LISTVIEW_StyleChanging(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10308 case WM_SYSCOLORCHANGE:
10309 COMCTL32_RefreshSysColors();
10310 return 0;
10312 /* case WM_TIMER: */
10313 case WM_THEMECHANGED:
10314 return LISTVIEW_ThemeChanged(infoPtr);
10316 case WM_VSCROLL:
10317 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10319 case WM_MOUSEWHEEL:
10320 if (wParam & (MK_SHIFT | MK_CONTROL))
10321 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10322 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
10324 case WM_WINDOWPOSCHANGED:
10325 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
10327 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
10328 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
10329 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
10331 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
10333 MEASUREITEMSTRUCT mis;
10334 mis.CtlType = ODT_LISTVIEW;
10335 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
10336 mis.itemID = -1;
10337 mis.itemWidth = 0;
10338 mis.itemData = 0;
10339 mis.itemHeight= infoPtr->nItemHeight;
10340 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
10341 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
10342 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
10345 LISTVIEW_UpdateSize(infoPtr);
10346 LISTVIEW_UpdateScroll(infoPtr);
10348 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10350 /* case WM_WININICHANGE: */
10352 default:
10353 if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
10354 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
10356 fwd_msg:
10357 /* call default window procedure */
10358 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10363 /***
10364 * DESCRIPTION:
10365 * Registers the window class.
10367 * PARAMETER(S):
10368 * None
10370 * RETURN:
10371 * None
10373 void LISTVIEW_Register(void)
10375 WNDCLASSW wndClass;
10377 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
10378 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
10379 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
10380 wndClass.cbClsExtra = 0;
10381 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
10382 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
10383 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
10384 wndClass.lpszClassName = WC_LISTVIEWW;
10385 RegisterClassW(&wndClass);
10388 /***
10389 * DESCRIPTION:
10390 * Unregisters the window class.
10392 * PARAMETER(S):
10393 * None
10395 * RETURN:
10396 * None
10398 void LISTVIEW_Unregister(void)
10400 UnregisterClassW(WC_LISTVIEWW, NULL);
10403 /***
10404 * DESCRIPTION:
10405 * Handle any WM_COMMAND messages
10407 * PARAMETER(S):
10408 * [I] infoPtr : valid pointer to the listview structure
10409 * [I] wParam : the first message parameter
10410 * [I] lParam : the second message parameter
10412 * RETURN:
10413 * Zero.
10415 static LRESULT LISTVIEW_Command(const LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
10417 switch (HIWORD(wParam))
10419 case EN_UPDATE:
10422 * Adjust the edit window size
10424 WCHAR buffer[1024];
10425 HDC hdc = GetDC(infoPtr->hwndEdit);
10426 HFONT hFont, hOldFont = 0;
10427 RECT rect;
10428 SIZE sz;
10430 if (!infoPtr->hwndEdit || !hdc) return 0;
10431 GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
10432 GetWindowRect(infoPtr->hwndEdit, &rect);
10434 /* Select font to get the right dimension of the string */
10435 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
10436 if(hFont != 0)
10438 hOldFont = SelectObject(hdc, hFont);
10441 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
10443 TEXTMETRICW textMetric;
10445 /* Add Extra spacing for the next character */
10446 GetTextMetricsW(hdc, &textMetric);
10447 sz.cx += (textMetric.tmMaxCharWidth * 2);
10449 SetWindowPos (
10450 infoPtr->hwndEdit,
10451 HWND_TOP,
10454 sz.cx,
10455 rect.bottom - rect.top,
10456 SWP_DRAWFRAME|SWP_NOMOVE);
10458 if(hFont != 0)
10459 SelectObject(hdc, hOldFont);
10461 ReleaseDC(infoPtr->hwndEdit, hdc);
10463 break;
10466 default:
10467 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
10470 return 0;
10474 /***
10475 * DESCRIPTION:
10476 * Subclassed edit control windproc function
10478 * PARAMETER(S):
10479 * [I] hwnd : the edit window handle
10480 * [I] uMsg : the message that is to be processed
10481 * [I] wParam : first message parameter
10482 * [I] lParam : second message parameter
10483 * [I] isW : TRUE if input is Unicode
10485 * RETURN:
10486 * Zero.
10488 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
10490 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
10491 BOOL cancel = FALSE;
10493 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10494 hwnd, uMsg, wParam, lParam, isW);
10496 switch (uMsg)
10498 case WM_GETDLGCODE:
10499 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
10501 case WM_KILLFOCUS:
10502 break;
10504 case WM_DESTROY:
10506 WNDPROC editProc = infoPtr->EditWndProc;
10507 infoPtr->EditWndProc = 0;
10508 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
10509 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
10512 case WM_KEYDOWN:
10513 if (VK_ESCAPE == (INT)wParam)
10515 cancel = TRUE;
10516 break;
10518 else if (VK_RETURN == (INT)wParam)
10519 break;
10521 default:
10522 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
10525 /* kill the edit */
10526 if (infoPtr->hwndEdit)
10528 LPWSTR buffer = NULL;
10530 infoPtr->hwndEdit = 0;
10531 if (!cancel)
10533 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
10535 if (len)
10537 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
10539 if (isW) GetWindowTextW(hwnd, buffer, len+1);
10540 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
10544 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
10546 Free(buffer);
10549 SendMessageW(hwnd, WM_CLOSE, 0, 0);
10550 return 0;
10553 /***
10554 * DESCRIPTION:
10555 * Subclassed edit control Unicode windproc function
10557 * PARAMETER(S):
10558 * [I] hwnd : the edit window handle
10559 * [I] uMsg : the message that is to be processed
10560 * [I] wParam : first message parameter
10561 * [I] lParam : second message parameter
10563 * RETURN:
10565 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10567 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
10570 /***
10571 * DESCRIPTION:
10572 * Subclassed edit control ANSI windproc function
10574 * PARAMETER(S):
10575 * [I] hwnd : the edit window handle
10576 * [I] uMsg : the message that is to be processed
10577 * [I] wParam : first message parameter
10578 * [I] lParam : second message parameter
10580 * RETURN:
10582 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10584 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
10587 /***
10588 * DESCRIPTION:
10589 * Creates a subclassed edit control
10591 * PARAMETER(S):
10592 * [I] infoPtr : valid pointer to the listview structure
10593 * [I] text : initial text for the edit
10594 * [I] style : the window style
10595 * [I] isW : TRUE if input is Unicode
10597 * RETURN:
10599 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
10600 INT x, INT y, INT width, INT height, BOOL isW)
10602 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
10603 HWND hedit;
10604 SIZE sz;
10605 HDC hdc;
10606 HDC hOldFont=0;
10607 TEXTMETRICW textMetric;
10608 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
10610 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
10612 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
10613 hdc = GetDC(infoPtr->hwndSelf);
10615 /* Select the font to get appropriate metric dimensions */
10616 if(infoPtr->hFont != 0)
10617 hOldFont = SelectObject(hdc, infoPtr->hFont);
10619 /*Get String Length in pixels */
10620 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
10622 /*Add Extra spacing for the next character */
10623 GetTextMetricsW(hdc, &textMetric);
10624 sz.cx += (textMetric.tmMaxCharWidth * 2);
10626 if(infoPtr->hFont != 0)
10627 SelectObject(hdc, hOldFont);
10629 ReleaseDC(infoPtr->hwndSelf, hdc);
10630 if (isW)
10631 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10632 else
10633 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10635 if (!hedit) return 0;
10637 infoPtr->EditWndProc = (WNDPROC)
10638 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
10639 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
10641 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);
10643 return hedit;