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
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.
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
41 * -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
42 * or small icon and the LVS_AUTOARRANGE style is specified.
47 * -- Hot item handling, mouse hovering
48 * -- Workareas support
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
63 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
64 * linear in the number of items in the list, and this is
65 * unacceptable for large lists.
66 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
67 * instead of inserting in the right spot
68 * -- we should keep an ordered array of coordinates in iconic mode
69 * this would allow to frame items (iterator_frameditems),
70 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
77 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
84 * -- LVS_NOSCROLL (see Q137520)
85 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
87 * -- LVS_TYPESTYLEMASK
90 * -- LVS_EX_BORDERSELECT
92 * -- LVS_EX_HEADERDRAGDROP
95 * -- LVS_EX_MULTIWORKAREAS
97 * -- LVS_EX_SIMPLESELECT
98 * -- LVS_EX_TWOCLICKACTIVATE
99 * -- LVS_EX_UNDERLINECOLD
100 * -- LVS_EX_UNDERLINEHOT
103 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
106 * -- LVN_MARQUEEBEGIN
112 * -- LVM_CANCELEDITLABEL
113 * -- LVM_ENABLEGROUPVIEW
114 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
115 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
116 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
117 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
118 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
119 * -- LVM_GETINSERTMARKRECT
120 * -- LVM_GETNUMBEROFWORKAREAS
121 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
122 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
123 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
124 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
125 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
126 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
127 * -- LVM_GETVIEW, LVM_SETVIEW
128 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
129 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
130 * -- LVM_INSERTGROUPSORTED
131 * -- LVM_INSERTMARKHITTEST
132 * -- LVM_ISGROUPVIEWENABLED
133 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
135 * -- LVM_MOVEITEMTOGROUP
137 * -- LVM_SETTILEWIDTH
141 * -- ListView_GetCheckSate, ListView_SetCheckState
142 * -- ListView_GetHoverTime, ListView_SetHoverTime
143 * -- ListView_GetISearchString
144 * -- ListView_GetNumberOfWorkAreas
145 * -- ListView_GetUnicodeFormat, ListView_SetUnicodeFormat
146 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
151 * Known differences in message stream from native control (not known if
152 * these differences cause problems):
153 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
154 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
155 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
156 * processing for "USEDOUBLECLICKTIME".
160 #include "wine/port.h"
175 #include "commctrl.h"
176 #include "comctl32.h"
179 #include "wine/debug.h"
180 #include "wine/unicode.h"
182 WINE_DEFAULT_DEBUG_CHANNEL(listview);
184 /* make sure you set this to 0 for production use! */
185 #define DEBUG_RANGES 1
187 typedef struct tagCOLUMN_INFO
189 RECT rcHeader; /* tracks the header's rectangle */
190 int fmt; /* same as LVCOLUMN.fmt */
193 typedef struct tagITEMHDR
197 } ITEMHDR, *LPITEMHDR;
199 typedef struct tagSUBITEM_INFO
205 typedef struct tagITEM_INFO
213 typedef struct tagRANGE
219 typedef struct tagRANGES
224 typedef struct tagITERATOR
233 typedef struct tagDELAYED_ITEM_EDIT
239 typedef struct tagLISTVIEW_INFO
246 HIMAGELIST himlNormal;
247 HIMAGELIST himlSmall;
248 HIMAGELIST himlState;
252 POINT ptClickPos; /* point where the user clicked */
253 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
256 RANGES selectionRanges;
261 RECT rcList; /* This rectangle is really the window
262 * client rectangle possibly reduced by the
263 * horizontal scroll bar and/or header - see
264 * LISTVIEW_UpdateSize. This rectangle offset
265 * by the LISTVIEW_GetOrigin value is in
266 * client coordinates */
275 INT ntmHeight; /* Some cached metrics of the font used */
276 INT ntmMaxCharWidth; /* by the listview to draw items */
278 BOOL bRedraw; /* Turns on/off repaints & invalidations */
279 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
281 BOOL bDoChangeNotify; /* send change notification messages? */
284 DWORD dwStyle; /* the cached window GWL_STYLE */
285 DWORD dwLvExStyle; /* extended listview style */
286 INT nItemCount; /* the number of items in the list */
287 HDPA hdpaItems; /* array ITEM_INFO pointers */
288 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
289 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
290 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
291 POINT currIconPos; /* this is the position next icon will be placed */
292 PFNLVCOMPARE pfnCompare;
300 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
302 DWORD lastKeyPressTimestamp;
304 INT nSearchParamLength;
305 WCHAR szSearchParam[ MAX_PATH ];
307 INT nMeasureItemHeight;
308 INT xTrackLine; /* The x coefficient of the track line or -1 if none */
309 DELAYED_ITEM_EDIT itemEdit; /* Pointer to this structure will be the timer ID */
311 DWORD iVersion; /* CCM_[G,S]ETVERSION */
317 /* How many we debug buffer to allocate */
318 #define DEBUG_BUFFERS 20
319 /* The size of a single debug bbuffer */
320 #define DEBUG_BUFFER_SIZE 256
322 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
323 #define SB_INTERNAL -1
325 /* maximum size of a label */
326 #define DISP_TEXT_SIZE 512
328 /* padding for items in list and small icon display modes */
329 #define WIDTH_PADDING 12
331 /* padding for items in list, report and small icon display modes */
332 #define HEIGHT_PADDING 1
334 /* offset of items in report display mode */
335 #define REPORT_MARGINX 2
337 /* padding for icon in large icon display mode
338 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
339 * that HITTEST will see.
340 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
341 * ICON_TOP_PADDING - sum of the two above.
342 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
343 * LABEL_HOR_PADDING - between text and sides of box
344 * LABEL_VERT_PADDING - between bottom of text and end of box
346 * ICON_LR_PADDING - additional width above icon size.
347 * ICON_LR_HALF - half of the above value
349 #define ICON_TOP_PADDING_NOTHITABLE 2
350 #define ICON_TOP_PADDING_HITABLE 2
351 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
352 #define ICON_BOTTOM_PADDING 4
353 #define LABEL_HOR_PADDING 5
354 #define LABEL_VERT_PADDING 7
355 #define ICON_LR_PADDING 16
356 #define ICON_LR_HALF (ICON_LR_PADDING/2)
358 /* default label width for items in list and small icon display modes */
359 #define DEFAULT_LABEL_WIDTH 40
361 /* default column width for items in list display mode */
362 #define DEFAULT_COLUMN_WIDTH 128
364 /* Size of "line" scroll for V & H scrolls */
365 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
367 /* Padding between image and label */
368 #define IMAGE_PADDING 2
370 /* Padding behind the label */
371 #define TRAILING_LABEL_PADDING 12
372 #define TRAILING_HEADER_PADDING 11
374 /* Border for the icon caption */
375 #define CAPTION_BORDER 2
377 /* Standard DrawText flags */
378 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
379 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
380 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
382 /* Image index from state */
383 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
385 /* The time in milliseconds to reset the search in the list */
386 #define KEY_DELAY 450
388 /* Dump the LISTVIEW_INFO structure to the debug channel */
389 #define LISTVIEW_DUMP(iP) do { \
390 TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
391 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
392 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
393 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
394 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
395 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
396 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
397 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
398 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
399 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
402 static const WCHAR themeClass[] = {'L','i','s','t','V','i','e','w',0};
405 * forward declarations
407 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *, LPLVITEMW, BOOL);
408 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *, INT, LPRECT);
409 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *, INT, LPPOINT);
410 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *, INT, LPPOINT);
411 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *, INT, LPRECT);
412 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *, INT);
413 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *, LPPOINT);
414 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *, LPRECT);
415 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
416 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LVITEMW *, BOOL);
417 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *);
418 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
419 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
420 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
421 static LRESULT LISTVIEW_Command(const LISTVIEW_INFO *, WPARAM, LPARAM);
422 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *, LPCWSTR, BOOL);
423 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT, BOOL);
424 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *, INT, UINT);
425 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
426 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
427 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
428 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *);
429 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
430 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
431 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
432 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
434 /******** Text handling functions *************************************/
436 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
437 * text string. The string may be ANSI or Unicode, in which case
438 * the boolean isW tells us the type of the string.
440 * The name of the function tell what type of strings it expects:
441 * W: Unicode, T: ANSI/Unicode - function of isW
444 static inline BOOL is_textW(LPCWSTR text)
446 return text != NULL && text != LPSTR_TEXTCALLBACKW;
449 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
451 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
452 return is_textW(text);
455 static inline int textlenT(LPCWSTR text, BOOL isW)
457 return !is_textT(text, isW) ? 0 :
458 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
461 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
464 if (isSrcW) lstrcpynW(dest, src, max);
465 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
467 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
468 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
471 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
473 LPWSTR wstr = (LPWSTR)text;
475 if (!isW && is_textT(text, isW))
477 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
478 wstr = Alloc(len * sizeof(WCHAR));
479 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
481 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
485 static inline void textfreeT(LPWSTR wstr, BOOL isW)
487 if (!isW && is_textT(wstr, isW)) Free (wstr);
491 * dest is a pointer to a Unicode string
492 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
494 static BOOL textsetptrT(LPWSTR *dest, LPCWSTR src, BOOL isW)
498 if (src == LPSTR_TEXTCALLBACKW)
500 if (is_textW(*dest)) Free(*dest);
501 *dest = LPSTR_TEXTCALLBACKW;
505 LPWSTR pszText = textdupTtoW(src, isW);
506 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
507 bResult = Str_SetPtrW(dest, pszText);
508 textfreeT(pszText, isW);
514 * compares a Unicode to a Unicode/ANSI text string
516 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
518 if (!aw) return bt ? -1 : 0;
519 if (!bt) return aw ? 1 : 0;
520 if (aw == LPSTR_TEXTCALLBACKW)
521 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
522 if (bt != LPSTR_TEXTCALLBACKW)
524 LPWSTR bw = textdupTtoW(bt, isW);
525 int r = bw ? lstrcmpW(aw, bw) : 1;
533 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
537 n = min(min(n, lstrlenW(s1)), lstrlenW(s2));
538 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
539 return res ? res - sizeof(WCHAR) : res;
542 /******** Debugging functions *****************************************/
544 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
546 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
547 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
550 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
552 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
553 n = min(textlenT(text, isW), n);
554 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
557 static char* debug_getbuf(void)
559 static int index = 0;
560 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
561 return buffers[index++ % DEBUG_BUFFERS];
564 static inline const char* debugrange(const RANGE *lprng)
566 if (!lprng) return "(null)";
567 return wine_dbg_sprintf("[%d, %d]", lprng->lower, lprng->upper);
570 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
572 char* buf = debug_getbuf(), *text = buf;
573 int len, size = DEBUG_BUFFER_SIZE;
575 if (pScrollInfo == NULL) return "(null)";
576 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
577 if (len == -1) goto end; buf += len; size -= len;
578 if (pScrollInfo->fMask & SIF_RANGE)
579 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
581 if (len == -1) goto end; buf += len; size -= len;
582 if (pScrollInfo->fMask & SIF_PAGE)
583 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
585 if (len == -1) goto end; buf += len; size -= len;
586 if (pScrollInfo->fMask & SIF_POS)
587 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
589 if (len == -1) goto end; buf += len; size -= len;
590 if (pScrollInfo->fMask & SIF_TRACKPOS)
591 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
593 if (len == -1) goto end; buf += len; size -= len;
596 buf = text + strlen(text);
598 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
602 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
604 if (!plvnm) return "(null)";
605 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
606 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
607 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
608 plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam);
611 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
613 char* buf = debug_getbuf(), *text = buf;
614 int len, size = DEBUG_BUFFER_SIZE;
616 if (lpLVItem == NULL) return "(null)";
617 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
618 if (len == -1) goto end; buf += len; size -= len;
619 if (lpLVItem->mask & LVIF_STATE)
620 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
622 if (len == -1) goto end; buf += len; size -= len;
623 if (lpLVItem->mask & LVIF_TEXT)
624 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
626 if (len == -1) goto end; buf += len; size -= len;
627 if (lpLVItem->mask & LVIF_IMAGE)
628 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
630 if (len == -1) goto end; buf += len; size -= len;
631 if (lpLVItem->mask & LVIF_PARAM)
632 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
634 if (len == -1) goto end; buf += len; size -= len;
635 if (lpLVItem->mask & LVIF_INDENT)
636 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
638 if (len == -1) goto end; buf += len; size -= len;
641 buf = text + strlen(text);
643 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
647 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
649 char* buf = debug_getbuf(), *text = buf;
650 int len, size = DEBUG_BUFFER_SIZE;
652 if (lpColumn == NULL) return "(null)";
653 len = snprintf(buf, size, "{");
654 if (len == -1) goto end; buf += len; size -= len;
655 if (lpColumn->mask & LVCF_SUBITEM)
656 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
658 if (len == -1) goto end; buf += len; size -= len;
659 if (lpColumn->mask & LVCF_FMT)
660 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
662 if (len == -1) goto end; buf += len; size -= len;
663 if (lpColumn->mask & LVCF_WIDTH)
664 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
666 if (len == -1) goto end; buf += len; size -= len;
667 if (lpColumn->mask & LVCF_TEXT)
668 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
670 if (len == -1) goto end; buf += len; size -= len;
671 if (lpColumn->mask & LVCF_IMAGE)
672 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
674 if (len == -1) goto end; buf += len; size -= len;
675 if (lpColumn->mask & LVCF_ORDER)
676 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
678 if (len == -1) goto end; buf += len; size -= len;
681 buf = text + strlen(text);
683 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
687 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
689 if (!lpht) return "(null)";
691 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
692 wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
695 /* Return the corresponding text for a given scroll value */
696 static inline LPCSTR debugscrollcode(int nScrollCode)
700 case SB_LINELEFT: return "SB_LINELEFT";
701 case SB_LINERIGHT: return "SB_LINERIGHT";
702 case SB_PAGELEFT: return "SB_PAGELEFT";
703 case SB_PAGERIGHT: return "SB_PAGERIGHT";
704 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
705 case SB_THUMBTRACK: return "SB_THUMBTRACK";
706 case SB_ENDSCROLL: return "SB_ENDSCROLL";
707 case SB_INTERNAL: return "SB_INTERNAL";
708 default: return "unknown";
713 /******** Notification functions ************************************/
715 static LRESULT notify_forward_header(const LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
717 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
718 (WPARAM)lpnmh->hdr.idFrom, (LPARAM)lpnmh);
721 static LRESULT notify_hdr(const LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
725 TRACE("(code=%d)\n", code);
727 pnmh->hwndFrom = infoPtr->hwndSelf;
728 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
730 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, pnmh->idFrom, (LPARAM)pnmh);
732 TRACE(" <= %ld\n", result);
737 static inline BOOL notify(const LISTVIEW_INFO *infoPtr, INT code)
740 HWND hwnd = infoPtr->hwndSelf;
741 notify_hdr(infoPtr, code, &nmh);
742 return IsWindow(hwnd);
745 static inline void notify_itemactivate(const LISTVIEW_INFO *infoPtr, const LVHITTESTINFO *htInfo)
756 item.mask = LVIF_PARAM|LVIF_STATE;
757 item.iItem = htInfo->iItem;
759 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
760 nmia.lParam = item.lParam;
761 nmia.uOldState = item.state;
762 nmia.uNewState = item.state | LVIS_ACTIVATING;
763 nmia.uChanged = LVIF_STATE;
766 nmia.iItem = htInfo->iItem;
767 nmia.iSubItem = htInfo->iSubItem;
768 nmia.ptAction = htInfo->pt;
770 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
771 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
772 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
774 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
777 static inline LRESULT notify_listview(const LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
779 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
780 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
783 static BOOL notify_click(const LISTVIEW_INFO *infoPtr, INT code, const LVHITTESTINFO *lvht)
787 HWND hwnd = infoPtr->hwndSelf;
789 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
790 ZeroMemory(&nmlv, sizeof(nmlv));
791 nmlv.iItem = lvht->iItem;
792 nmlv.iSubItem = lvht->iSubItem;
793 nmlv.ptAction = lvht->pt;
794 item.mask = LVIF_PARAM;
795 item.iItem = lvht->iItem;
797 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
798 notify_listview(infoPtr, code, &nmlv);
799 return IsWindow(hwnd);
802 static BOOL notify_deleteitem(const LISTVIEW_INFO *infoPtr, INT nItem)
806 HWND hwnd = infoPtr->hwndSelf;
808 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
810 item.mask = LVIF_PARAM;
813 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
814 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
815 return IsWindow(hwnd);
818 static int get_ansi_notification(UINT unicodeNotificationCode)
820 switch (unicodeNotificationCode)
822 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
823 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
824 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
825 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
826 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
827 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
829 ERR("unknown notification %x\n", unicodeNotificationCode);
835 Send notification. depends on dispinfoW having same
836 structure as dispinfoA.
837 infoPtr : listview struct
838 notificationCode : *Unicode* notification code
839 pdi : dispinfo structure (can be unicode or ansi)
840 isW : TRUE if dispinfo is Unicode
842 static BOOL notify_dispinfoT(const LISTVIEW_INFO *infoPtr, UINT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
844 BOOL bResult = FALSE;
845 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
846 INT cchTempBufMax = 0, savCchTextMax = 0;
848 LPWSTR pszTempBuf = NULL, savPszText = NULL;
850 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
852 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
853 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
856 if (convertToAnsi || convertToUnicode)
858 if (notificationCode != LVN_GETDISPINFOW)
860 cchTempBufMax = convertToUnicode ?
861 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
862 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
866 cchTempBufMax = pdi->item.cchTextMax;
867 *pdi->item.pszText = 0; /* make sure we don't process garbage */
870 pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
871 if (!pszTempBuf) return FALSE;
873 if (convertToUnicode)
874 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
875 pszTempBuf, cchTempBufMax);
877 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
878 cchTempBufMax, NULL, NULL);
880 savCchTextMax = pdi->item.cchTextMax;
881 savPszText = pdi->item.pszText;
882 pdi->item.pszText = pszTempBuf;
883 pdi->item.cchTextMax = cchTempBufMax;
886 if (infoPtr->notifyFormat == NFR_ANSI)
887 realNotifCode = get_ansi_notification(notificationCode);
889 realNotifCode = notificationCode;
890 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
891 bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
893 if (convertToUnicode || convertToAnsi)
895 if (convertToUnicode) /* note : pointer can be changed by app ! */
896 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
897 savCchTextMax, NULL, NULL);
899 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
900 savPszText, savCchTextMax);
901 pdi->item.pszText = savPszText; /* restores our buffer */
902 pdi->item.cchTextMax = savCchTextMax;
908 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, const LISTVIEW_INFO *infoPtr, HDC hdc,
909 const RECT *rcBounds, const LVITEMW *lplvItem)
911 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
912 lpnmlvcd->nmcd.hdc = hdc;
913 lpnmlvcd->nmcd.rc = *rcBounds;
914 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
915 lpnmlvcd->clrText = infoPtr->clrText;
916 if (!lplvItem) return;
917 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
918 lpnmlvcd->iSubItem = lplvItem->iSubItem;
919 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
920 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
921 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
922 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
925 static inline DWORD notify_customdraw (const LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
927 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
930 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
931 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
932 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
933 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
934 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
935 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
939 static void prepaint_setup (const LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd, BOOL SubItem)
941 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
942 lpnmlvcd->clrTextBk = comctl32_color.clrWindow;
943 if (lpnmlvcd->clrText == CLR_DEFAULT)
944 lpnmlvcd->clrText = comctl32_color.clrWindowText;
946 /* apparently, for selected items, we have to override the returned values */
949 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
953 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
954 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
956 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
958 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
959 lpnmlvcd->clrText = comctl32_color.clrBtnText;
964 /* Set the text attributes */
965 if (lpnmlvcd->clrTextBk != CLR_NONE)
967 SetBkMode(hdc, OPAQUE);
968 SetBkColor(hdc,lpnmlvcd->clrTextBk);
971 SetBkMode(hdc, TRANSPARENT);
972 SetTextColor(hdc, lpnmlvcd->clrText);
975 static inline DWORD notify_postpaint (const LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
977 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
980 /******** Item iterator functions **********************************/
982 static RANGES ranges_create(int count);
983 static void ranges_destroy(RANGES ranges);
984 static BOOL ranges_add(RANGES ranges, RANGE range);
985 static BOOL ranges_del(RANGES ranges, RANGE range);
986 static void ranges_dump(RANGES ranges);
988 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
990 RANGE range = { nItem, nItem + 1 };
992 return ranges_add(ranges, range);
995 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
997 RANGE range = { nItem, nItem + 1 };
999 return ranges_del(ranges, range);
1003 * ITERATOR DOCUMENTATION
1005 * The iterator functions allow for easy, and convenient iteration
1006 * over items of interest in the list. Typically, you create a
1007 * iterator, use it, and destroy it, as such:
1010 * iterator_xxxitems(&i, ...);
1011 * while (iterator_{prev,next}(&i)
1013 * //code which uses i.nItem
1015 * iterator_destroy(&i);
1017 * where xxx is either: framed, or visible.
1018 * Note that it is important that the code destroys the iterator
1019 * after it's done with it, as the creation of the iterator may
1020 * allocate memory, which thus needs to be freed.
1022 * You can iterate both forwards, and backwards through the list,
1023 * by using iterator_next or iterator_prev respectively.
1025 * Lower numbered items are draw on top of higher number items in
1026 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1027 * items may overlap). So, to test items, you should use
1029 * which lists the items top to bottom (in Z-order).
1030 * For drawing items, you should use
1032 * which lists the items bottom to top (in Z-order).
1033 * If you keep iterating over the items after the end-of-items
1034 * marker (-1) is returned, the iterator will start from the
1035 * beginning. Typically, you don't need to test for -1,
1036 * because iterator_{next,prev} will return TRUE if more items
1037 * are to be iterated over, or FALSE otherwise.
1039 * Note: the iterator is defined to be bidirectional. That is,
1040 * any number of prev followed by any number of next, or
1041 * five versa, should leave the iterator at the same item:
1042 * prev * n, next * n = next * n, prev * n
1044 * The iterator has a notion of an out-of-order, special item,
1045 * which sits at the start of the list. This is used in
1046 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1047 * which needs to be first, as it may overlap other items.
1049 * The code is a bit messy because we have:
1050 * - a special item to deal with
1051 * - simple range, or composite range
1053 * If you find bugs, or want to add features, please make sure you
1054 * always check/modify *both* iterator_prev, and iterator_next.
1058 * This function iterates through the items in increasing order,
1059 * but prefixed by the special item, then -1. That is:
1060 * special, 1, 2, 3, ..., n, -1.
1061 * Each item is listed only once.
1063 static inline BOOL iterator_next(ITERATOR* i)
1067 i->nItem = i->nSpecial;
1068 if (i->nItem != -1) return TRUE;
1070 if (i->nItem == i->nSpecial)
1072 if (i->ranges) i->index = 0;
1078 if (i->nItem == i->nSpecial) i->nItem++;
1079 if (i->nItem < i->range.upper) return TRUE;
1084 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1085 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1088 else if (i->nItem >= i->range.upper) goto end;
1090 i->nItem = i->range.lower;
1091 if (i->nItem >= 0) goto testitem;
1098 * This function iterates through the items in decreasing order,
1099 * followed by the special item, then -1. That is:
1100 * n, n-1, ..., 3, 2, 1, special, -1.
1101 * Each item is listed only once.
1103 static inline BOOL iterator_prev(ITERATOR* i)
1110 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1113 if (i->nItem == i->nSpecial)
1121 if (i->nItem == i->nSpecial) i->nItem--;
1122 if (i->nItem >= i->range.lower) return TRUE;
1128 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1131 else if (!start && i->nItem < i->range.lower) goto end;
1133 i->nItem = i->range.upper;
1134 if (i->nItem > 0) goto testitem;
1136 return (i->nItem = i->nSpecial) != -1;
1139 static RANGE iterator_range(const ITERATOR *i)
1143 if (!i->ranges) return i->range;
1145 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1147 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1148 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1150 else range.lower = range.upper = 0;
1156 * Releases resources associated with this ierator.
1158 static inline void iterator_destroy(const ITERATOR *i)
1160 ranges_destroy(i->ranges);
1164 * Create an empty iterator.
1166 static inline BOOL iterator_empty(ITERATOR* i)
1168 ZeroMemory(i, sizeof(*i));
1169 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1174 * Create an iterator over a range.
1176 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1184 * Create an iterator over a bunch of ranges.
1185 * Please note that the iterator will take ownership of the ranges,
1186 * and will free them upon destruction.
1188 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1196 * Creates an iterator over the items which intersect lprc.
1198 static BOOL iterator_frameditems(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *lprc)
1200 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1201 RECT frame = *lprc, rcItem, rcTemp;
1204 /* in case we fail, we want to return an empty iterator */
1205 if (!iterator_empty(i)) return FALSE;
1207 LISTVIEW_GetOrigin(infoPtr, &Origin);
1209 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc));
1210 OffsetRect(&frame, -Origin.x, -Origin.y);
1212 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1216 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1218 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1219 if (IntersectRect(&rcTemp, &rcItem, lprc))
1220 i->nSpecial = infoPtr->nFocusedItem;
1222 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1223 /* to do better here, we need to have PosX, and PosY sorted */
1224 TRACE("building icon ranges:\n");
1225 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1227 rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1228 rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1229 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1230 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1231 if (IntersectRect(&rcTemp, &rcItem, &frame))
1232 ranges_additem(i->ranges, nItem);
1236 else if (uView == LVS_REPORT)
1240 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1241 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1243 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1244 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1245 if (range.upper <= range.lower) return TRUE;
1246 if (!iterator_rangeitems(i, range)) return FALSE;
1247 TRACE(" report=%s\n", debugrange(&i->range));
1251 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1252 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1253 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1254 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1255 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1256 INT lower = nFirstCol * nPerCol + nFirstRow;
1260 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1261 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1263 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1265 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1266 TRACE("building list ranges:\n");
1267 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1269 item_range.lower = nCol * nPerCol + nFirstRow;
1270 if(item_range.lower >= infoPtr->nItemCount) break;
1271 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1272 TRACE(" list=%s\n", debugrange(&item_range));
1273 ranges_add(i->ranges, item_range);
1281 * Creates an iterator over the items which intersect the visible region of hdc.
1283 static BOOL iterator_visibleitems(ITERATOR *i, const LISTVIEW_INFO *infoPtr, HDC hdc)
1285 POINT Origin, Position;
1286 RECT rcItem, rcClip;
1289 rgntype = GetClipBox(hdc, &rcClip);
1290 if (rgntype == NULLREGION) return iterator_empty(i);
1291 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1292 if (rgntype == SIMPLEREGION) return TRUE;
1294 /* first deal with the special item */
1295 if (i->nSpecial != -1)
1297 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1298 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1301 /* if we can't deal with the region, we'll just go with the simple range */
1302 LISTVIEW_GetOrigin(infoPtr, &Origin);
1303 TRACE("building visible range:\n");
1304 if (!i->ranges && i->range.lower < i->range.upper)
1306 if (!(i->ranges = ranges_create(50))) return TRUE;
1307 if (!ranges_add(i->ranges, i->range))
1309 ranges_destroy(i->ranges);
1315 /* now delete the invisible items from the list */
1316 while(iterator_next(i))
1318 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1319 rcItem.left = Position.x + Origin.x;
1320 rcItem.top = Position.y + Origin.y;
1321 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1322 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1323 if (!RectVisible(hdc, &rcItem))
1324 ranges_delitem(i->ranges, i->nItem);
1326 /* the iterator should restart on the next iterator_next */
1332 /******** Misc helper functions ************************************/
1334 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1335 WPARAM wParam, LPARAM lParam, BOOL isW)
1337 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1338 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1341 static inline BOOL is_autoarrange(const LISTVIEW_INFO *infoPtr)
1343 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1345 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1346 (uView == LVS_ICON || uView == LVS_SMALLICON);
1349 static void toggle_checkbox_state(LISTVIEW_INFO *infoPtr, INT nItem)
1351 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
1352 if(state == 1 || state == 2)
1356 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
1357 lvitem.stateMask = LVIS_STATEIMAGEMASK;
1358 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
1362 /******** Internal API functions ************************************/
1364 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(const LISTVIEW_INFO *infoPtr, INT nSubItem)
1366 static COLUMN_INFO mainItem;
1368 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1369 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1370 return DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1373 static INT LISTVIEW_CreateHeader(LISTVIEW_INFO *infoPtr)
1375 DWORD dFlags = WS_CHILD | HDS_HORZ | HDS_FULLDRAG | HDS_DRAGDROP;
1378 if (infoPtr->hwndHeader) return 0;
1380 TRACE("Creating header for list %p\n", infoPtr->hwndSelf);
1382 /* setup creation flags */
1383 dFlags |= (LVS_NOSORTHEADER & infoPtr->dwStyle) ? 0 : HDS_BUTTONS;
1384 dFlags |= (LVS_NOCOLUMNHEADER & infoPtr->dwStyle) ? HDS_HIDDEN : 0;
1386 hInst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
1389 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL, dFlags,
1390 0, 0, 0, 0, infoPtr->hwndSelf, NULL, hInst, NULL);
1391 if (!infoPtr->hwndHeader) return -1;
1393 /* set header unicode format */
1394 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, TRUE, 0);
1396 /* set header font */
1397 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
1399 LISTVIEW_UpdateSize(infoPtr);
1404 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO *infoPtr, INT nSubItem, LPRECT lprc)
1406 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1409 static inline BOOL LISTVIEW_GetItemW(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1411 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1414 /* used to handle collapse main item column case */
1415 static inline BOOL LISTVIEW_DrawFocusRect(const LISTVIEW_INFO *infoPtr, HDC hdc)
1417 return (infoPtr->rcFocus.left < infoPtr->rcFocus.right) ?
1418 DrawFocusRect(hdc, &infoPtr->rcFocus) : FALSE;
1421 /* Listview invalidation functions: use _only_ these functions to invalidate */
1423 static inline BOOL is_redrawing(const LISTVIEW_INFO *infoPtr)
1425 return infoPtr->bRedraw;
1428 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO *infoPtr, const RECT* rect)
1430 if(!is_redrawing(infoPtr)) return;
1431 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect));
1432 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1435 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO *infoPtr, INT nItem)
1439 if(!is_redrawing(infoPtr)) return;
1440 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1441 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1444 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1446 POINT Origin, Position;
1449 if(!is_redrawing(infoPtr)) return;
1450 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1451 LISTVIEW_GetOrigin(infoPtr, &Origin);
1452 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1453 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1455 rcBox.bottom = infoPtr->nItemHeight;
1456 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1457 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1460 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO *infoPtr)
1462 LISTVIEW_InvalidateRect(infoPtr, NULL);
1465 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO *infoPtr, INT nColumn)
1469 if(!is_redrawing(infoPtr)) return;
1470 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1471 rcCol.top = infoPtr->rcList.top;
1472 rcCol.bottom = infoPtr->rcList.bottom;
1473 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1478 * Retrieves the number of items that can fit vertically in the client area.
1481 * [I] infoPtr : valid pointer to the listview structure
1484 * Number of items per row.
1486 static inline INT LISTVIEW_GetCountPerRow(const LISTVIEW_INFO *infoPtr)
1488 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1490 return max(nListWidth/infoPtr->nItemWidth, 1);
1495 * Retrieves the number of items that can fit horizontally in the client
1499 * [I] infoPtr : valid pointer to the listview structure
1502 * Number of items per column.
1504 static inline INT LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO *infoPtr)
1506 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1508 return max(nListHeight / infoPtr->nItemHeight, 1);
1512 /*************************************************************************
1513 * LISTVIEW_ProcessLetterKeys
1515 * Processes keyboard messages generated by pressing the letter keys
1517 * What this does is perform a case insensitive search from the
1518 * current position with the following quirks:
1519 * - If two chars or more are pressed in quick succession we search
1520 * for the corresponding string (e.g. 'abc').
1521 * - If there is a delay we wipe away the current search string and
1522 * restart with just that char.
1523 * - If the user keeps pressing the same character, whether slowly or
1524 * fast, so that the search string is entirely composed of this
1525 * character ('aaaaa' for instance), then we search for first item
1526 * that starting with that character.
1527 * - If the user types the above character in quick succession, then
1528 * we must also search for the corresponding string ('aaaaa'), and
1529 * go to that string if there is a match.
1532 * [I] hwnd : handle to the window
1533 * [I] charCode : the character code, the actual character
1534 * [I] keyData : key data
1542 * - The current implementation has a list of characters it will
1543 * accept and it ignores everything else. In particular it will
1544 * ignore accentuated characters which seems to match what
1545 * Windows does. But I'm not sure it makes sense to follow
1547 * - We don't sound a beep when the search fails.
1551 * TREEVIEW_ProcessLetterKeys
1553 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1558 WCHAR buffer[MAX_PATH];
1559 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1561 /* simple parameter checking */
1562 if (!charCode || !keyData) return 0;
1564 /* only allow the valid WM_CHARs through */
1565 if (!isalnumW(charCode) &&
1566 charCode != '.' && charCode != '`' && 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 != '~')
1577 /* if there's one item or less, there is no where to go */
1578 if (infoPtr->nItemCount <= 1) return 0;
1580 /* update the search parameters */
1581 infoPtr->lastKeyPressTimestamp = GetTickCount();
1582 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1583 if (infoPtr->nSearchParamLength < MAX_PATH-1)
1584 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1585 if (infoPtr->charCode != charCode)
1586 infoPtr->charCode = charCode = 0;
1588 infoPtr->charCode=charCode;
1589 infoPtr->szSearchParam[0]=charCode;
1590 infoPtr->nSearchParamLength=1;
1591 /* Redundant with the 1 char string */
1595 /* and search from the current position */
1597 if (infoPtr->nFocusedItem >= 0) {
1598 endidx=infoPtr->nFocusedItem;
1600 /* if looking for single character match,
1601 * then we must always move forward
1603 if (infoPtr->nSearchParamLength == 1)
1606 endidx=infoPtr->nItemCount;
1610 /* Let application handle this for virtual listview */
1611 if (infoPtr->dwStyle & LVS_OWNERDATA)
1616 ZeroMemory(&lvfi, sizeof(lvfi));
1617 lvfi.flags = (LVFI_WRAP | LVFI_PARTIAL);
1618 infoPtr->szSearchParam[infoPtr->nSearchParamLength] = '\0';
1619 lvfi.psz = infoPtr->szSearchParam;
1623 nItem = notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
1626 LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1632 if (idx == infoPtr->nItemCount) {
1633 if (endidx == infoPtr->nItemCount || endidx == 0)
1639 item.mask = LVIF_TEXT;
1642 item.pszText = buffer;
1643 item.cchTextMax = MAX_PATH;
1644 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1646 /* check for a match */
1647 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1650 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1651 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1652 /* This would work but we must keep looking for a longer match */
1656 } while (idx != endidx);
1659 LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1664 /*************************************************************************
1665 * LISTVIEW_UpdateHeaderSize [Internal]
1667 * Function to resize the header control
1670 * [I] hwnd : handle to a window
1671 * [I] nNewScrollPos : scroll pos to set
1676 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1681 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1683 if (!infoPtr->hwndHeader) return;
1685 GetWindowRect(infoPtr->hwndHeader, &winRect);
1686 point[0].x = winRect.left;
1687 point[0].y = winRect.top;
1688 point[1].x = winRect.right;
1689 point[1].y = winRect.bottom;
1691 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1692 point[0].x = -nNewScrollPos;
1693 point[1].x += nNewScrollPos;
1695 SetWindowPos(infoPtr->hwndHeader,0,
1696 point[0].x,point[0].y,point[1].x,point[1].y,
1697 (infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW |
1698 SWP_NOZORDER | SWP_NOACTIVATE);
1703 * Update the scrollbars. This functions should be called whenever
1704 * the content, size or view changes.
1707 * [I] infoPtr : valid pointer to the listview structure
1712 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *infoPtr)
1714 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1715 SCROLLINFO horzInfo, vertInfo;
1718 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1720 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1721 horzInfo.cbSize = sizeof(SCROLLINFO);
1722 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1724 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1725 if (uView == LVS_LIST)
1727 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1728 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1730 /* scroll by at least one column per page */
1731 if(horzInfo.nPage < infoPtr->nItemWidth)
1732 horzInfo.nPage = infoPtr->nItemWidth;
1734 horzInfo.nPage /= infoPtr->nItemWidth;
1736 else if (uView == LVS_REPORT)
1738 horzInfo.nMax = infoPtr->nItemWidth;
1740 else /* LVS_ICON, or LVS_SMALLICON */
1744 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1747 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1748 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1749 dx = GetScrollPos(infoPtr->hwndSelf, SB_HORZ);
1750 dx -= SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1751 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1753 /* Setting the horizontal scroll can change the listview size
1754 * (and potentially everything else) so we need to recompute
1755 * everything again for the vertical scroll
1758 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1759 vertInfo.cbSize = sizeof(SCROLLINFO);
1760 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1762 if (uView == LVS_REPORT)
1764 vertInfo.nMax = infoPtr->nItemCount;
1766 /* scroll by at least one page */
1767 if(vertInfo.nPage < infoPtr->nItemHeight)
1768 vertInfo.nPage = infoPtr->nItemHeight;
1770 if (infoPtr->nItemHeight > 0)
1771 vertInfo.nPage /= infoPtr->nItemHeight;
1773 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1777 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1780 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1781 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1782 dy = GetScrollPos(infoPtr->hwndSelf, SB_VERT);
1783 dy -= SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1784 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1786 /* Change of the range may have changed the scroll pos. If so move the content */
1787 if (dx != 0 || dy != 0)
1790 listRect = infoPtr->rcList;
1791 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &listRect, &listRect, 0, 0,
1792 SW_ERASE | SW_INVALIDATE);
1795 /* Update the Header Control */
1796 if (uView == LVS_REPORT)
1798 horzInfo.fMask = SIF_POS;
1799 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1800 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1807 * Shows/hides the focus rectangle.
1810 * [I] infoPtr : valid pointer to the listview structure
1811 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1816 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO *infoPtr, BOOL fShow)
1818 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1821 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1823 if (infoPtr->nFocusedItem < 0) return;
1825 /* we need some gymnastics in ICON mode to handle large items */
1826 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1830 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1831 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1833 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1838 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1840 /* for some reason, owner draw should work only in report mode */
1841 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1846 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1847 HFONT hOldFont = SelectObject(hdc, hFont);
1849 item.iItem = infoPtr->nFocusedItem;
1851 item.mask = LVIF_PARAM;
1852 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1854 ZeroMemory(&dis, sizeof(dis));
1855 dis.CtlType = ODT_LISTVIEW;
1856 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1857 dis.itemID = item.iItem;
1858 dis.itemAction = ODA_FOCUS;
1859 if (fShow) dis.itemState |= ODS_FOCUS;
1860 dis.hwndItem = infoPtr->hwndSelf;
1862 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1863 dis.itemData = item.lParam;
1865 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1867 SelectObject(hdc, hOldFont);
1871 LISTVIEW_DrawFocusRect(infoPtr, hdc);
1874 ReleaseDC(infoPtr->hwndSelf, hdc);
1878 * Invalidates all visible selected items.
1880 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO *infoPtr)
1884 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1885 while(iterator_next(&i))
1887 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1888 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1890 iterator_destroy(&i);
1895 * DESCRIPTION: [INTERNAL]
1896 * Computes an item's (left,top) corner, relative to rcView.
1897 * That is, the position has NOT been made relative to the Origin.
1898 * This is deliberate, to avoid computing the Origin over, and
1899 * over again, when this function is called in a loop. Instead,
1900 * one can factor the computation of the Origin before the loop,
1901 * and offset the value returned by this function, on every iteration.
1904 * [I] infoPtr : valid pointer to the listview structure
1905 * [I] nItem : item number
1906 * [O] lpptOrig : item top, left corner
1911 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1913 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1915 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1917 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1919 lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1920 lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1922 else if (uView == LVS_LIST)
1924 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1925 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1926 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1928 else /* LVS_REPORT */
1930 lpptPosition->x = 0;
1931 lpptPosition->y = nItem * infoPtr->nItemHeight;
1936 * DESCRIPTION: [INTERNAL]
1937 * Compute the rectangles of an item. This is to localize all
1938 * the computations in one place. If you are not interested in some
1939 * of these values, simply pass in a NULL -- the function is smart
1940 * enough to compute only what's necessary. The function computes
1941 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1942 * one, the BOX rectangle. This rectangle is very cheap to compute,
1943 * and is guaranteed to contain all the other rectangles. Computing
1944 * the ICON rect is also cheap, but all the others are potentially
1945 * expensive. This gives an easy and effective optimization when
1946 * searching (like point inclusion, or rectangle intersection):
1947 * first test against the BOX, and if TRUE, test against the desired
1949 * If the function does not have all the necessary information
1950 * to computed the requested rectangles, will crash with a
1951 * failed assertion. This is done so we catch all programming
1952 * errors, given that the function is called only from our code.
1954 * We have the following 'special' meanings for a few fields:
1955 * * If LVIS_FOCUSED is set, we assume the item has the focus
1956 * This is important in ICON mode, where it might get a larger
1957 * then usual rectangle
1959 * Please note that subitem support works only in REPORT mode.
1962 * [I] infoPtr : valid pointer to the listview structure
1963 * [I] lpLVItem : item to compute the measures for
1964 * [O] lprcBox : ptr to Box rectangle
1965 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1966 * [0] lprcSelectBox : ptr to select box rectangle
1967 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
1968 * [O] lprcIcon : ptr to Icon rectangle
1969 * Same as LVM_GETITEMRECT with LVIR_ICON
1970 * [O] lprcStateIcon: ptr to State Icon rectangle
1971 * [O] lprcLabel : ptr to Label rectangle
1972 * Same as LVM_GETITEMRECT with LVIR_LABEL
1977 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1978 LPRECT lprcBox, LPRECT lprcSelectBox,
1979 LPRECT lprcIcon, LPRECT lprcStateIcon, LPRECT lprcLabel)
1981 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1982 BOOL doSelectBox = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1983 RECT Box, SelectBox, Icon, Label;
1984 COLUMN_INFO *lpColumnInfo = NULL;
1985 SIZE labelSize = { 0, 0 };
1987 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1989 /* Be smart and try to figure out the minimum we have to do */
1990 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1991 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1993 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1994 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1996 if (lprcSelectBox) doSelectBox = TRUE;
1997 if (lprcLabel) doLabel = TRUE;
1998 if (doLabel || lprcIcon || lprcStateIcon) doIcon = TRUE;
2005 /************************************************************/
2006 /* compute the box rectangle (it should be cheap to do) */
2007 /************************************************************/
2008 if (lpLVItem->iSubItem || uView == LVS_REPORT)
2009 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
2011 if (lpLVItem->iSubItem)
2013 Box = lpColumnInfo->rcHeader;
2018 Box.right = infoPtr->nItemWidth;
2021 Box.bottom = infoPtr->nItemHeight;
2023 /******************************************************************/
2024 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
2025 /******************************************************************/
2028 LONG state_width = 0;
2030 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
2031 state_width = infoPtr->iconStateSize.cx;
2033 if (uView == LVS_ICON)
2035 Icon.left = Box.left + state_width;
2036 if (infoPtr->himlNormal)
2037 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx - state_width) / 2;
2038 Icon.top = Box.top + ICON_TOP_PADDING;
2039 Icon.right = Icon.left;
2040 Icon.bottom = Icon.top;
2041 if (infoPtr->himlNormal)
2043 Icon.right += infoPtr->iconSize.cx;
2044 Icon.bottom += infoPtr->iconSize.cy;
2047 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2049 Icon.left = Box.left + state_width;
2051 if (uView == LVS_REPORT)
2052 Icon.left += REPORT_MARGINX;
2055 Icon.right = Icon.left;
2056 if (infoPtr->himlSmall &&
2057 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
2058 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
2059 Icon.right += infoPtr->iconSize.cx;
2060 Icon.bottom = Icon.top + infoPtr->iconSize.cy;
2062 if(lprcIcon) *lprcIcon = Icon;
2063 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon));
2065 /* TODO: is this correct? */
2068 lprcStateIcon->left = Icon.left - state_width;
2069 lprcStateIcon->right = Icon.left;
2070 lprcStateIcon->top = Icon.top;
2071 lprcStateIcon->bottom = lprcStateIcon->top + infoPtr->iconSize.cy;
2072 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon));
2075 else Icon.right = 0;
2077 /************************************************************/
2078 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2079 /************************************************************/
2082 /* calculate how far to the right can the label stretch */
2083 Label.right = Box.right;
2084 if (uView == LVS_REPORT)
2086 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
2089 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
2091 labelSize.cx = infoPtr->nItemWidth;
2092 labelSize.cy = infoPtr->nItemHeight;
2096 /* we need the text in non owner draw mode */
2097 assert(lpLVItem->mask & LVIF_TEXT);
2098 if (is_textT(lpLVItem->pszText, TRUE))
2100 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2101 HDC hdc = GetDC(infoPtr->hwndSelf);
2102 HFONT hOldFont = SelectObject(hdc, hFont);
2106 /* compute rough rectangle where the label will go */
2107 SetRectEmpty(&rcText);
2108 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2109 rcText.bottom = infoPtr->nItemHeight;
2110 if (uView == LVS_ICON)
2111 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2113 /* now figure out the flags */
2114 if (uView == LVS_ICON)
2115 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2117 uFormat = LV_SL_DT_FLAGS;
2119 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2121 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2122 labelSize.cy = rcText.bottom - rcText.top;
2124 SelectObject(hdc, hOldFont);
2125 ReleaseDC(infoPtr->hwndSelf, hdc);
2129 if (uView == LVS_ICON)
2131 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2132 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2133 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2134 Label.right = Label.left + labelSize.cx;
2135 Label.bottom = Label.top + infoPtr->nItemHeight;
2136 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2138 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2139 labelSize.cy /= infoPtr->ntmHeight;
2140 labelSize.cy = max(labelSize.cy, 1);
2141 labelSize.cy *= infoPtr->ntmHeight;
2143 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2145 else if (uView == LVS_REPORT)
2147 Label.left = Icon.right;
2148 Label.top = Box.top;
2149 Label.right = lpColumnInfo->rcHeader.right;
2150 Label.bottom = Label.top + infoPtr->nItemHeight;
2152 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2154 Label.left = Icon.right;
2155 Label.top = Box.top;
2156 Label.right = min(Label.left + labelSize.cx, Label.right);
2157 Label.bottom = Label.top + infoPtr->nItemHeight;
2160 if (lprcLabel) *lprcLabel = Label;
2161 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label));
2164 /************************************************************/
2165 /* compute STATEICON bounding box */
2166 /************************************************************/
2169 if (uView == LVS_REPORT)
2171 SelectBox.left = Icon.right; /* FIXME: should be Icon.left */
2172 SelectBox.top = Box.top;
2173 SelectBox.bottom = Box.bottom;
2174 if (lpLVItem->iSubItem == 0)
2176 /* we need the indent in report mode */
2177 assert(lpLVItem->mask & LVIF_INDENT);
2178 SelectBox.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
2180 SelectBox.right = min(SelectBox.left + labelSize.cx, Label.right);
2184 UnionRect(&SelectBox, &Icon, &Label);
2186 if (lprcSelectBox) *lprcSelectBox = SelectBox;
2187 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox));
2190 /* Fix the Box if necessary */
2193 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2194 else *lprcBox = Box;
2196 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box));
2200 * DESCRIPTION: [INTERNAL]
2203 * [I] infoPtr : valid pointer to the listview structure
2204 * [I] nItem : item number
2205 * [O] lprcBox : ptr to Box rectangle
2210 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2212 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2213 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2214 POINT Position, Origin;
2217 LISTVIEW_GetOrigin(infoPtr, &Origin);
2218 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2220 /* Be smart and try to figure out the minimum we have to do */
2222 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2223 lvItem.mask |= LVIF_TEXT;
2224 lvItem.iItem = nItem;
2225 lvItem.iSubItem = 0;
2226 lvItem.pszText = szDispText;
2227 lvItem.cchTextMax = DISP_TEXT_SIZE;
2228 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2229 if (uView == LVS_ICON)
2231 lvItem.mask |= LVIF_STATE;
2232 lvItem.stateMask = LVIS_FOCUSED;
2233 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2235 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0, 0);
2237 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2243 * Returns the current icon position, and advances it along the top.
2244 * The returned position is not offset by Origin.
2247 * [I] infoPtr : valid pointer to the listview structure
2248 * [O] lpPos : will get the current icon position
2253 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2255 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2257 *lpPos = infoPtr->currIconPos;
2259 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2260 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2262 infoPtr->currIconPos.x = 0;
2263 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2269 * Returns the current icon position, and advances it down the left edge.
2270 * The returned position is not offset by Origin.
2273 * [I] infoPtr : valid pointer to the listview structure
2274 * [O] lpPos : will get the current icon position
2279 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2281 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2283 *lpPos = infoPtr->currIconPos;
2285 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2286 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2288 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2289 infoPtr->currIconPos.y = 0;
2295 * Moves an icon to the specified position.
2296 * It takes care of invalidating the item, etc.
2299 * [I] infoPtr : valid pointer to the listview structure
2300 * [I] nItem : the item to move
2301 * [I] lpPos : the new icon position
2302 * [I] isNew : flags the item as being new
2308 static BOOL LISTVIEW_MoveIconTo(const LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2314 old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2315 old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2317 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2318 LISTVIEW_InvalidateItem(infoPtr, nItem);
2321 /* Allocating a POINTER for every item is too resource intensive,
2322 * so we'll keep the (x,y) in different arrays */
2323 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2324 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2326 LISTVIEW_InvalidateItem(infoPtr, nItem);
2333 * Arranges listview items in icon display mode.
2336 * [I] infoPtr : valid pointer to the listview structure
2337 * [I] nAlignCode : alignment code
2343 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2345 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2346 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2350 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2352 TRACE("nAlignCode=%d\n", nAlignCode);
2354 if (nAlignCode == LVA_DEFAULT)
2356 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2357 else nAlignCode = LVA_ALIGNTOP;
2362 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2363 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2364 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2365 default: return FALSE;
2368 infoPtr->bAutoarrange = TRUE;
2369 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2370 for (i = 0; i < infoPtr->nItemCount; i++)
2372 next_pos(infoPtr, &pos);
2373 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2381 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2384 * [I] infoPtr : valid pointer to the listview structure
2385 * [O] lprcView : bounding rectangle
2391 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2395 SetRectEmpty(lprcView);
2397 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2401 for (i = 0; i < infoPtr->nItemCount; i++)
2403 x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2404 y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2405 lprcView->right = max(lprcView->right, x);
2406 lprcView->bottom = max(lprcView->bottom, y);
2408 if (infoPtr->nItemCount > 0)
2410 lprcView->right += infoPtr->nItemWidth;
2411 lprcView->bottom += infoPtr->nItemHeight;
2416 y = LISTVIEW_GetCountPerColumn(infoPtr);
2417 x = infoPtr->nItemCount / y;
2418 if (infoPtr->nItemCount % y) x++;
2419 lprcView->right = x * infoPtr->nItemWidth;
2420 lprcView->bottom = y * infoPtr->nItemHeight;
2424 lprcView->right = infoPtr->nItemWidth;
2425 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2432 * Retrieves the bounding rectangle of all the items.
2435 * [I] infoPtr : valid pointer to the listview structure
2436 * [O] lprcView : bounding rectangle
2442 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2446 TRACE("(lprcView=%p)\n", lprcView);
2448 if (!lprcView) return FALSE;
2450 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2451 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2452 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2454 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView));
2461 * Retrieves the subitem pointer associated with the subitem index.
2464 * [I] hdpaSubItems : DPA handle for a specific item
2465 * [I] nSubItem : index of subitem
2468 * SUCCESS : subitem pointer
2471 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2473 SUBITEM_INFO *lpSubItem;
2476 /* we should binary search here if need be */
2477 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2479 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
2480 if (lpSubItem->iSubItem == nSubItem)
2490 * Calculates the desired item width.
2493 * [I] infoPtr : valid pointer to the listview structure
2496 * The desired item width.
2498 static INT LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO *infoPtr)
2500 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2503 TRACE("uView=%d\n", uView);
2505 if (uView == LVS_ICON)
2506 nItemWidth = infoPtr->iconSpacing.cx;
2507 else if (uView == LVS_REPORT)
2511 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2513 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2514 nItemWidth = rcHeader.right;
2517 else /* LVS_SMALLICON, or LVS_LIST */
2521 for (i = 0; i < infoPtr->nItemCount; i++)
2522 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2524 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2525 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2527 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2530 return max(nItemWidth, 1);
2535 * Calculates the desired item height.
2538 * [I] infoPtr : valid pointer to the listview structure
2541 * The desired item height.
2543 static INT LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO *infoPtr)
2545 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2548 TRACE("uView=%d\n", uView);
2550 if (uView == LVS_ICON)
2551 nItemHeight = infoPtr->iconSpacing.cy;
2554 nItemHeight = infoPtr->ntmHeight;
2555 if (uView == LVS_REPORT && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
2557 if (infoPtr->himlState)
2558 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2559 if (infoPtr->himlSmall)
2560 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2561 if (infoPtr->himlState || infoPtr->himlSmall)
2562 nItemHeight += HEIGHT_PADDING;
2563 if (infoPtr->nMeasureItemHeight > 0)
2564 nItemHeight = infoPtr->nMeasureItemHeight;
2567 return max(nItemHeight, 1);
2572 * Updates the width, and height of an item.
2575 * [I] infoPtr : valid pointer to the listview structure
2580 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2582 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2583 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2589 * Retrieves and saves important text metrics info for the current
2593 * [I] infoPtr : valid pointer to the listview structure
2596 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2598 HDC hdc = GetDC(infoPtr->hwndSelf);
2599 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2600 HFONT hOldFont = SelectObject(hdc, hFont);
2604 if (GetTextMetricsW(hdc, &tm))
2606 infoPtr->ntmHeight = tm.tmHeight;
2607 infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
2610 if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
2611 infoPtr->nEllipsisWidth = sz.cx;
2613 SelectObject(hdc, hOldFont);
2614 ReleaseDC(infoPtr->hwndSelf, hdc);
2616 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2621 * A compare function for ranges
2624 * [I] range1 : pointer to range 1;
2625 * [I] range2 : pointer to range 2;
2629 * > 0 : if range 1 > range 2
2630 * < 0 : if range 2 > range 1
2631 * = 0 : if range intersects range 2
2633 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2637 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2639 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2644 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1), debugrange(range2), cmp);
2650 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2652 #define ranges_check(ranges, desc) do { } while(0)
2655 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2660 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2662 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2663 ranges_dump(ranges);
2664 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2666 prev = DPA_GetPtr(ranges->hdpa, 0);
2667 assert (prev->lower >= 0 && prev->lower < prev->upper);
2668 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2670 curr = DPA_GetPtr(ranges->hdpa, i);
2671 assert (prev->upper <= curr->lower);
2672 assert (curr->lower < curr->upper);
2676 TRACE("--- Done checking---\n");
2679 static RANGES ranges_create(int count)
2681 RANGES ranges = Alloc(sizeof(struct tagRANGES));
2682 if (!ranges) return NULL;
2683 ranges->hdpa = DPA_Create(count);
2684 if (ranges->hdpa) return ranges;
2689 static void ranges_clear(RANGES ranges)
2693 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2694 Free(DPA_GetPtr(ranges->hdpa, i));
2695 DPA_DeleteAllPtrs(ranges->hdpa);
2699 static void ranges_destroy(RANGES ranges)
2701 if (!ranges) return;
2702 ranges_clear(ranges);
2703 DPA_Destroy(ranges->hdpa);
2707 static RANGES ranges_clone(RANGES ranges)
2712 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2714 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2716 RANGE *newrng = Alloc(sizeof(RANGE));
2717 if (!newrng) goto fail;
2718 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2719 DPA_SetPtr(clone->hdpa, i, newrng);
2724 TRACE ("clone failed\n");
2725 ranges_destroy(clone);
2729 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2733 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2734 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2739 static void ranges_dump(RANGES ranges)
2743 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2744 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2747 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2749 RANGE srchrng = { nItem, nItem + 1 };
2751 TRACE("(nItem=%d)\n", nItem);
2752 ranges_check(ranges, "before contain");
2753 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2756 static INT ranges_itemcount(RANGES ranges)
2760 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2762 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2763 count += sel->upper - sel->lower;
2769 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2771 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2774 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2775 if (index == -1) return TRUE;
2777 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2779 chkrng = DPA_GetPtr(ranges->hdpa, index);
2780 if (chkrng->lower >= nItem)
2781 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2782 if (chkrng->upper > nItem)
2783 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2788 static BOOL ranges_add(RANGES ranges, RANGE range)
2793 TRACE("(%s)\n", debugrange(&range));
2794 ranges_check(ranges, "before add");
2796 /* try find overlapping regions first */
2797 srchrgn.lower = range.lower - 1;
2798 srchrgn.upper = range.upper + 1;
2799 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2805 TRACE("Adding new range\n");
2807 /* create the brand new range to insert */
2808 newrgn = Alloc(sizeof(RANGE));
2809 if(!newrgn) goto fail;
2812 /* figure out where to insert it */
2813 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2814 TRACE("index=%d\n", index);
2815 if (index == -1) index = 0;
2817 /* and get it over with */
2818 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2826 RANGE *chkrgn, *mrgrgn;
2827 INT fromindex, mergeindex;
2829 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2830 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2832 chkrgn->lower = min(range.lower, chkrgn->lower);
2833 chkrgn->upper = max(range.upper, chkrgn->upper);
2835 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2837 /* merge now common ranges */
2839 srchrgn.lower = chkrgn->lower - 1;
2840 srchrgn.upper = chkrgn->upper + 1;
2844 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2845 if (mergeindex == -1) break;
2846 if (mergeindex == index)
2848 fromindex = index + 1;
2852 TRACE("Merge with index %i\n", mergeindex);
2854 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2855 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2856 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2858 DPA_DeletePtr(ranges->hdpa, mergeindex);
2859 if (mergeindex < index) index --;
2863 ranges_check(ranges, "after add");
2867 ranges_check(ranges, "failed add");
2871 static BOOL ranges_del(RANGES ranges, RANGE range)
2876 TRACE("(%s)\n", debugrange(&range));
2877 ranges_check(ranges, "before del");
2879 /* we don't use DPAS_SORTED here, since we need *
2880 * to find the first overlapping range */
2881 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2884 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2886 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2888 /* case 1: Same range */
2889 if ( (chkrgn->upper == range.upper) &&
2890 (chkrgn->lower == range.lower) )
2892 DPA_DeletePtr(ranges->hdpa, index);
2895 /* case 2: engulf */
2896 else if ( (chkrgn->upper <= range.upper) &&
2897 (chkrgn->lower >= range.lower) )
2899 DPA_DeletePtr(ranges->hdpa, index);
2901 /* case 3: overlap upper */
2902 else if ( (chkrgn->upper <= range.upper) &&
2903 (chkrgn->lower < range.lower) )
2905 chkrgn->upper = range.lower;
2907 /* case 4: overlap lower */
2908 else if ( (chkrgn->upper > range.upper) &&
2909 (chkrgn->lower >= range.lower) )
2911 chkrgn->lower = range.upper;
2914 /* case 5: fully internal */
2917 RANGE tmprgn = *chkrgn, *newrgn;
2919 if (!(newrgn = Alloc(sizeof(RANGE)))) goto fail;
2920 newrgn->lower = chkrgn->lower;
2921 newrgn->upper = range.lower;
2922 chkrgn->lower = range.upper;
2923 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2932 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2935 ranges_check(ranges, "after del");
2939 ranges_check(ranges, "failed del");
2945 * Removes all selection ranges
2948 * [I] infoPtr : valid pointer to the listview structure
2949 * [I] toSkip : item range to skip removing the selection
2955 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2964 lvItem.stateMask = LVIS_SELECTED;
2966 /* need to clone the DPA because callbacks can change it */
2967 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2968 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2969 while(iterator_next(&i))
2970 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2971 /* note that the iterator destructor will free the cloned range */
2972 iterator_destroy(&i);
2977 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2981 if (!(toSkip = ranges_create(1))) return FALSE;
2982 if (nItem != -1) ranges_additem(toSkip, nItem);
2983 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2984 ranges_destroy(toSkip);
2988 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2990 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2995 * Retrieves the number of items that are marked as selected.
2998 * [I] infoPtr : valid pointer to the listview structure
3001 * Number of items selected.
3003 static INT LISTVIEW_GetSelectedCount(const LISTVIEW_INFO *infoPtr)
3005 INT nSelectedCount = 0;
3007 if (infoPtr->uCallbackMask & LVIS_SELECTED)
3010 for (i = 0; i < infoPtr->nItemCount; i++)
3012 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
3017 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
3019 TRACE("nSelectedCount=%d\n", nSelectedCount);
3020 return nSelectedCount;
3025 * Manages the item focus.
3028 * [I] infoPtr : valid pointer to the listview structure
3029 * [I] nItem : item index
3032 * TRUE : focused item changed
3033 * FALSE : focused item has NOT changed
3035 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
3037 INT oldFocus = infoPtr->nFocusedItem;
3040 if (nItem == infoPtr->nFocusedItem) return FALSE;
3042 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
3043 lvItem.stateMask = LVIS_FOCUSED;
3044 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
3046 return oldFocus != infoPtr->nFocusedItem;
3049 /* Helper function for LISTVIEW_ShiftIndices *only* */
3050 static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
3052 if (nShiftItem < nItem) return nShiftItem;
3054 if (nShiftItem > nItem) return nShiftItem + direction;
3056 if (direction > 0) return nShiftItem + direction;
3058 return min(nShiftItem, infoPtr->nItemCount - 1);
3063 * Updates the various indices after an item has been inserted or deleted.
3066 * [I] infoPtr : valid pointer to the listview structure
3067 * [I] nItem : item index
3068 * [I] direction : Direction of shift, +1 or -1.
3073 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
3078 /* temporarily disable change notification while shifting items */
3079 bOldChange = infoPtr->bDoChangeNotify;
3080 infoPtr->bDoChangeNotify = FALSE;
3082 TRACE("Shifting %iu, %i steps\n", nItem, direction);
3084 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
3086 assert(abs(direction) == 1);
3088 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
3090 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
3091 if (nNewFocus != infoPtr->nFocusedItem)
3092 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
3094 /* But we are not supposed to modify nHotItem! */
3096 infoPtr->bDoChangeNotify = bOldChange;
3102 * Adds a block of selections.
3105 * [I] infoPtr : valid pointer to the listview structure
3106 * [I] nItem : item index
3109 * Whether the window is still valid.
3111 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3113 INT nFirst = min(infoPtr->nSelectionMark, nItem);
3114 INT nLast = max(infoPtr->nSelectionMark, nItem);
3115 HWND hwndSelf = infoPtr->hwndSelf;
3116 NMLVODSTATECHANGE nmlv;
3121 /* Temporarily disable change notification
3122 * If the control is LVS_OWNERDATA, we need to send
3123 * only one LVN_ODSTATECHANGED notification.
3124 * See MSDN documentation for LVN_ITEMCHANGED.
3126 bOldChange = infoPtr->bDoChangeNotify;
3127 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3129 if (nFirst == -1) nFirst = nItem;
3131 item.state = LVIS_SELECTED;
3132 item.stateMask = LVIS_SELECTED;
3134 for (i = nFirst; i <= nLast; i++)
3135 LISTVIEW_SetItemState(infoPtr,i,&item);
3137 ZeroMemory(&nmlv, sizeof(nmlv));
3138 nmlv.iFrom = nFirst;
3141 nmlv.uOldState = item.state;
3143 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3144 if (!IsWindow(hwndSelf))
3146 infoPtr->bDoChangeNotify = bOldChange;
3153 * Sets a single group selection.
3156 * [I] infoPtr : valid pointer to the listview structure
3157 * [I] nItem : item index
3162 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3164 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3170 if (!(selection = ranges_create(100))) return;
3172 item.state = LVIS_SELECTED;
3173 item.stateMask = LVIS_SELECTED;
3175 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3177 if (infoPtr->nSelectionMark == -1)
3179 infoPtr->nSelectionMark = nItem;
3180 ranges_additem(selection, nItem);
3186 sel.lower = min(infoPtr->nSelectionMark, nItem);
3187 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3188 ranges_add(selection, sel);
3193 RECT rcItem, rcSel, rcSelMark;
3196 rcItem.left = LVIR_BOUNDS;
3197 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3198 rcSelMark.left = LVIR_BOUNDS;
3199 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3200 UnionRect(&rcSel, &rcItem, &rcSelMark);
3201 iterator_frameditems(&i, infoPtr, &rcSel);
3202 while(iterator_next(&i))
3204 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3205 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3207 iterator_destroy(&i);
3210 /* disable per item notifications on LVS_OWNERDATA style
3211 FIXME: single LVN_ODSTATECHANGED should be used */
3212 bOldChange = infoPtr->bDoChangeNotify;
3213 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3215 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3218 iterator_rangesitems(&i, selection);
3219 while(iterator_next(&i))
3220 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3221 /* this will also destroy the selection */
3222 iterator_destroy(&i);
3224 infoPtr->bDoChangeNotify = bOldChange;
3226 LISTVIEW_SetItemFocus(infoPtr, nItem);
3231 * Sets a single selection.
3234 * [I] infoPtr : valid pointer to the listview structure
3235 * [I] nItem : item index
3240 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3244 TRACE("nItem=%d\n", nItem);
3246 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3248 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3249 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3250 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3252 infoPtr->nSelectionMark = nItem;
3257 * Set selection(s) with keyboard.
3260 * [I] infoPtr : valid pointer to the listview structure
3261 * [I] nItem : item index
3262 * [I] space : VK_SPACE code sent
3265 * SUCCESS : TRUE (needs to be repainted)
3266 * FAILURE : FALSE (nothing has changed)
3268 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem, BOOL space)
3270 /* FIXME: pass in the state */
3271 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3272 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3273 BOOL bResult = FALSE;
3275 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3276 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3278 if (infoPtr->dwStyle & LVS_SINGLESEL)
3281 LISTVIEW_SetSelection(infoPtr, nItem);
3288 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3293 lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3294 lvItem.stateMask = LVIS_SELECTED;
3297 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3298 if (lvItem.state & LVIS_SELECTED)
3299 infoPtr->nSelectionMark = nItem;
3301 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3306 LISTVIEW_SetSelection(infoPtr, nItem);
3309 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3312 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3316 static BOOL LISTVIEW_GetItemAtPt(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3318 LVHITTESTINFO lvHitTestInfo;
3320 ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3321 lvHitTestInfo.pt.x = pt.x;
3322 lvHitTestInfo.pt.y = pt.y;
3324 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3326 lpLVItem->mask = LVIF_PARAM;
3327 lpLVItem->iItem = lvHitTestInfo.iItem;
3328 lpLVItem->iSubItem = 0;
3330 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3333 static inline BOOL LISTVIEW_isHotTracking(const LISTVIEW_INFO *infoPtr)
3335 return ((infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) ||
3336 (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE) ||
3337 (infoPtr->dwLvExStyle & LVS_EX_TWOCLICKACTIVATE));
3342 * Called when the mouse is being actively tracked and has hovered for a specified
3346 * [I] infoPtr : valid pointer to the listview structure
3347 * [I] fwKeys : key indicator
3348 * [I] x,y : mouse position
3351 * 0 if the message was processed, non-zero if there was an error
3354 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3355 * over the item for a certain period of time.
3358 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3360 if (LISTVIEW_isHotTracking(infoPtr))
3368 if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3369 LISTVIEW_SetSelection(infoPtr, item.iItem);
3377 * Called whenever WM_MOUSEMOVE is received.
3380 * [I] infoPtr : valid pointer to the listview structure
3381 * [I] fwKeys : key indicator
3382 * [I] x,y : mouse position
3385 * 0 if the message is processed, non-zero if there was an error
3387 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3389 TRACKMOUSEEVENT trackinfo;
3391 if (!(fwKeys & MK_LBUTTON))
3392 infoPtr->bLButtonDown = FALSE;
3394 if (infoPtr->bLButtonDown)
3398 WORD wDragWidth = GetSystemMetrics(SM_CXDRAG);
3399 WORD wDragHeight= GetSystemMetrics(SM_CYDRAG);
3401 rect.left = infoPtr->ptClickPos.x - wDragWidth;
3402 rect.right = infoPtr->ptClickPos.x + wDragWidth;
3403 rect.top = infoPtr->ptClickPos.y - wDragHeight;
3404 rect.bottom = infoPtr->ptClickPos.y + wDragHeight;
3409 if (!PtInRect(&rect, tmp))
3411 LVHITTESTINFO lvHitTestInfo;
3414 lvHitTestInfo.pt = infoPtr->ptClickPos;
3415 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3417 ZeroMemory(&nmlv, sizeof(nmlv));
3418 nmlv.iItem = lvHitTestInfo.iItem;
3419 nmlv.ptAction = infoPtr->ptClickPos;
3421 if (!infoPtr->bDragging)
3423 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3424 infoPtr->bDragging = TRUE;
3431 infoPtr->bLButtonDown = FALSE;
3433 /* see if we are supposed to be tracking mouse hovering */
3434 if (LISTVIEW_isHotTracking(infoPtr)) {
3435 /* fill in the trackinfo struct */
3436 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3437 trackinfo.dwFlags = TME_QUERY;
3438 trackinfo.hwndTrack = infoPtr->hwndSelf;
3439 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3441 /* see if we are already tracking this hwnd */
3442 _TrackMouseEvent(&trackinfo);
3444 if(!(trackinfo.dwFlags & TME_HOVER)) {
3445 trackinfo.dwFlags = TME_HOVER;
3447 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3448 _TrackMouseEvent(&trackinfo);
3457 * Tests whether the item is assignable to a list with style lStyle
3459 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3461 if ( (lpLVItem->mask & LVIF_TEXT) &&
3462 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3463 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3471 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3474 * [I] infoPtr : valid pointer to the listview structure
3475 * [I] lpLVItem : valid pointer to new item attributes
3476 * [I] isNew : the item being set is being inserted
3477 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3478 * [O] bChanged : will be set to TRUE if the item really changed
3484 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3486 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3491 /* stateMask is ignored for LVM_INSERTITEM */
3492 UINT stateMask = isNew ? ~0 : lpLVItem->stateMask;
3496 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3498 if (lpLVItem->mask == 0) return TRUE;
3500 if (infoPtr->dwStyle & LVS_OWNERDATA)
3502 /* a virtual listview only stores selection and focus */
3503 if (lpLVItem->mask & ~LVIF_STATE)
3509 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3510 lpItem = DPA_GetPtr(hdpaSubItems, 0);
3514 /* we need to get the lParam and state of the item */
3515 item.iItem = lpLVItem->iItem;
3516 item.iSubItem = lpLVItem->iSubItem;
3517 item.mask = LVIF_STATE | LVIF_PARAM;
3518 item.stateMask = ~0;
3521 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3523 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3524 /* determine what fields will change */
3525 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & stateMask & ~infoPtr->uCallbackMask))
3526 uChanged |= LVIF_STATE;
3528 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3529 uChanged |= LVIF_IMAGE;
3531 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3532 uChanged |= LVIF_PARAM;
3534 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3535 uChanged |= LVIF_INDENT;
3537 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3538 uChanged |= LVIF_TEXT;
3540 TRACE("uChanged=0x%x\n", uChanged);
3541 if (!uChanged) return TRUE;
3544 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3545 nmlv.iItem = lpLVItem->iItem;
3546 nmlv.uNewState = (item.state & ~stateMask) | (lpLVItem->state & stateMask);
3547 nmlv.uOldState = item.state;
3548 nmlv.uChanged = uChanged;
3549 nmlv.lParam = item.lParam;
3551 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3552 /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
3554 if(lpItem && !isNew && infoPtr->bDoChangeNotify)
3556 HWND hwndSelf = infoPtr->hwndSelf;
3558 if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3560 if (!IsWindow(hwndSelf))
3564 /* copy information */
3565 if (lpLVItem->mask & LVIF_TEXT)
3566 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3568 if (lpLVItem->mask & LVIF_IMAGE)
3569 lpItem->hdr.iImage = lpLVItem->iImage;
3571 if (lpLVItem->mask & LVIF_PARAM)
3572 lpItem->lParam = lpLVItem->lParam;
3574 if (lpLVItem->mask & LVIF_INDENT)
3575 lpItem->iIndent = lpLVItem->iIndent;
3577 if (uChanged & LVIF_STATE)
3579 if (lpItem && (stateMask & ~infoPtr->uCallbackMask))
3581 lpItem->state &= ~stateMask;
3582 lpItem->state |= (lpLVItem->state & stateMask);
3584 if (lpLVItem->state & stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3586 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3587 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3589 else if (stateMask & LVIS_SELECTED)
3591 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3593 /* if we are asked to change focus, and we manage it, do it */
3594 if (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3596 if (lpLVItem->state & LVIS_FOCUSED)
3598 if (infoPtr->nFocusedItem != -1)
3600 /* remove current focus */
3601 item.mask = LVIF_STATE;
3603 item.stateMask = LVIS_FOCUSED;
3605 /* recurse with redrawing an item */
3606 LISTVIEW_SetItemState(infoPtr, infoPtr->nFocusedItem, &item);
3609 infoPtr->nFocusedItem = lpLVItem->iItem;
3610 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3612 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3614 infoPtr->nFocusedItem = -1;
3619 /* if we're inserting the item, we're done */
3620 if (isNew) return TRUE;
3622 /* send LVN_ITEMCHANGED notification */
3623 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3624 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3631 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3634 * [I] infoPtr : valid pointer to the listview structure
3635 * [I] lpLVItem : valid pointer to new subitem attributes
3636 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3637 * [O] bChanged : will be set to TRUE if the item really changed
3643 static BOOL set_sub_item(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3646 SUBITEM_INFO *lpSubItem;
3648 /* we do not support subitems for virtual listviews */
3649 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3651 /* set subitem only if column is present */
3652 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3654 /* First do some sanity checks */
3655 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
3656 particularly useful. We currently do not actually do anything with
3657 the flag on subitems.
3659 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)) return FALSE;
3660 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))) return TRUE;
3662 /* get the subitem structure, and create it if not there */
3663 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3664 assert (hdpaSubItems);
3666 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3669 SUBITEM_INFO *tmpSubItem;
3672 lpSubItem = Alloc(sizeof(SUBITEM_INFO));
3673 if (!lpSubItem) return FALSE;
3674 /* we could binary search here, if need be...*/
3675 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3677 tmpSubItem = DPA_GetPtr(hdpaSubItems, i);
3678 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3680 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3685 lpSubItem->iSubItem = lpLVItem->iSubItem;
3686 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3690 if (lpLVItem->mask & LVIF_IMAGE)
3691 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3693 lpSubItem->hdr.iImage = lpLVItem->iImage;
3697 if (lpLVItem->mask & LVIF_TEXT)
3698 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3700 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3709 * Sets item attributes.
3712 * [I] infoPtr : valid pointer to the listview structure
3713 * [I] lpLVItem : new item attributes
3714 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3720 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem, BOOL isW)
3722 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3723 HWND hwndSelf = infoPtr->hwndSelf;
3724 LPWSTR pszText = NULL;
3725 BOOL bResult, bChanged = FALSE;
3727 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3729 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3732 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3733 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3735 pszText = lpLVItem->pszText;
3736 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
3739 /* actually set the fields */
3740 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3742 if (lpLVItem->iSubItem)
3743 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3745 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3746 if (!IsWindow(hwndSelf))
3749 /* redraw item, if necessary */
3750 if (bChanged && !infoPtr->bIsDrawing)
3752 /* this little optimization eliminates some nasty flicker */
3753 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3754 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
3755 lpLVItem->iSubItem > 0 && lpLVItem->iSubItem <= DPA_GetPtrCount(infoPtr->hdpaColumns) )
3756 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3758 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3763 textfreeT(lpLVItem->pszText, isW);
3764 lpLVItem->pszText = pszText;
3772 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3775 * [I] infoPtr : valid pointer to the listview structure
3780 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *infoPtr)
3782 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3784 SCROLLINFO scrollInfo;
3786 scrollInfo.cbSize = sizeof(SCROLLINFO);
3787 scrollInfo.fMask = SIF_POS;
3789 if (uView == LVS_LIST)
3791 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3792 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3794 else if (uView == LVS_REPORT)
3796 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3797 nItem = scrollInfo.nPos;
3801 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3802 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3805 TRACE("nItem=%d\n", nItem);
3813 * Erases the background of the given rectangle
3816 * [I] infoPtr : valid pointer to the listview structure
3817 * [I] hdc : device context handle
3818 * [I] lprcBox : clipping rectangle
3824 static inline BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3826 if (!infoPtr->hBkBrush) return FALSE;
3828 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
3830 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3838 * [I] infoPtr : valid pointer to the listview structure
3839 * [I] hdc : device context handle
3840 * [I] nItem : item index
3841 * [I] nSubItem : subitem index
3842 * [I] pos : item position in client coordinates
3843 * [I] cdmode : custom draw mode
3849 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3851 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3852 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3853 static WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3854 DWORD cdsubitemmode = CDRF_DODEFAULT;
3856 RECT rcSelect, rcBox, rcIcon, rcLabel, rcStateIcon;
3857 NMLVCUSTOMDRAW nmlvcd;
3862 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
3864 /* get information needed for drawing the item */
3865 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
3866 if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
3867 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3868 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3869 lvItem.iItem = nItem;
3870 lvItem.iSubItem = nSubItem;
3873 lvItem.cchTextMax = DISP_TEXT_SIZE;
3874 lvItem.pszText = szDispText;
3875 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3876 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3877 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3878 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
3879 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3881 /* now check if we need to update the focus rectangle */
3882 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3884 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3885 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
3886 OffsetRect(&rcBox, pos.x, pos.y);
3887 OffsetRect(&rcSelect, pos.x, pos.y);
3888 OffsetRect(&rcIcon, pos.x, pos.y);
3889 OffsetRect(&rcStateIcon, pos.x, pos.y);
3890 OffsetRect(&rcLabel, pos.x, pos.y);
3891 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
3892 wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect),
3893 wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
3895 /* fill in the custom draw structure */
3896 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3898 hOldFont = GetCurrentObject(hdc, OBJ_FONT);
3899 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3900 if (cdmode & CDRF_SKIPDEFAULT) goto postpaint;
3901 if (cdmode & CDRF_NOTIFYITEMDRAW)
3902 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3903 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3904 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3905 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3906 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3908 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3909 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3911 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3912 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
3913 else if ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) == FALSE)
3914 prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE);
3916 /* in full row select, subitems, will just use main item's colors */
3917 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3918 nmlvcd.clrTextBk = CLR_NONE;
3921 if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && (nSubItem == 0))
3923 UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
3926 TRACE("uStateImage=%d\n", uStateImage);
3927 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
3928 rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
3933 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3934 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3936 TRACE("iImage=%d\n", lvItem.iImage);
3937 ImageList_DrawEx(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3938 rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk, CLR_DEFAULT,
3939 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3942 /* Don't bother painting item being edited */
3943 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3945 /* FIXME: temporary hack */
3946 rcSelect.left = rcLabel.left;
3948 /* draw the selection background, if we're drawing the main item */
3951 /* in icon mode, the label rect is really what we want to draw the
3953 if (uView == LVS_ICON)
3956 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3957 rcSelect.right = rcBox.right;
3959 if (nmlvcd.clrTextBk != CLR_NONE)
3960 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3961 if(lprcFocus) *lprcFocus = rcSelect;
3964 /* figure out the text drawing flags */
3965 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3966 if (uView == LVS_ICON)
3967 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3970 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3972 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3973 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3974 default: uFormat |= DT_LEFT;
3977 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3979 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3980 else rcLabel.left += LABEL_HOR_PADDING;
3982 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3984 /* for GRIDLINES reduce the bottom so the text formats correctly */
3985 if (uView == LVS_REPORT && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
3988 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3991 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3992 notify_postpaint(infoPtr, &nmlvcd);
3993 if (cdsubitemmode & CDRF_NEWFONT)
3994 SelectObject(hdc, hOldFont);
4000 * Draws listview items when in owner draw mode.
4003 * [I] infoPtr : valid pointer to the listview structure
4004 * [I] hdc : device context handle
4009 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4011 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
4012 DWORD cditemmode = CDRF_DODEFAULT;
4013 NMLVCUSTOMDRAW nmlvcd;
4014 POINT Origin, Position;
4020 ZeroMemory(&dis, sizeof(dis));
4022 /* Get scroll info once before loop */
4023 LISTVIEW_GetOrigin(infoPtr, &Origin);
4025 /* iterate through the invalidated rows */
4026 while(iterator_next(i))
4028 item.iItem = i->nItem;
4030 item.mask = LVIF_PARAM | LVIF_STATE;
4031 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4032 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
4034 dis.CtlType = ODT_LISTVIEW;
4036 dis.itemID = item.iItem;
4037 dis.itemAction = ODA_DRAWENTIRE;
4039 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
4040 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
4041 dis.hwndItem = infoPtr->hwndSelf;
4043 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
4044 dis.rcItem.left = Position.x + Origin.x;
4045 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
4046 dis.rcItem.top = Position.y + Origin.y;
4047 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
4048 dis.itemData = item.lParam;
4050 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
4053 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
4054 * structure for the rest. of the paint cycle
4056 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
4057 if (cdmode & CDRF_NOTIFYITEMDRAW)
4058 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4060 if (!(cditemmode & CDRF_SKIPDEFAULT))
4062 prepaint_setup (infoPtr, hdc, &nmlvcd, FALSE);
4063 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
4066 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
4067 notify_postpaint(infoPtr, &nmlvcd);
4073 * Draws listview items when in report display mode.
4076 * [I] infoPtr : valid pointer to the listview structure
4077 * [I] hdc : device context handle
4078 * [I] cdmode : custom draw mode
4083 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4086 RECT rcClip, rcItem;
4087 POINT Origin, Position;
4093 /* figure out what to draw */
4094 rgntype = GetClipBox(hdc, &rcClip);
4095 if (rgntype == NULLREGION) return;
4097 /* Get scroll info once before loop */
4098 LISTVIEW_GetOrigin(infoPtr, &Origin);
4100 /* narrow down the columns we need to paint */
4101 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
4103 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
4104 if (rcItem.right + Origin.x >= rcClip.left) break;
4106 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
4108 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
4109 if (rcItem.left + Origin.x < rcClip.right) break;
4111 iterator_rangeitems(&j, colRange);
4113 /* in full row select, we _have_ to draw the main item */
4114 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
4117 /* iterate through the invalidated rows */
4118 while(iterator_next(i))
4120 /* iterate through the invalidated columns */
4121 while(iterator_next(&j))
4123 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4124 Position.x += Origin.x;
4125 Position.y += Origin.y;
4127 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
4129 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4131 rcItem.bottom = infoPtr->nItemHeight;
4132 OffsetRect(&rcItem, Position.x, Position.y);
4133 if (!RectVisible(hdc, &rcItem)) continue;
4136 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
4139 iterator_destroy(&j);
4144 * Draws the gridlines if necessary when in report display mode.
4147 * [I] infoPtr : valid pointer to the listview structure
4148 * [I] hdc : device context handle
4153 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO *infoPtr, HDC hdc)
4158 RECT rcClip, rcItem = {0};
4166 /* figure out what to draw */
4167 rgntype = GetClipBox(hdc, &rcClip);
4168 if (rgntype == NULLREGION) return;
4170 /* Get scroll info once before loop */
4171 LISTVIEW_GetOrigin(infoPtr, &Origin);
4173 /* narrow down the columns we need to paint */
4174 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
4176 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
4177 if (rcItem.right + Origin.x >= rcClip.left) break;
4179 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
4181 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
4182 if (rcItem.left + Origin.x < rcClip.right) break;
4184 /* is right most vertical line visible? */
4185 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
4187 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcItem);
4188 rmost = (rcItem.right + Origin.x < rcClip.right);
4191 if ((hPen = CreatePen( PS_SOLID, 1, comctl32_color.clr3dFace )))
4193 hOldPen = SelectObject ( hdc, hPen );
4195 /* draw the vertical lines for the columns */
4196 iterator_rangeitems(&j, colRange);
4197 while(iterator_next(&j))
4199 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4200 if (rcItem.left == 0) continue; /* skip first column */
4201 rcItem.left += Origin.x;
4202 rcItem.right += Origin.x;
4203 rcItem.top = infoPtr->rcList.top;
4204 rcItem.bottom = infoPtr->rcList.bottom;
4205 TRACE("vert col=%d, rcItem=%s\n", j.nItem, wine_dbgstr_rect(&rcItem));
4206 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4207 LineTo (hdc, rcItem.left, rcItem.bottom);
4209 iterator_destroy(&j);
4210 /* draw rightmost grid line if visible */
4213 MoveToEx (hdc, rcItem.right, rcItem.top, NULL);
4214 LineTo (hdc, rcItem.right, rcItem.bottom);
4217 /* draw the horizontial lines for the rows */
4218 itemheight = LISTVIEW_CalculateItemHeight(infoPtr);
4219 rcItem.left = infoPtr->rcList.left;
4220 rcItem.right = infoPtr->rcList.right;
4221 rcItem.bottom = rcItem.top = Origin.y - 1;
4222 MoveToEx(hdc, rcItem.left, rcItem.top, NULL);
4223 LineTo(hdc, rcItem.right, rcItem.top);
4224 for(y=itemheight-1+Origin.y; y<=infoPtr->rcList.bottom; y+=itemheight)
4226 rcItem.bottom = rcItem.top = y;
4227 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem));
4228 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4229 LineTo (hdc, rcItem.right, rcItem.top);
4232 SelectObject( hdc, hOldPen );
4233 DeleteObject( hPen );
4239 * Draws listview items when in list display mode.
4242 * [I] infoPtr : valid pointer to the listview structure
4243 * [I] hdc : device context handle
4244 * [I] cdmode : custom draw mode
4249 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4251 POINT Origin, Position;
4253 /* Get scroll info once before loop */
4254 LISTVIEW_GetOrigin(infoPtr, &Origin);
4256 while(iterator_prev(i))
4258 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4259 Position.x += Origin.x;
4260 Position.y += Origin.y;
4262 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
4269 * Draws listview items.
4272 * [I] infoPtr : valid pointer to the listview structure
4273 * [I] hdc : device context handle
4274 * [I] prcErase : rect to be erased before refresh (may be NULL)
4279 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcErase)
4281 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4282 COLORREF oldTextColor = 0, oldBkColor = 0, oldClrTextBk, oldClrText;
4283 NMLVCUSTOMDRAW nmlvcd;
4290 HBITMAP hbmp = NULL;
4292 LISTVIEW_DUMP(infoPtr);
4294 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4295 TRACE("double buffering\n");
4297 hdc = CreateCompatibleDC(hdcOrig);
4299 ERR("Failed to create DC for backbuffer\n");
4302 hbmp = CreateCompatibleBitmap(hdcOrig, infoPtr->rcList.right,
4303 infoPtr->rcList.bottom);
4305 ERR("Failed to create bitmap for backbuffer\n");
4310 SelectObject(hdc, hbmp);
4311 SelectObject(hdc, infoPtr->hFont);
4313 /* Save dc values we're gonna trash while drawing
4314 * FIXME: Should be done in LISTVIEW_DrawItem() */
4315 hOldFont = SelectObject(hdc, infoPtr->hFont);
4316 oldBkMode = GetBkMode(hdc);
4317 oldBkColor = GetBkColor(hdc);
4318 oldTextColor = GetTextColor(hdc);
4321 infoPtr->bIsDrawing = TRUE;
4324 LISTVIEW_FillBkgnd(infoPtr, hdc, prcErase);
4325 } else if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4326 /* If no erasing was done (usually because RedrawWindow was called
4327 * with RDW_INVALIDATE only) we need to copy the old contents into
4328 * the backbuffer before continuing. */
4329 BitBlt(hdc, infoPtr->rcList.left, infoPtr->rcList.top,
4330 infoPtr->rcList.right - infoPtr->rcList.left,
4331 infoPtr->rcList.bottom - infoPtr->rcList.top,
4332 hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4335 /* FIXME: Shouldn't need to do this */
4336 oldClrTextBk = infoPtr->clrTextBk;
4337 oldClrText = infoPtr->clrText;
4339 infoPtr->cditemmode = CDRF_DODEFAULT;
4341 GetClientRect(infoPtr->hwndSelf, &rcClient);
4342 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4343 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4344 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4345 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4347 /* Use these colors to draw the items */
4348 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4349 infoPtr->clrText = nmlvcd.clrText;
4351 /* nothing to draw */
4352 if(infoPtr->nItemCount == 0) goto enddraw;
4354 /* figure out what we need to draw */
4355 iterator_visibleitems(&i, infoPtr, hdc);
4357 /* send cache hint notification */
4358 if (infoPtr->dwStyle & LVS_OWNERDATA)
4360 RANGE range = iterator_range(&i);
4363 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4364 nmlv.iFrom = range.lower;
4365 nmlv.iTo = range.upper - 1;
4366 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4369 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4370 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4373 if (uView == LVS_REPORT)
4374 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4375 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4376 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4378 /* if we have a focus rect, draw it */
4379 if (infoPtr->bFocus)
4380 LISTVIEW_DrawFocusRect(infoPtr, hdc);
4382 iterator_destroy(&i);
4385 /* For LVS_EX_GRIDLINES go and draw lines */
4386 /* This includes the case where there were *no* items */
4387 if ((uView == LVS_REPORT) && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
4388 LISTVIEW_RefreshReportGrid(infoPtr, hdc);
4390 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4391 notify_postpaint(infoPtr, &nmlvcd);
4393 infoPtr->clrTextBk = oldClrTextBk;
4394 infoPtr->clrText = oldClrText;
4397 BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top,
4398 infoPtr->rcList.right - infoPtr->rcList.left,
4399 infoPtr->rcList.bottom - infoPtr->rcList.top,
4400 hdc, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4405 SelectObject(hdc, hOldFont);
4406 SetBkMode(hdc, oldBkMode);
4407 SetBkColor(hdc, oldBkColor);
4408 SetTextColor(hdc, oldTextColor);
4411 infoPtr->bIsDrawing = FALSE;
4417 * Calculates the approximate width and height of a given number of items.
4420 * [I] infoPtr : valid pointer to the listview structure
4421 * [I] nItemCount : number of items
4422 * [I] wWidth : width
4423 * [I] wHeight : height
4426 * Returns a DWORD. The width in the low word and the height in high word.
4428 static DWORD LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO *infoPtr, INT nItemCount,
4429 WORD wWidth, WORD wHeight)
4431 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4432 INT nItemCountPerColumn = 1;
4433 INT nColumnCount = 0;
4434 DWORD dwViewRect = 0;
4436 if (nItemCount == -1)
4437 nItemCount = infoPtr->nItemCount;
4439 if (uView == LVS_LIST)
4441 if (wHeight == 0xFFFF)
4443 /* use current height */
4444 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4447 if (wHeight < infoPtr->nItemHeight)
4448 wHeight = infoPtr->nItemHeight;
4452 if (infoPtr->nItemHeight > 0)
4454 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4455 if (nItemCountPerColumn == 0)
4456 nItemCountPerColumn = 1;
4458 if (nItemCount % nItemCountPerColumn != 0)
4459 nColumnCount = nItemCount / nItemCountPerColumn;
4461 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4465 /* Microsoft padding magic */
4466 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4467 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4469 dwViewRect = MAKELONG(wWidth, wHeight);
4471 else if (uView == LVS_REPORT)
4475 if (infoPtr->nItemCount > 0)
4477 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4478 wWidth = rcBox.right - rcBox.left;
4479 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4483 /* use current height and width */
4484 if (wHeight == 0xffff)
4485 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4486 if (wWidth == 0xffff)
4487 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4490 dwViewRect = MAKELONG(wWidth, wHeight);
4492 else if (uView == LVS_SMALLICON)
4493 FIXME("uView == LVS_SMALLICON: not implemented\n");
4494 else if (uView == LVS_ICON)
4495 FIXME("uView == LVS_ICON: not implemented\n");
4503 * Create a drag image list for the specified item.
4506 * [I] infoPtr : valid pointer to the listview structure
4507 * [I] iItem : index of item
4508 * [O] lppt : Upper-left corner of the image
4511 * Returns a handle to the image list if successful, NULL otherwise.
4513 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4519 HBITMAP hbmp, hOldbmp;
4520 HIMAGELIST dragList = 0;
4521 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
4523 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4526 rcItem.left = LVIR_BOUNDS;
4527 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4530 lppt->x = rcItem.left;
4531 lppt->y = rcItem.top;
4533 size.cx = rcItem.right - rcItem.left;
4534 size.cy = rcItem.bottom - rcItem.top;
4536 hdcOrig = GetDC(infoPtr->hwndSelf);
4537 hdc = CreateCompatibleDC(hdcOrig);
4538 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4539 hOldbmp = SelectObject(hdc, hbmp);
4541 rcItem.left = rcItem.top = 0;
4542 rcItem.right = size.cx;
4543 rcItem.bottom = size.cy;
4544 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4547 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4549 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4550 SelectObject(hdc, hOldbmp);
4551 ImageList_Add(dragList, hbmp, 0);
4554 SelectObject(hdc, hOldbmp);
4558 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4560 TRACE("ret=%p\n", dragList);
4568 * Removes all listview items and subitems.
4571 * [I] infoPtr : valid pointer to the listview structure
4577 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr, BOOL destroy)
4580 HDPA hdpaSubItems = NULL;
4587 /* we do it directly, to avoid notifications */
4588 ranges_clear(infoPtr->selectionRanges);
4589 infoPtr->nSelectionMark = -1;
4590 infoPtr->nFocusedItem = -1;
4591 SetRectEmpty(&infoPtr->rcFocus);
4592 /* But we are supposed to leave nHotItem as is! */
4595 /* send LVN_DELETEALLITEMS notification */
4596 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4598 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4600 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4602 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4604 /* send LVN_DELETEITEM notification, if not suppressed
4605 and if it is not a virtual listview */
4606 if (!bSuppress) notify_deleteitem(infoPtr, i);
4607 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
4608 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4610 hdrItem = DPA_GetPtr(hdpaSubItems, j);
4611 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4614 DPA_Destroy(hdpaSubItems);
4615 DPA_DeletePtr(infoPtr->hdpaItems, i);
4617 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4618 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4619 infoPtr->nItemCount --;
4624 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4625 LISTVIEW_UpdateScroll(infoPtr);
4627 LISTVIEW_InvalidateList(infoPtr);
4634 * Scrolls, and updates the columns, when a column is changing width.
4637 * [I] infoPtr : valid pointer to the listview structure
4638 * [I] nColumn : column to scroll
4639 * [I] dx : amount of scroll, in pixels
4644 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4646 COLUMN_INFO *lpColumnInfo;
4651 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4652 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4653 rcCol = lpColumnInfo->rcHeader;
4654 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4655 rcCol.left = rcCol.right;
4657 /* adjust the other columns */
4658 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4660 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4661 lpColumnInfo->rcHeader.left += dx;
4662 lpColumnInfo->rcHeader.right += dx;
4665 /* do not update screen if not in report mode */
4666 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4668 /* if we have a focus, we must first erase the focus rect */
4669 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4671 /* Need to reset the item width when inserting a new column */
4672 infoPtr->nItemWidth += dx;
4674 LISTVIEW_UpdateScroll(infoPtr);
4675 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4677 /* scroll to cover the deleted column, and invalidate for redraw */
4678 rcOld = infoPtr->rcList;
4679 rcOld.left = ptOrigin.x + rcCol.left + dx;
4680 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4682 /* we can restore focus now */
4683 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4688 * Removes a column from the listview control.
4691 * [I] infoPtr : valid pointer to the listview structure
4692 * [I] nColumn : column index
4698 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4702 TRACE("nColumn=%d\n", nColumn);
4704 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4705 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4707 /* While the MSDN specifically says that column zero should not be deleted,
4708 what actually happens is that the column itself is deleted but no items or subitems
4712 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4714 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4717 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4718 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4720 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4722 SUBITEM_INFO *lpSubItem, *lpDelItem;
4724 INT nItem, nSubItem, i;
4726 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4728 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
4731 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4733 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
4734 if (lpSubItem->iSubItem == nColumn)
4737 lpDelItem = lpSubItem;
4739 else if (lpSubItem->iSubItem > nColumn)
4741 lpSubItem->iSubItem--;
4745 /* if we found our subitem, zapp it */
4749 if (is_textW(lpDelItem->hdr.pszText))
4750 Free(lpDelItem->hdr.pszText);
4755 /* free dpa memory */
4756 DPA_DeletePtr(hdpaSubItems, nSubItem);
4761 /* update the other column info */
4762 LISTVIEW_UpdateItemSize(infoPtr);
4763 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4764 LISTVIEW_InvalidateList(infoPtr);
4766 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4773 * Invalidates the listview after an item's insertion or deletion.
4776 * [I] infoPtr : valid pointer to the listview structure
4777 * [I] nItem : item index
4778 * [I] dir : -1 if deleting, 1 if inserting
4783 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4785 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4786 INT nPerCol, nItemCol, nItemRow;
4790 /* if we don't refresh, what's the point of scrolling? */
4791 if (!is_redrawing(infoPtr)) return;
4793 assert (abs(dir) == 1);
4795 /* arrange icons if autoarrange is on */
4796 if (is_autoarrange(infoPtr))
4798 BOOL arrange = TRUE;
4799 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4800 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4801 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4804 /* scrollbars need updating */
4805 LISTVIEW_UpdateScroll(infoPtr);
4807 /* figure out the item's position */
4808 if (uView == LVS_REPORT)
4809 nPerCol = infoPtr->nItemCount + 1;
4810 else if (uView == LVS_LIST)
4811 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4812 else /* LVS_ICON, or LVS_SMALLICON */
4815 nItemCol = nItem / nPerCol;
4816 nItemRow = nItem % nPerCol;
4817 LISTVIEW_GetOrigin(infoPtr, &Origin);
4819 /* move the items below up a slot */
4820 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4821 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4822 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4823 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4824 OffsetRect(&rcScroll, Origin.x, Origin.y);
4825 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
4826 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4828 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
4829 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4830 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4833 /* report has only that column, so we're done */
4834 if (uView == LVS_REPORT) return;
4836 /* now for LISTs, we have to deal with the columns to the right */
4837 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4839 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4840 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4841 OffsetRect(&rcScroll, Origin.x, Origin.y);
4842 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4843 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4844 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4849 * Removes an item from the listview control.
4852 * [I] infoPtr : valid pointer to the listview structure
4853 * [I] nItem : item index
4859 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4862 const UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4863 const BOOL is_icon = (uView == LVS_SMALLICON || uView == LVS_ICON);
4865 TRACE("(nItem=%d)\n", nItem);
4867 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4869 /* remove selection, and focus */
4871 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4872 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4874 /* send LVN_DELETEITEM notification. */
4875 if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
4877 /* we need to do this here, because we'll be deleting stuff */
4879 LISTVIEW_InvalidateItem(infoPtr, nItem);
4881 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4887 hdpaSubItems = DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4888 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4890 hdrItem = DPA_GetPtr(hdpaSubItems, i);
4891 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4894 DPA_Destroy(hdpaSubItems);
4899 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4900 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4903 infoPtr->nItemCount--;
4904 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4906 /* now is the invalidation fun */
4908 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4915 * Callback implementation for editlabel control
4918 * [I] infoPtr : valid pointer to the listview structure
4919 * [I] pszText : modified text
4920 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4926 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4928 HWND hwndSelf = infoPtr->hwndSelf;
4929 NMLVDISPINFOW dispInfo;
4930 INT editedItem = infoPtr->nEditLabelItem;
4933 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4935 infoPtr->nEditLabelItem = -1;
4937 ZeroMemory(&dispInfo, sizeof(dispInfo));
4938 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4939 dispInfo.item.iItem = editedItem;
4940 dispInfo.item.iSubItem = 0;
4941 dispInfo.item.stateMask = ~0;
4942 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4945 bSame = (lstrcmpW(dispInfo.item.pszText, pszText) == 0);
4948 LPWSTR tmp = textdupTtoW(pszText, FALSE);
4949 bSame = (lstrcmpW(dispInfo.item.pszText, tmp) == 0);
4950 textfreeT(tmp, FALSE);
4952 if (bSame) return TRUE;
4954 /* add the text from the edit in */
4955 dispInfo.item.mask |= LVIF_TEXT;
4956 dispInfo.item.pszText = pszText;
4957 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4959 /* Do we need to update the Item Text */
4960 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4961 if (!IsWindow(hwndSelf))
4963 if (!pszText) return TRUE;
4965 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4967 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, editedItem);
4968 ITEM_INFO* lpItem = DPA_GetPtr(hdpaSubItems, 0);
4969 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4971 LISTVIEW_InvalidateItem(infoPtr, editedItem);
4976 ZeroMemory(&dispInfo, sizeof(dispInfo));
4977 dispInfo.item.mask = LVIF_TEXT;
4978 dispInfo.item.iItem = editedItem;
4979 dispInfo.item.iSubItem = 0;
4980 dispInfo.item.pszText = pszText;
4981 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4982 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4987 * Begin in place editing of specified list view item
4990 * [I] infoPtr : valid pointer to the listview structure
4991 * [I] nItem : item index
4992 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4998 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
5000 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
5001 NMLVDISPINFOW dispInfo;
5003 HWND hwndSelf = infoPtr->hwndSelf;
5005 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
5007 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
5008 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5010 infoPtr->nEditLabelItem = nItem;
5012 /* Is the EditBox still there, if so remove it */
5013 if(infoPtr->hwndEdit != 0)
5015 SetFocus(infoPtr->hwndSelf);
5016 infoPtr->hwndEdit = 0;
5019 LISTVIEW_SetSelection(infoPtr, nItem);
5020 LISTVIEW_SetItemFocus(infoPtr, nItem);
5021 LISTVIEW_InvalidateItem(infoPtr, nItem);
5023 rect.left = LVIR_LABEL;
5024 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
5026 ZeroMemory(&dispInfo, sizeof(dispInfo));
5027 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
5028 dispInfo.item.iItem = nItem;
5029 dispInfo.item.iSubItem = 0;
5030 dispInfo.item.stateMask = ~0;
5031 dispInfo.item.pszText = szDispText;
5032 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
5033 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
5035 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
5036 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
5037 if (!infoPtr->hwndEdit) return 0;
5039 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
5041 if (!IsWindow(hwndSelf))
5043 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
5044 infoPtr->hwndEdit = 0;
5048 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
5049 SetFocus(infoPtr->hwndEdit);
5050 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
5051 return infoPtr->hwndEdit;
5057 * Ensures the specified item is visible, scrolling into view if necessary.
5060 * [I] infoPtr : valid pointer to the listview structure
5061 * [I] nItem : item index
5062 * [I] bPartial : partially or entirely visible
5068 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
5070 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5071 INT nScrollPosHeight = 0;
5072 INT nScrollPosWidth = 0;
5073 INT nHorzAdjust = 0;
5074 INT nVertAdjust = 0;
5077 RECT rcItem, rcTemp;
5079 rcItem.left = LVIR_BOUNDS;
5080 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
5082 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
5084 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
5086 /* scroll left/right, but in LVS_REPORT mode */
5087 if (uView == LVS_LIST)
5088 nScrollPosWidth = infoPtr->nItemWidth;
5089 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5090 nScrollPosWidth = 1;
5092 if (rcItem.left < infoPtr->rcList.left)
5095 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
5100 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
5104 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
5106 /* scroll up/down, but not in LVS_LIST mode */
5107 if (uView == LVS_REPORT)
5108 nScrollPosHeight = infoPtr->nItemHeight;
5109 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
5110 nScrollPosHeight = 1;
5112 if (rcItem.top < infoPtr->rcList.top)
5115 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
5120 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
5124 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
5126 if (nScrollPosWidth)
5128 INT diff = nHorzDiff / nScrollPosWidth;
5129 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
5130 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
5133 if (nScrollPosHeight)
5135 INT diff = nVertDiff / nScrollPosHeight;
5136 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
5137 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
5145 * Searches for an item with specific characteristics.
5148 * [I] hwnd : window handle
5149 * [I] nStart : base item index
5150 * [I] lpFindInfo : item information to look for
5153 * SUCCESS : index of item
5156 static INT LISTVIEW_FindItemW(const LISTVIEW_INFO *infoPtr, INT nStart,
5157 const LVFINDINFOW *lpFindInfo)
5159 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5160 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5161 BOOL bWrap = FALSE, bNearest = FALSE;
5162 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
5163 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
5164 POINT Position, Destination;
5167 /* Search in virtual listviews should be done by application, not by
5168 listview control, so we just send LVN_ODFINDITEMW and return the result */
5169 if (infoPtr->dwStyle & LVS_OWNERDATA)
5173 nmlv.iStart = nStart;
5174 nmlv.lvfi = *lpFindInfo;
5175 return notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
5178 if (!lpFindInfo || nItem < 0) return -1;
5181 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
5183 lvItem.mask |= LVIF_TEXT;
5184 lvItem.pszText = szDispText;
5185 lvItem.cchTextMax = DISP_TEXT_SIZE;
5188 if (lpFindInfo->flags & LVFI_WRAP)
5191 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
5192 (uView == LVS_ICON || uView ==LVS_SMALLICON))
5197 LISTVIEW_GetOrigin(infoPtr, &Origin);
5198 Destination.x = lpFindInfo->pt.x - Origin.x;
5199 Destination.y = lpFindInfo->pt.y - Origin.y;
5200 switch(lpFindInfo->vkDirection)
5202 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
5203 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
5204 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
5205 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
5206 case VK_HOME: Destination.x = Destination.y = 0; break;
5207 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5208 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5210 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
5211 Destination.x = rcArea.right;
5212 Destination.y = rcArea.bottom;
5214 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
5218 else Destination.x = Destination.y = 0;
5220 /* if LVFI_PARAM is specified, all other flags are ignored */
5221 if (lpFindInfo->flags & LVFI_PARAM)
5223 lvItem.mask |= LVIF_PARAM;
5225 lvItem.mask &= ~LVIF_TEXT;
5229 for (; nItem < nLast; nItem++)
5231 lvItem.iItem = nItem;
5232 lvItem.iSubItem = 0;
5233 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
5235 if (lvItem.mask & LVIF_PARAM)
5237 if (lpFindInfo->lParam == lvItem.lParam)
5243 if (lvItem.mask & LVIF_TEXT)
5245 if (lpFindInfo->flags & LVFI_PARTIAL)
5247 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
5251 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
5255 if (!bNearest) return nItem;
5257 /* This is very inefficient. To do a good job here,
5258 * we need a sorted array of (x,y) item positions */
5259 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5261 /* compute the distance^2 to the destination */
5262 xdist = Destination.x - Position.x;
5263 ydist = Destination.y - Position.y;
5264 dist = xdist * xdist + ydist * ydist;
5266 /* remember the distance, and item if it's closer */
5270 nNearestItem = nItem;
5277 nLast = min(nStart + 1, infoPtr->nItemCount);
5282 return nNearestItem;
5287 * Searches for an item with specific characteristics.
5290 * [I] hwnd : window handle
5291 * [I] nStart : base item index
5292 * [I] lpFindInfo : item information to look for
5295 * SUCCESS : index of item
5298 static INT LISTVIEW_FindItemA(const LISTVIEW_INFO *infoPtr, INT nStart,
5299 const LVFINDINFOA *lpFindInfo)
5301 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
5306 memcpy(&fiw, lpFindInfo, sizeof(fiw));
5307 if (hasText) fiw.psz = strW = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
5308 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
5309 textfreeT(strW, FALSE);
5315 * Retrieves the background image of the listview control.
5318 * [I] infoPtr : valid pointer to the listview structure
5319 * [O] lpBkImage : background image attributes
5325 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
5327 /* FIXME (listview, "empty stub!\n"); */
5333 * Retrieves column attributes.
5336 * [I] infoPtr : valid pointer to the listview structure
5337 * [I] nColumn : column index
5338 * [IO] lpColumn : column information
5339 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5340 * otherwise it is in fact a LPLVCOLUMNA
5346 static BOOL LISTVIEW_GetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
5348 COLUMN_INFO *lpColumnInfo;
5351 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5352 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
5354 /* initialize memory */
5355 ZeroMemory(&hdi, sizeof(hdi));
5357 if (lpColumn->mask & LVCF_TEXT)
5359 hdi.mask |= HDI_TEXT;
5360 hdi.pszText = lpColumn->pszText;
5361 hdi.cchTextMax = lpColumn->cchTextMax;
5364 if (lpColumn->mask & LVCF_IMAGE)
5365 hdi.mask |= HDI_IMAGE;
5367 if (lpColumn->mask & LVCF_ORDER)
5368 hdi.mask |= HDI_ORDER;
5370 if (lpColumn->mask & LVCF_SUBITEM)
5371 hdi.mask |= HDI_LPARAM;
5373 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
5375 if (lpColumn->mask & LVCF_FMT)
5376 lpColumn->fmt = lpColumnInfo->fmt;
5378 if (lpColumn->mask & LVCF_WIDTH)
5379 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
5381 if (lpColumn->mask & LVCF_IMAGE)
5382 lpColumn->iImage = hdi.iImage;
5384 if (lpColumn->mask & LVCF_ORDER)
5385 lpColumn->iOrder = hdi.iOrder;
5387 if (lpColumn->mask & LVCF_SUBITEM)
5388 lpColumn->iSubItem = hdi.lParam;
5394 static BOOL LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5396 TRACE("iCount=%d, lpiArray=%p\n", iCount, lpiArray);
5401 return SendMessageW(infoPtr->hwndHeader, HDM_GETORDERARRAY, iCount, (LPARAM)lpiArray);
5406 * Retrieves the column width.
5409 * [I] infoPtr : valid pointer to the listview structure
5410 * [I] int : column index
5413 * SUCCESS : column width
5416 static INT LISTVIEW_GetColumnWidth(const LISTVIEW_INFO *infoPtr, INT nColumn)
5418 INT nColumnWidth = 0;
5421 TRACE("nColumn=%d\n", nColumn);
5423 /* we have a 'column' in LIST and REPORT mode only */
5424 switch(infoPtr->dwStyle & LVS_TYPEMASK)
5427 nColumnWidth = infoPtr->nItemWidth;
5430 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDM_ITEMCHANGED.
5431 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
5432 * HDM_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
5434 * TODO: should we do the same in LVM_GETCOLUMN?
5436 hdItem.mask = HDI_WIDTH;
5437 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdItem))
5439 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr->hwndSelf, nColumn);
5442 nColumnWidth = hdItem.cxy;
5446 TRACE("nColumnWidth=%d\n", nColumnWidth);
5447 return nColumnWidth;
5452 * In list or report display mode, retrieves the number of items that can fit
5453 * vertically in the visible area. In icon or small icon display mode,
5454 * retrieves the total number of visible items.
5457 * [I] infoPtr : valid pointer to the listview structure
5460 * Number of fully visible items.
5462 static INT LISTVIEW_GetCountPerPage(const LISTVIEW_INFO *infoPtr)
5464 switch (infoPtr->dwStyle & LVS_TYPEMASK)
5468 return infoPtr->nItemCount;
5470 return LISTVIEW_GetCountPerColumn(infoPtr);
5472 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5480 * Retrieves an image list handle.
5483 * [I] infoPtr : valid pointer to the listview structure
5484 * [I] nImageList : image list identifier
5487 * SUCCESS : image list handle
5490 static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImageList)
5494 case LVSIL_NORMAL: return infoPtr->himlNormal;
5495 case LVSIL_SMALL: return infoPtr->himlSmall;
5496 case LVSIL_STATE: return infoPtr->himlState;
5501 /* LISTVIEW_GetISearchString */
5505 * Retrieves item attributes.
5508 * [I] hwnd : window handle
5509 * [IO] lpLVItem : item info
5510 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5511 * if FALSE, then lpLVItem is a LPLVITEMA.
5514 * This is the internal 'GetItem' interface -- it tries to
5515 * be smart and avoid text copies, if possible, by modifying
5516 * lpLVItem->pszText to point to the text string. Please note
5517 * that this is not always possible (e.g. OWNERDATA), so on
5518 * entry you *must* supply valid values for pszText, and cchTextMax.
5519 * The only difference to the documented interface is that upon
5520 * return, you should use *only* the lpLVItem->pszText, rather than
5521 * the buffer pointer you provided on input. Most code already does
5522 * that, so it's not a problem.
5523 * For the two cases when the text must be copied (that is,
5524 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5530 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5532 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5533 NMLVDISPINFOW dispInfo;
5539 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5541 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5544 if (lpLVItem->mask == 0) return TRUE;
5546 /* make a local copy */
5547 isubitem = lpLVItem->iSubItem;
5549 /* a quick optimization if all we're asked is the focus state
5550 * these queries are worth optimising since they are common,
5551 * and can be answered in constant time, without the heavy accesses */
5552 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5553 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5555 lpLVItem->state = 0;
5556 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5557 lpLVItem->state |= LVIS_FOCUSED;
5561 ZeroMemory(&dispInfo, sizeof(dispInfo));
5563 /* if the app stores all the data, handle it separately */
5564 if (infoPtr->dwStyle & LVS_OWNERDATA)
5566 dispInfo.item.state = 0;
5568 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5569 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5571 UINT mask = lpLVItem->mask;
5573 /* NOTE: copy only fields which we _know_ are initialized, some apps
5574 * depend on the uninitialized fields being 0 */
5575 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5576 dispInfo.item.iItem = lpLVItem->iItem;
5577 dispInfo.item.iSubItem = isubitem;
5578 if (lpLVItem->mask & LVIF_TEXT)
5580 if (lpLVItem->mask & LVIF_NORECOMPUTE)
5582 dispInfo.item.mask &= ~(LVIF_TEXT | LVIF_NORECOMPUTE);
5585 dispInfo.item.pszText = lpLVItem->pszText;
5586 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5589 if (lpLVItem->mask & LVIF_STATE)
5590 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5591 /* could be zeroed on LVIF_NORECOMPUTE case */
5592 if (dispInfo.item.mask != 0)
5594 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5595 dispInfo.item.stateMask = lpLVItem->stateMask;
5596 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5598 /* full size structure expected - _WIN32IE >= 0x560 */
5599 *lpLVItem = dispInfo.item;
5601 else if (lpLVItem->mask & LVIF_INDENT)
5603 /* indent member expected - _WIN32IE >= 0x300 */
5604 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5608 /* minimal structure expected */
5609 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5611 lpLVItem->mask = mask;
5612 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5616 /* make sure lParam is zeroed out */
5617 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5619 /* callback marked pointer required here */
5620 if ((lpLVItem->mask & LVIF_TEXT) && (lpLVItem->mask & LVIF_NORECOMPUTE))
5621 lpLVItem->pszText = LPSTR_TEXTCALLBACKW;
5623 /* we store only a little state, so if we're not asked, we're done */
5624 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5626 /* if focus is handled by us, report it */
5627 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5629 lpLVItem->state &= ~LVIS_FOCUSED;
5630 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5631 lpLVItem->state |= LVIS_FOCUSED;
5634 /* and do the same for selection, if we handle it */
5635 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5637 lpLVItem->state &= ~LVIS_SELECTED;
5638 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5639 lpLVItem->state |= LVIS_SELECTED;
5645 /* find the item and subitem structures before we proceed */
5646 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5647 lpItem = DPA_GetPtr(hdpaSubItems, 0);
5652 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5653 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5656 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5661 pItemHdr = &lpItem->hdr;
5663 /* Do we need to query the state from the app? */
5664 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5666 dispInfo.item.mask |= LVIF_STATE;
5667 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5670 /* Do we need to enquire about the image? */
5671 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5672 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5674 dispInfo.item.mask |= LVIF_IMAGE;
5675 dispInfo.item.iImage = I_IMAGECALLBACK;
5678 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5679 if ((lpLVItem->mask & LVIF_TEXT) && !(lpLVItem->mask & LVIF_NORECOMPUTE) &&
5680 !is_textW(pItemHdr->pszText))
5682 dispInfo.item.mask |= LVIF_TEXT;
5683 dispInfo.item.pszText = lpLVItem->pszText;
5684 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5685 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5686 *dispInfo.item.pszText = '\0';
5689 /* If we don't have all the requested info, query the application */
5690 if (dispInfo.item.mask != 0)
5692 dispInfo.item.iItem = lpLVItem->iItem;
5693 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5694 dispInfo.item.lParam = lpItem->lParam;
5695 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5696 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5699 /* we should not store values for subitems */
5700 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5702 /* Now, handle the iImage field */
5703 if (dispInfo.item.mask & LVIF_IMAGE)
5705 lpLVItem->iImage = dispInfo.item.iImage;
5706 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5707 pItemHdr->iImage = dispInfo.item.iImage;
5709 else if (lpLVItem->mask & LVIF_IMAGE)
5711 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5712 lpLVItem->iImage = pItemHdr->iImage;
5714 lpLVItem->iImage = 0;
5717 /* The pszText field */
5718 if (dispInfo.item.mask & LVIF_TEXT)
5720 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5721 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5723 lpLVItem->pszText = dispInfo.item.pszText;
5725 else if (lpLVItem->mask & LVIF_TEXT)
5727 /* if LVN_GETDISPINFO's disabled with LVIF_NORECOMPUTE return callback placeholder */
5728 if (isW || !is_textW(pItemHdr->pszText)) lpLVItem->pszText = pItemHdr->pszText;
5729 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5732 /* Next is the lParam field */
5733 if (dispInfo.item.mask & LVIF_PARAM)
5735 lpLVItem->lParam = dispInfo.item.lParam;
5736 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5737 lpItem->lParam = dispInfo.item.lParam;
5739 else if (lpLVItem->mask & LVIF_PARAM)
5740 lpLVItem->lParam = lpItem->lParam;
5742 /* if this is a subitem, we're done */
5743 if (isubitem) return TRUE;
5745 /* ... the state field (this one is different due to uCallbackmask) */
5746 if (lpLVItem->mask & LVIF_STATE)
5748 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5749 if (dispInfo.item.mask & LVIF_STATE)
5751 lpLVItem->state &= ~dispInfo.item.stateMask;
5752 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5754 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5756 lpLVItem->state &= ~LVIS_FOCUSED;
5757 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5758 lpLVItem->state |= LVIS_FOCUSED;
5760 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5762 lpLVItem->state &= ~LVIS_SELECTED;
5763 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5764 lpLVItem->state |= LVIS_SELECTED;
5768 /* and last, but not least, the indent field */
5769 if (lpLVItem->mask & LVIF_INDENT)
5770 lpLVItem->iIndent = lpItem->iIndent;
5777 * Retrieves item attributes.
5780 * [I] hwnd : window handle
5781 * [IO] lpLVItem : item info
5782 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5783 * if FALSE, then lpLVItem is a LPLVITEMA.
5786 * This is the external 'GetItem' interface -- it properly copies
5787 * the text in the provided buffer.
5793 static BOOL LISTVIEW_GetItemExtT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5798 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5801 pszText = lpLVItem->pszText;
5802 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5803 if (bResult && lpLVItem->pszText != pszText)
5805 if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW)
5806 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5808 pszText = LPSTR_TEXTCALLBACKW;
5810 lpLVItem->pszText = pszText;
5818 * Retrieves the position (upper-left) of the listview control item.
5819 * Note that for LVS_ICON style, the upper-left is that of the icon
5820 * and not the bounding box.
5823 * [I] infoPtr : valid pointer to the listview structure
5824 * [I] nItem : item index
5825 * [O] lpptPosition : coordinate information
5831 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5833 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5836 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5838 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5840 LISTVIEW_GetOrigin(infoPtr, &Origin);
5841 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5843 if (uView == LVS_ICON)
5845 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5846 lpptPosition->y += ICON_TOP_PADDING;
5848 lpptPosition->x += Origin.x;
5849 lpptPosition->y += Origin.y;
5851 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
5858 * Retrieves the bounding rectangle for a listview control item.
5861 * [I] infoPtr : valid pointer to the listview structure
5862 * [I] nItem : item index
5863 * [IO] lprc : bounding rectangle coordinates
5864 * lprc->left specifies the portion of the item for which the bounding
5865 * rectangle will be retrieved.
5867 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5868 * including the icon and label.
5871 * * Experiment shows that native control returns:
5872 * * width = min (48, length of text line)
5873 * * .left = position.x - (width - iconsize.cx)/2
5874 * * .right = .left + width
5875 * * height = #lines of text * ntmHeight + icon height + 8
5876 * * .top = position.y - 2
5877 * * .bottom = .top + height
5878 * * separation between items .y = itemSpacing.cy - height
5879 * * .x = itemSpacing.cx - width
5880 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5883 * * Experiment shows that native control returns:
5884 * * width = iconSize.cx + 16
5885 * * .left = position.x - (width - iconsize.cx)/2
5886 * * .right = .left + width
5887 * * height = iconSize.cy + 4
5888 * * .top = position.y - 2
5889 * * .bottom = .top + height
5890 * * separation between items .y = itemSpacing.cy - height
5891 * * .x = itemSpacing.cx - width
5892 * LVIR_LABEL Returns the bounding rectangle of the item text.
5895 * * Experiment shows that native control returns:
5896 * * width = text length
5897 * * .left = position.x - width/2
5898 * * .right = .left + width
5899 * * height = ntmH * linecount + 2
5900 * * .top = position.y + iconSize.cy + 6
5901 * * .bottom = .top + height
5902 * * separation between items .y = itemSpacing.cy - height
5903 * * .x = itemSpacing.cx - width
5904 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5905 * rectangles, but excludes columns in report view.
5912 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5913 * upon whether the window has the focus currently and on whether the item
5914 * is the one with the focus. Ensure that the control's record of which
5915 * item has the focus agrees with the items' records.
5917 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5919 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5920 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5921 BOOL doLabel = TRUE, oversizedBox = FALSE;
5922 POINT Position, Origin;
5925 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5927 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5929 LISTVIEW_GetOrigin(infoPtr, &Origin);
5930 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5932 /* Be smart and try to figure out the minimum we have to do */
5933 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5934 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5935 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5936 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5937 oversizedBox = TRUE;
5939 /* get what we need from the item before hand, so we make
5940 * only one request. This can speed up things, if data
5941 * is stored on the app side */
5943 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5944 if (doLabel) lvItem.mask |= LVIF_TEXT;
5945 lvItem.iItem = nItem;
5946 lvItem.iSubItem = 0;
5947 lvItem.pszText = szDispText;
5948 lvItem.cchTextMax = DISP_TEXT_SIZE;
5949 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5950 /* we got the state already up, simulate it here, to avoid a reget */
5951 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5953 lvItem.mask |= LVIF_STATE;
5954 lvItem.stateMask = LVIS_FOCUSED;
5955 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5958 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5959 lprc->left = LVIR_BOUNDS;
5963 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
5967 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc);
5971 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
5974 case LVIR_SELECTBOUNDS:
5975 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL);
5979 WARN("Unknown value: %d\n", lprc->left);
5983 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5985 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
5992 * Retrieves the spacing between listview control items.
5995 * [I] infoPtr : valid pointer to the listview structure
5996 * [IO] lprc : rectangle to receive the output
5997 * on input, lprc->top = nSubItem
5998 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
6000 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
6001 * not only those of the first column.
6002 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
6008 static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
6014 if (!lprc) return FALSE;
6016 nColumn = lprc->top;
6018 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
6019 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
6021 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
6023 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
6025 /* special case for header items */
6028 if (lprc->left != LVIR_BOUNDS)
6030 FIXME("Only LVIR_BOUNDS is implemented for header, got %d\n", lprc->left);
6034 if (infoPtr->hwndHeader)
6035 return Header_GetItemRect(infoPtr->hwndHeader, lprc->top, lprc);
6038 memset(lprc, 0, sizeof(RECT));
6043 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
6045 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6048 lvItem.iItem = nItem;
6049 lvItem.iSubItem = nColumn;
6051 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
6055 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
6060 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
6064 ERR("Unknown bounds=%d\n", lprc->left);
6068 OffsetRect(lprc, Position.x, Position.y);
6075 * Retrieves the width of a label.
6078 * [I] infoPtr : valid pointer to the listview structure
6081 * SUCCESS : string width (in pixels)
6084 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *infoPtr, INT nItem)
6086 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6089 TRACE("(nItem=%d)\n", nItem);
6091 lvItem.mask = LVIF_TEXT;
6092 lvItem.iItem = nItem;
6093 lvItem.iSubItem = 0;
6094 lvItem.pszText = szDispText;
6095 lvItem.cchTextMax = DISP_TEXT_SIZE;
6096 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6098 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6103 * Retrieves the spacing between listview control items.
6106 * [I] infoPtr : valid pointer to the listview structure
6107 * [I] bSmall : flag for small or large icon
6110 * Horizontal + vertical spacing
6112 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall)
6118 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6122 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
6123 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6125 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6132 * Retrieves the state of a listview control item.
6135 * [I] infoPtr : valid pointer to the listview structure
6136 * [I] nItem : item index
6137 * [I] uMask : state mask
6140 * State specified by the mask.
6142 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
6146 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6148 lvItem.iItem = nItem;
6149 lvItem.iSubItem = 0;
6150 lvItem.mask = LVIF_STATE;
6151 lvItem.stateMask = uMask;
6152 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6154 return lvItem.state & uMask;
6159 * Retrieves the text of a listview control item or subitem.
6162 * [I] hwnd : window handle
6163 * [I] nItem : item index
6164 * [IO] lpLVItem : item information
6165 * [I] isW : TRUE if lpLVItem is Unicode
6168 * SUCCESS : string length
6171 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6173 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6175 lpLVItem->mask = LVIF_TEXT;
6176 lpLVItem->iItem = nItem;
6177 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
6179 return textlenT(lpLVItem->pszText, isW);
6184 * Searches for an item based on properties + relationships.
6187 * [I] infoPtr : valid pointer to the listview structure
6188 * [I] nItem : item index
6189 * [I] uFlags : relationship flag
6192 * SUCCESS : item index
6195 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
6197 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6199 LVFINDINFOW lvFindInfo;
6200 INT nCountPerColumn;
6204 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
6205 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
6207 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6209 if (uFlags & LVNI_CUT)
6212 if (uFlags & LVNI_DROPHILITED)
6213 uMask |= LVIS_DROPHILITED;
6215 if (uFlags & LVNI_FOCUSED)
6216 uMask |= LVIS_FOCUSED;
6218 if (uFlags & LVNI_SELECTED)
6219 uMask |= LVIS_SELECTED;
6221 /* if we're asked for the focused item, that's only one,
6222 * so it's worth optimizing */
6223 if (uFlags & LVNI_FOCUSED)
6225 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
6226 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
6229 if (uFlags & LVNI_ABOVE)
6231 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6236 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6242 /* Special case for autoarrange - move 'til the top of a list */
6243 if (is_autoarrange(infoPtr))
6245 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6246 while (nItem - nCountPerRow >= 0)
6248 nItem -= nCountPerRow;
6249 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6254 lvFindInfo.flags = LVFI_NEARESTXY;
6255 lvFindInfo.vkDirection = VK_UP;
6256 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6257 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6259 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6264 else if (uFlags & LVNI_BELOW)
6266 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6268 while (nItem < infoPtr->nItemCount)
6271 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6277 /* Special case for autoarrange - move 'til the bottom of a list */
6278 if (is_autoarrange(infoPtr))
6280 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6281 while (nItem + nCountPerRow < infoPtr->nItemCount )
6283 nItem += nCountPerRow;
6284 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6289 lvFindInfo.flags = LVFI_NEARESTXY;
6290 lvFindInfo.vkDirection = VK_DOWN;
6291 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6292 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6294 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6299 else if (uFlags & LVNI_TOLEFT)
6301 if (uView == LVS_LIST)
6303 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6304 while (nItem - nCountPerColumn >= 0)
6306 nItem -= nCountPerColumn;
6307 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6311 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6313 /* Special case for autoarrange - move 'til the beginning of a row */
6314 if (is_autoarrange(infoPtr))
6316 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6317 while (nItem % nCountPerRow > 0)
6320 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6325 lvFindInfo.flags = LVFI_NEARESTXY;
6326 lvFindInfo.vkDirection = VK_LEFT;
6327 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6328 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6330 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6335 else if (uFlags & LVNI_TORIGHT)
6337 if (uView == LVS_LIST)
6339 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6340 while (nItem + nCountPerColumn < infoPtr->nItemCount)
6342 nItem += nCountPerColumn;
6343 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6347 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6349 /* Special case for autoarrange - move 'til the end of a row */
6350 if (is_autoarrange(infoPtr))
6352 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6353 while (nItem % nCountPerRow < nCountPerRow - 1 )
6356 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6361 lvFindInfo.flags = LVFI_NEARESTXY;
6362 lvFindInfo.vkDirection = VK_RIGHT;
6363 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6364 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6366 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6375 /* search by index */
6376 for (i = nItem; i < infoPtr->nItemCount; i++)
6378 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
6386 /* LISTVIEW_GetNumberOfWorkAreas */
6390 * Retrieves the origin coordinates when in icon or small icon display mode.
6393 * [I] infoPtr : valid pointer to the listview structure
6394 * [O] lpptOrigin : coordinate information
6399 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
6401 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6402 INT nHorzPos = 0, nVertPos = 0;
6403 SCROLLINFO scrollInfo;
6405 scrollInfo.cbSize = sizeof(SCROLLINFO);
6406 scrollInfo.fMask = SIF_POS;
6408 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6409 nHorzPos = scrollInfo.nPos;
6410 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6411 nVertPos = scrollInfo.nPos;
6413 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6415 lpptOrigin->x = infoPtr->rcList.left;
6416 lpptOrigin->y = infoPtr->rcList.top;
6417 if (uView == LVS_LIST)
6418 nHorzPos *= infoPtr->nItemWidth;
6419 else if (uView == LVS_REPORT)
6420 nVertPos *= infoPtr->nItemHeight;
6422 lpptOrigin->x -= nHorzPos;
6423 lpptOrigin->y -= nVertPos;
6425 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
6430 * Retrieves the width of a string.
6433 * [I] hwnd : window handle
6434 * [I] lpszText : text string to process
6435 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6438 * SUCCESS : string width (in pixels)
6441 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6446 if (is_textT(lpszText, isW))
6448 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6449 HDC hdc = GetDC(infoPtr->hwndSelf);
6450 HFONT hOldFont = SelectObject(hdc, hFont);
6453 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6455 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6456 SelectObject(hdc, hOldFont);
6457 ReleaseDC(infoPtr->hwndSelf, hdc);
6459 return stringSize.cx;
6464 * Determines which listview item is located at the specified position.
6467 * [I] infoPtr : valid pointer to the listview structure
6468 * [IO] lpht : hit test information
6469 * [I] subitem : fill out iSubItem.
6470 * [I] select : return the index only if the hit selects the item
6473 * (mm 20001022): We must not allow iSubItem to be touched, for
6474 * an app might pass only a structure with space up to iItem!
6475 * (MS Office 97 does that for instance in the file open dialog)
6478 * SUCCESS : item index
6481 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6483 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6484 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6485 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6486 POINT Origin, Position, opt;
6491 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6495 if (subitem) lpht->iSubItem = 0;
6497 if (infoPtr->rcList.left > lpht->pt.x)
6498 lpht->flags |= LVHT_TOLEFT;
6499 else if (infoPtr->rcList.right < lpht->pt.x)
6500 lpht->flags |= LVHT_TORIGHT;
6502 if (infoPtr->rcList.top > lpht->pt.y)
6503 lpht->flags |= LVHT_ABOVE;
6504 else if (infoPtr->rcList.bottom < lpht->pt.y)
6505 lpht->flags |= LVHT_BELOW;
6507 TRACE("lpht->flags=0x%x\n", lpht->flags);
6508 if (lpht->flags) return -1;
6510 lpht->flags |= LVHT_NOWHERE;
6512 LISTVIEW_GetOrigin(infoPtr, &Origin);
6514 /* first deal with the large items */
6515 rcSearch.left = lpht->pt.x;
6516 rcSearch.top = lpht->pt.y;
6517 rcSearch.right = rcSearch.left + 1;
6518 rcSearch.bottom = rcSearch.top + 1;
6520 iterator_frameditems(&i, infoPtr, &rcSearch);
6521 iterator_next(&i); /* go to first item in the sequence */
6523 iterator_destroy(&i);
6525 TRACE("lpht->iItem=%d\n", iItem);
6526 if (iItem == -1) return -1;
6528 lvItem.mask = LVIF_STATE | LVIF_TEXT;
6529 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6530 lvItem.stateMask = LVIS_STATEIMAGEMASK;
6531 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6532 lvItem.iItem = iItem;
6533 lvItem.iSubItem = 0;
6534 lvItem.pszText = szDispText;
6535 lvItem.cchTextMax = DISP_TEXT_SIZE;
6536 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6537 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6539 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
6540 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6541 opt.x = lpht->pt.x - Position.x - Origin.x;
6542 opt.y = lpht->pt.y - Position.y - Origin.y;
6544 if (uView == LVS_REPORT)
6548 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6549 UnionRect(&rcBounds, &rcBounds, &rcState);
6551 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6552 if (!PtInRect(&rcBounds, opt)) return -1;
6554 if (PtInRect(&rcIcon, opt))
6555 lpht->flags |= LVHT_ONITEMICON;
6556 else if (PtInRect(&rcLabel, opt))
6557 lpht->flags |= LVHT_ONITEMLABEL;
6558 else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6559 lpht->flags |= LVHT_ONITEMSTATEICON;
6560 if (lpht->flags & LVHT_ONITEM)
6561 lpht->flags &= ~LVHT_NOWHERE;
6563 TRACE("lpht->flags=0x%x\n", lpht->flags);
6564 if (uView == LVS_REPORT && subitem)
6568 rcBounds.right = rcBounds.left;
6569 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6571 rcBounds.left = rcBounds.right;
6572 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6573 if (PtInRect(&rcBounds, opt))
6581 if (select && !(uView == LVS_REPORT &&
6582 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6583 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6585 if (uView == LVS_REPORT)
6587 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6588 UnionRect(&rcBounds, &rcBounds, &rcState);
6590 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6592 return lpht->iItem = iItem;
6596 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6597 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6598 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6599 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6600 their own sort proc. when sending LVM_SORTITEMS.
6603 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6605 LVS_SORTXXX must be specified,
6606 LVS_OWNERDRAW is not set,
6607 <item>.pszText is not LPSTR_TEXTCALLBACK.
6609 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6610 are sorted based on item text..."
6612 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6614 ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
6615 ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
6616 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6618 /* if we're sorting descending, negate the return value */
6619 return (((const LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6624 * Inserts a new item in the listview control.
6627 * [I] infoPtr : valid pointer to the listview structure
6628 * [I] lpLVItem : item information
6629 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6632 * SUCCESS : new item index
6635 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6637 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6642 BOOL is_sorted, has_changed;
6644 HWND hwndSelf = infoPtr->hwndSelf;
6646 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6648 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6650 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6651 if (!lpLVItem || lpLVItem->iSubItem) return -1;
6653 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6655 if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
6657 /* insert item in listview control data structure */
6658 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6659 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6661 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6662 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6664 if (lpLVItem->iItem < 0 && !is_sorted) return -1;
6666 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6667 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6668 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6669 if (nItem == -1) goto fail;
6670 infoPtr->nItemCount++;
6672 /* shift indices first so they don't get tangled */
6673 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6675 /* set the item attributes */
6676 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6678 /* full size structure expected - _WIN32IE >= 0x560 */
6681 else if (lpLVItem->mask & LVIF_INDENT)
6683 /* indent member expected - _WIN32IE >= 0x300 */
6684 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6688 /* minimal structure expected */
6689 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6692 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6694 item.mask |= LVIF_STATE;
6695 item.stateMask |= LVIS_STATEIMAGEMASK;
6696 item.state &= ~LVIS_STATEIMAGEMASK;
6697 item.state |= INDEXTOSTATEIMAGEMASK(1);
6699 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6701 /* if we're sorted, sort the list, and update the index */
6704 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6705 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6706 assert(nItem != -1);
6709 /* make room for the position, if we are in the right mode */
6710 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6712 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6714 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6716 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6721 /* send LVN_INSERTITEM notification */
6722 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6724 nmlv.lParam = lpItem->lParam;
6725 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6726 if (!IsWindow(hwndSelf))
6729 /* align items (set position of each item) */
6730 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6734 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6735 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6737 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6739 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6742 /* now is the invalidation fun */
6743 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6747 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6748 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6749 infoPtr->nItemCount--;
6751 DPA_DeletePtr(hdpaSubItems, 0);
6752 DPA_Destroy (hdpaSubItems);
6759 * Redraws a range of items.
6762 * [I] infoPtr : valid pointer to the listview structure
6763 * [I] nFirst : first item
6764 * [I] nLast : last item
6770 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6774 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6775 max(nFirst, nLast) >= infoPtr->nItemCount)
6778 for (i = nFirst; i <= nLast; i++)
6779 LISTVIEW_InvalidateItem(infoPtr, i);
6786 * Scroll the content of a listview.
6789 * [I] infoPtr : valid pointer to the listview structure
6790 * [I] dx : horizontal scroll amount in pixels
6791 * [I] dy : vertical scroll amount in pixels
6798 * If the control is in report mode (LVS_REPORT) the control can
6799 * be scrolled only in line increments. "dy" will be rounded to the
6800 * nearest number of pixels that are a whole line. Ex: if line height
6801 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6802 * is passed, then the scroll will be 0. (per MSDN 7/2002)
6804 * For: (per experimentation with native control and CSpy ListView)
6805 * LVS_ICON dy=1 = 1 pixel (vertical only)
6807 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6809 * LVS_LIST dx=1 = 1 column (horizontal only)
6810 * but will only scroll 1 column per message
6811 * no matter what the value.
6812 * dy must be 0 or FALSE returned.
6813 * LVS_REPORT dx=1 = 1 pixel
6817 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6819 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6821 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6822 dy /= infoPtr->nItemHeight;
6825 if (dy != 0) return FALSE;
6832 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6833 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6840 * Sets the background color.
6843 * [I] infoPtr : valid pointer to the listview structure
6844 * [I] clrBk : background color
6850 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6852 TRACE("(clrBk=%x)\n", clrBk);
6854 if(infoPtr->clrBk != clrBk) {
6855 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6856 infoPtr->clrBk = clrBk;
6857 if (clrBk == CLR_NONE)
6858 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6860 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6861 LISTVIEW_InvalidateList(infoPtr);
6867 /* LISTVIEW_SetBkImage */
6869 /*** Helper for {Insert,Set}ColumnT *only* */
6870 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
6871 const LVCOLUMNW *lpColumn, BOOL isW)
6873 if (lpColumn->mask & LVCF_FMT)
6875 /* format member is valid */
6876 lphdi->mask |= HDI_FORMAT;
6878 /* set text alignment (leftmost column must be left-aligned) */
6879 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6880 lphdi->fmt |= HDF_LEFT;
6881 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6882 lphdi->fmt |= HDF_RIGHT;
6883 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6884 lphdi->fmt |= HDF_CENTER;
6886 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6887 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6889 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6891 lphdi->fmt |= HDF_IMAGE;
6892 lphdi->iImage = I_IMAGECALLBACK;
6896 if (lpColumn->mask & LVCF_WIDTH)
6898 lphdi->mask |= HDI_WIDTH;
6899 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6901 /* make it fill the remainder of the controls width */
6905 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6907 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6908 lphdi->cxy += rcHeader.right - rcHeader.left;
6911 /* retrieve the layout of the header */
6912 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6913 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
6915 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6918 lphdi->cxy = lpColumn->cx;
6921 if (lpColumn->mask & LVCF_TEXT)
6923 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6924 lphdi->fmt |= HDF_STRING;
6925 lphdi->pszText = lpColumn->pszText;
6926 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6929 if (lpColumn->mask & LVCF_IMAGE)
6931 lphdi->mask |= HDI_IMAGE;
6932 lphdi->iImage = lpColumn->iImage;
6935 if (lpColumn->mask & LVCF_ORDER)
6937 lphdi->mask |= HDI_ORDER;
6938 lphdi->iOrder = lpColumn->iOrder;
6945 * Inserts a new column.
6948 * [I] infoPtr : valid pointer to the listview structure
6949 * [I] nColumn : column index
6950 * [I] lpColumn : column information
6951 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6954 * SUCCESS : new column index
6957 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6958 const LVCOLUMNW *lpColumn, BOOL isW)
6960 COLUMN_INFO *lpColumnInfo;
6963 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6965 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6967 if (!lpColumn || nColumn < 0) return -1;
6968 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6970 ZeroMemory(&hdi, sizeof(HDITEMW));
6971 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6974 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
6975 * (can be seen in SPY) otherwise column never gets added.
6977 if (!(lpColumn->mask & LVCF_WIDTH)) {
6978 hdi.mask |= HDI_WIDTH;
6983 * when the iSubItem is available Windows copies it to the header lParam. It seems
6984 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
6986 if (lpColumn->mask & LVCF_SUBITEM)
6988 hdi.mask |= HDI_LPARAM;
6989 hdi.lParam = lpColumn->iSubItem;
6992 /* create header if not present */
6993 LISTVIEW_CreateHeader(infoPtr);
6994 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle) &&
6995 (LVS_REPORT == uView) && (WS_VISIBLE & infoPtr->dwStyle))
6997 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7000 /* insert item in header control */
7001 nNewColumn = SendMessageW(infoPtr->hwndHeader,
7002 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
7003 (WPARAM)nColumn, (LPARAM)&hdi);
7004 if (nNewColumn == -1) return -1;
7005 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
7007 /* create our own column info */
7008 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
7009 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
7011 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
7012 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
7014 /* now we have to actually adjust the data */
7015 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
7017 SUBITEM_INFO *lpSubItem;
7021 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
7023 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
7024 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
7026 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
7027 if (lpSubItem->iSubItem >= nNewColumn)
7028 lpSubItem->iSubItem++;
7033 /* make space for the new column */
7034 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7035 LISTVIEW_UpdateItemSize(infoPtr);
7040 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
7043 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
7051 * Sets the attributes of a header item.
7054 * [I] infoPtr : valid pointer to the listview structure
7055 * [I] nColumn : column index
7056 * [I] lpColumn : column attributes
7057 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
7063 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
7064 const LVCOLUMNW *lpColumn, BOOL isW)
7066 HDITEMW hdi, hdiget;
7069 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
7071 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7073 ZeroMemory(&hdi, sizeof(HDITEMW));
7074 if (lpColumn->mask & LVCF_FMT)
7076 hdi.mask |= HDI_FORMAT;
7077 hdiget.mask = HDI_FORMAT;
7078 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
7079 hdi.fmt = hdiget.fmt & HDF_STRING;
7081 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
7083 /* set header item attributes */
7084 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
7085 if (!bResult) return FALSE;
7087 if (lpColumn->mask & LVCF_FMT)
7089 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
7090 int oldFmt = lpColumnInfo->fmt;
7092 lpColumnInfo->fmt = lpColumn->fmt;
7093 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
7095 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7096 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
7105 * Sets the column order array
7108 * [I] infoPtr : valid pointer to the listview structure
7109 * [I] iCount : number of elements in column order array
7110 * [I] lpiArray : pointer to column order array
7116 static BOOL LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
7118 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7129 * Sets the width of a column
7132 * [I] infoPtr : valid pointer to the listview structure
7133 * [I] nColumn : column index
7134 * [I] cx : column width
7140 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
7142 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7143 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
7147 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
7149 /* set column width only if in report or list mode */
7150 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
7152 /* take care of invalid cx values */
7153 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
7154 else if (uView == LVS_LIST && cx < 1) return FALSE;
7156 /* resize all columns if in LVS_LIST mode */
7157 if(uView == LVS_LIST)
7159 infoPtr->nItemWidth = cx;
7160 LISTVIEW_InvalidateList(infoPtr);
7164 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7166 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
7171 lvItem.mask = LVIF_TEXT;
7173 lvItem.iSubItem = nColumn;
7174 lvItem.pszText = szDispText;
7175 lvItem.cchTextMax = DISP_TEXT_SIZE;
7176 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7178 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
7179 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
7180 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
7182 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
7183 max_cx += infoPtr->iconSize.cx;
7184 max_cx += TRAILING_LABEL_PADDING;
7187 /* autosize based on listview items width */
7188 if(cx == LVSCW_AUTOSIZE)
7190 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7192 /* if iCol is the last column make it fill the remainder of the controls width */
7193 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
7198 LISTVIEW_GetOrigin(infoPtr, &Origin);
7199 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
7201 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
7205 /* Despite what the MS docs say, if this is not the last
7206 column, then MS resizes the column to the width of the
7207 largest text string in the column, including headers
7208 and items. This is different from LVSCW_AUTOSIZE in that
7209 LVSCW_AUTOSIZE ignores the header string length. */
7212 /* retrieve header text */
7213 hdi.mask = HDI_TEXT;
7214 hdi.cchTextMax = DISP_TEXT_SIZE;
7215 hdi.pszText = szDispText;
7216 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
7218 HDC hdc = GetDC(infoPtr->hwndSelf);
7219 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
7222 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
7223 cx = size.cx + TRAILING_HEADER_PADDING;
7224 /* FIXME: Take into account the header image, if one is present */
7225 SelectObject(hdc, old_font);
7226 ReleaseDC(infoPtr->hwndSelf, hdc);
7228 cx = max (cx, max_cx);
7232 if (cx < 0) return FALSE;
7234 /* call header to update the column change */
7235 hdi.mask = HDI_WIDTH;
7237 TRACE("hdi.cxy=%d\n", hdi.cxy);
7238 return Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7242 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
7245 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
7248 HBITMAP hbm_im, hbm_mask, hbm_orig;
7250 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
7251 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
7254 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
7255 ILC_COLOR | ILC_MASK, 2, 2);
7256 hdc_wnd = GetDC(infoPtr->hwndSelf);
7257 hdc = CreateCompatibleDC(hdc_wnd);
7258 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
7259 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
7260 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
7262 rc.left = rc.top = 0;
7263 rc.right = GetSystemMetrics(SM_CXSMICON);
7264 rc.bottom = GetSystemMetrics(SM_CYSMICON);
7266 hbm_orig = SelectObject(hdc, hbm_mask);
7267 FillRect(hdc, &rc, hbr_white);
7268 InflateRect(&rc, -2, -2);
7269 FillRect(hdc, &rc, hbr_black);
7271 SelectObject(hdc, hbm_im);
7272 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
7273 SelectObject(hdc, hbm_orig);
7274 ImageList_Add(himl, hbm_im, hbm_mask);
7276 SelectObject(hdc, hbm_im);
7277 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
7278 SelectObject(hdc, hbm_orig);
7279 ImageList_Add(himl, hbm_im, hbm_mask);
7281 DeleteObject(hbm_mask);
7282 DeleteObject(hbm_im);
7290 * Sets the extended listview style.
7293 * [I] infoPtr : valid pointer to the listview structure
7295 * [I] dwStyle : style
7298 * SUCCESS : previous style
7301 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwExStyle)
7303 DWORD dwOldExStyle = infoPtr->dwLvExStyle;
7307 infoPtr->dwLvExStyle = (dwOldExStyle & ~dwMask) | (dwExStyle & dwMask);
7309 infoPtr->dwLvExStyle = dwExStyle;
7311 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_CHECKBOXES)
7313 HIMAGELIST himl = 0;
7314 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7317 item.mask = LVIF_STATE;
7318 item.stateMask = LVIS_STATEIMAGEMASK;
7319 item.state = INDEXTOSTATEIMAGEMASK(1);
7320 LISTVIEW_SetItemState(infoPtr, -1, &item);
7322 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
7324 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
7327 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_HEADERDRAGDROP)
7331 /* if not already created */
7332 LISTVIEW_CreateHeader(infoPtr);
7334 dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
7335 if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
7336 dwStyle |= HDS_DRAGDROP;
7338 dwStyle &= ~HDS_DRAGDROP;
7339 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
7342 /* GRIDLINES adds decoration at top so changes sizes */
7343 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_GRIDLINES)
7345 LISTVIEW_UpdateSize(infoPtr);
7349 LISTVIEW_InvalidateList(infoPtr);
7350 return dwOldExStyle;
7355 * Sets the new hot cursor used during hot tracking and hover selection.
7358 * [I] infoPtr : valid pointer to the listview structure
7359 * [I] hCursor : the new hot cursor handle
7362 * Returns the previous hot cursor
7364 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
7366 HCURSOR oldCursor = infoPtr->hHotCursor;
7368 infoPtr->hHotCursor = hCursor;
7376 * Sets the hot item index.
7379 * [I] infoPtr : valid pointer to the listview structure
7380 * [I] iIndex : index
7383 * SUCCESS : previous hot item index
7384 * FAILURE : -1 (no hot item)
7386 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
7388 INT iOldIndex = infoPtr->nHotItem;
7390 infoPtr->nHotItem = iIndex;
7398 * Sets the amount of time the cursor must hover over an item before it is selected.
7401 * [I] infoPtr : valid pointer to the listview structure
7402 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7405 * Returns the previous hover time
7407 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
7409 DWORD oldHoverTime = infoPtr->dwHoverTime;
7411 infoPtr->dwHoverTime = dwHoverTime;
7413 return oldHoverTime;
7418 * Sets spacing for icons of LVS_ICON style.
7421 * [I] infoPtr : valid pointer to the listview structure
7422 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7423 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7426 * MAKELONG(oldcx, oldcy)
7428 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
7430 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7431 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7433 TRACE("requested=(%d,%d)\n", cx, cy);
7435 /* this is supported only for LVS_ICON style */
7436 if (uView != LVS_ICON) return oldspacing;
7438 /* set to defaults, if instructed to */
7439 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
7440 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
7442 /* if 0 then compute width
7443 * FIXME: Should scan each item and determine max width of
7444 * icon or label, then make that the width */
7446 cx = infoPtr->iconSpacing.cx;
7448 /* if 0 then compute height */
7450 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
7451 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
7454 infoPtr->iconSpacing.cx = cx;
7455 infoPtr->iconSpacing.cy = cy;
7457 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7458 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
7459 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7460 infoPtr->ntmHeight);
7462 /* these depend on the iconSpacing */
7463 LISTVIEW_UpdateItemSize(infoPtr);
7468 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
7472 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
7479 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
7480 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7489 * [I] infoPtr : valid pointer to the listview structure
7490 * [I] nType : image list type
7491 * [I] himl : image list handle
7494 * SUCCESS : old image list
7497 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7499 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7500 INT oldHeight = infoPtr->nItemHeight;
7501 HIMAGELIST himlOld = 0;
7503 TRACE("(nType=%d, himl=%p\n", nType, himl);
7508 himlOld = infoPtr->himlNormal;
7509 infoPtr->himlNormal = himl;
7510 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7511 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7515 himlOld = infoPtr->himlSmall;
7516 infoPtr->himlSmall = himl;
7517 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7521 himlOld = infoPtr->himlState;
7522 infoPtr->himlState = himl;
7523 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7524 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7528 ERR("Unknown icon type=%d\n", nType);
7532 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7533 if (infoPtr->nItemHeight != oldHeight)
7534 LISTVIEW_UpdateScroll(infoPtr);
7541 * Preallocates memory (does *not* set the actual count of items !)
7544 * [I] infoPtr : valid pointer to the listview structure
7545 * [I] nItems : item count (projected number of items to allocate)
7546 * [I] dwFlags : update flags
7552 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7554 TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
7556 if (infoPtr->dwStyle & LVS_OWNERDATA)
7558 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7559 INT nOldCount = infoPtr->nItemCount;
7561 if (nItems < nOldCount)
7563 RANGE range = { nItems, nOldCount };
7564 ranges_del(infoPtr->selectionRanges, range);
7565 if (infoPtr->nFocusedItem >= nItems)
7567 LISTVIEW_SetItemFocus(infoPtr, -1);
7568 SetRectEmpty(&infoPtr->rcFocus);
7572 infoPtr->nItemCount = nItems;
7573 LISTVIEW_UpdateScroll(infoPtr);
7575 /* the flags are valid only in ownerdata report and list modes */
7576 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7578 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7579 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7581 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7582 LISTVIEW_InvalidateList(infoPtr);
7589 LISTVIEW_GetOrigin(infoPtr, &Origin);
7590 nFrom = min(nOldCount, nItems);
7591 nTo = max(nOldCount, nItems);
7593 if (uView == LVS_REPORT)
7596 rcErase.top = nFrom * infoPtr->nItemHeight;
7597 rcErase.right = infoPtr->nItemWidth;
7598 rcErase.bottom = nTo * infoPtr->nItemHeight;
7599 OffsetRect(&rcErase, Origin.x, Origin.y);
7600 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7601 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7605 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7607 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7608 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7609 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7610 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7611 OffsetRect(&rcErase, Origin.x, Origin.y);
7612 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7613 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7615 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7617 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7618 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7619 OffsetRect(&rcErase, Origin.x, Origin.y);
7620 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7621 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7627 /* According to MSDN for non-LVS_OWNERDATA this is just
7628 * a performance issue. The control allocates its internal
7629 * data structures for the number of items specified. It
7630 * cuts down on the number of memory allocations. Therefore
7631 * we will just issue a WARN here
7633 WARN("for non-ownerdata performance option not implemented.\n");
7641 * Sets the position of an item.
7644 * [I] infoPtr : valid pointer to the listview structure
7645 * [I] nItem : item index
7646 * [I] pt : coordinate
7652 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7654 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7657 TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7659 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7660 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7662 LISTVIEW_GetOrigin(infoPtr, &Origin);
7664 /* This point value seems to be an undocumented feature.
7665 * The best guess is that it means either at the origin,
7666 * or at true beginning of the list. I will assume the origin. */
7667 if ((pt.x == -1) && (pt.y == -1))
7670 if (uView == LVS_ICON)
7672 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7673 pt.y -= ICON_TOP_PADDING;
7678 infoPtr->bAutoarrange = FALSE;
7680 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7685 * Sets the state of one or many items.
7688 * [I] infoPtr : valid pointer to the listview structure
7689 * [I] nItem : item index
7690 * [I] lpLVItem : item or subitem info
7696 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7698 BOOL bResult = TRUE;
7701 lvItem.iItem = nItem;
7702 lvItem.iSubItem = 0;
7703 lvItem.mask = LVIF_STATE;
7704 lvItem.state = lpLVItem->state;
7705 lvItem.stateMask = lpLVItem->stateMask;
7706 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7710 /* apply to all items */
7711 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7712 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7715 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7718 * Update selection mark
7720 * Investigation on windows 2k showed that selection mark was updated
7721 * whenever a new selection was made, but if the selected item was
7722 * unselected it was not updated.
7724 * we are probably still not 100% accurate, but this at least sets the
7725 * proper selection mark when it is needed
7728 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7729 (infoPtr->nSelectionMark == -1))
7732 for (i = 0; i < infoPtr->nItemCount; i++)
7734 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7736 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7738 infoPtr->nSelectionMark = i;
7742 else if (ranges_contain(infoPtr->selectionRanges, i))
7744 infoPtr->nSelectionMark = i;
7755 * Sets the text of an item or subitem.
7758 * [I] hwnd : window handle
7759 * [I] nItem : item index
7760 * [I] lpLVItem : item or subitem info
7761 * [I] isW : TRUE if input is Unicode
7767 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7771 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7773 lvItem.iItem = nItem;
7774 lvItem.iSubItem = lpLVItem->iSubItem;
7775 lvItem.mask = LVIF_TEXT;
7776 lvItem.pszText = lpLVItem->pszText;
7777 lvItem.cchTextMax = lpLVItem->cchTextMax;
7779 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7781 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7786 * Set item index that marks the start of a multiple selection.
7789 * [I] infoPtr : valid pointer to the listview structure
7790 * [I] nIndex : index
7793 * Index number or -1 if there is no selection mark.
7795 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7797 INT nOldIndex = infoPtr->nSelectionMark;
7799 TRACE("(nIndex=%d)\n", nIndex);
7801 infoPtr->nSelectionMark = nIndex;
7808 * Sets the text background color.
7811 * [I] infoPtr : valid pointer to the listview structure
7812 * [I] clrTextBk : text background color
7818 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7820 TRACE("(clrTextBk=%x)\n", clrTextBk);
7822 if (infoPtr->clrTextBk != clrTextBk)
7824 infoPtr->clrTextBk = clrTextBk;
7825 LISTVIEW_InvalidateList(infoPtr);
7833 * Sets the text foreground color.
7836 * [I] infoPtr : valid pointer to the listview structure
7837 * [I] clrText : text color
7843 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7845 TRACE("(clrText=%x)\n", clrText);
7847 if (infoPtr->clrText != clrText)
7849 infoPtr->clrText = clrText;
7850 LISTVIEW_InvalidateList(infoPtr);
7858 * Sets new ToolTip window to ListView control.
7861 * [I] infoPtr : valid pointer to the listview structure
7862 * [I] hwndNewToolTip : handle to new ToolTip
7867 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7869 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7870 infoPtr->hwndToolTip = hwndNewToolTip;
7871 return hwndOldToolTip;
7876 * sets the Unicode character format flag for the control
7878 * [I] infoPtr :valid pointer to the listview structure
7879 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
7882 * Old Unicode Format
7884 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL fUnicode)
7886 BOOL rc = infoPtr->notifyFormat;
7887 infoPtr->notifyFormat = (fUnicode)?NFR_UNICODE:NFR_ANSI;
7891 /* LISTVIEW_SetWorkAreas */
7895 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMS
7898 * [I] first : pointer to first ITEM_INFO to compare
7899 * [I] second : pointer to second ITEM_INFO to compare
7900 * [I] lParam : HWND of control
7903 * if first comes before second : negative
7904 * if first comes after second : positive
7905 * if first and second are equivalent : zero
7907 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7909 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7910 ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
7911 ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
7913 /* Forward the call to the client defined callback */
7914 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7919 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMSEX
7922 * [I] first : pointer to first ITEM_INFO to compare
7923 * [I] second : pointer to second ITEM_INFO to compare
7924 * [I] lParam : HWND of control
7927 * if first comes before second : negative
7928 * if first comes after second : positive
7929 * if first and second are equivalent : zero
7931 static INT WINAPI LISTVIEW_CallBackCompareEx(LPVOID first, LPVOID second, LPARAM lParam)
7933 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7934 INT first_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, first );
7935 INT second_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, second );
7937 /* Forward the call to the client defined callback */
7938 return (infoPtr->pfnCompare)( first_idx, second_idx, infoPtr->lParamSort );
7943 * Sorts the listview items.
7946 * [I] infoPtr : valid pointer to the listview structure
7947 * [I] pfnCompare : application-defined value
7948 * [I] lParamSort : pointer to comparison callback
7949 * [I] IsEx : TRUE when LVM_SORTITEMSEX used
7955 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare,
7956 LPARAM lParamSort, BOOL IsEx)
7958 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7961 LPVOID selectionMarkItem = NULL;
7962 LPVOID focusedItem = NULL;
7965 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7967 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7969 if (!pfnCompare) return FALSE;
7970 if (!infoPtr->hdpaItems) return FALSE;
7972 /* if there are 0 or 1 items, there is no need to sort */
7973 if (infoPtr->nItemCount < 2) return TRUE;
7975 /* clear selection */
7976 ranges_clear(infoPtr->selectionRanges);
7978 /* save selection mark and focused item */
7979 if (infoPtr->nSelectionMark >= 0)
7980 selectionMarkItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark);
7981 if (infoPtr->nFocusedItem >= 0)
7982 focusedItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7984 infoPtr->pfnCompare = pfnCompare;
7985 infoPtr->lParamSort = lParamSort;
7987 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompareEx, (LPARAM)infoPtr);
7989 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7991 /* restore selection ranges */
7992 for (i=0; i < infoPtr->nItemCount; i++)
7994 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
7995 lpItem = DPA_GetPtr(hdpaSubItems, 0);
7997 if (lpItem->state & LVIS_SELECTED)
7998 ranges_additem(infoPtr->selectionRanges, i);
8000 /* restore selection mark and focused item */
8001 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
8002 infoPtr->nFocusedItem = DPA_GetPtrIndex(infoPtr->hdpaItems, focusedItem);
8004 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
8006 /* refresh the display */
8007 if (uView != LVS_ICON && uView != LVS_SMALLICON)
8008 LISTVIEW_InvalidateList(infoPtr);
8015 * Update theme handle after a theme change.
8018 * [I] infoPtr : valid pointer to the listview structure
8022 * FAILURE : something else
8024 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr)
8026 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8027 CloseThemeData(theme);
8028 OpenThemeData(infoPtr->hwndSelf, themeClass);
8034 * Updates an items or rearranges the listview control.
8037 * [I] infoPtr : valid pointer to the listview structure
8038 * [I] nItem : item index
8044 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
8046 TRACE("(nItem=%d)\n", nItem);
8048 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
8050 /* rearrange with default alignment style */
8051 if (is_autoarrange(infoPtr))
8052 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8054 LISTVIEW_InvalidateItem(infoPtr, nItem);
8061 * Draw the track line at the place defined in the infoPtr structure.
8062 * The line is drawn with a XOR pen so drawing the line for the second time
8063 * in the same place erases the line.
8066 * [I] infoPtr : valid pointer to the listview structure
8072 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
8078 if (infoPtr->xTrackLine == -1)
8081 if (!(hdc = GetDC(infoPtr->hwndSelf)))
8083 hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
8084 oldROP = SetROP2(hdc, R2_XORPEN);
8085 MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
8086 LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
8087 SetROP2(hdc, oldROP);
8088 SelectObject(hdc, hOldPen);
8089 ReleaseDC(infoPtr->hwndSelf, hdc);
8095 * Called when an edit control should be displayed. This function is called after
8096 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
8099 * [I] hwnd : Handle to the listview
8100 * [I] uMsg : WM_TIMER (ignored)
8101 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
8102 * [I] dwTimer : The elapsed time (ignored)
8107 static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
8109 DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
8110 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8112 KillTimer(hwnd, idEvent);
8113 editItem->fEnabled = FALSE;
8114 /* check if the item is still selected */
8115 if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
8116 LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
8121 * Creates the listview control - the WM_NCCREATE phase.
8124 * [I] hwnd : window handle
8125 * [I] lpcs : the create parameters
8131 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
8133 LISTVIEW_INFO *infoPtr;
8136 TRACE("(lpcs=%p)\n", lpcs);
8138 /* initialize info pointer */
8139 infoPtr = Alloc(sizeof(LISTVIEW_INFO));
8140 if (!infoPtr) return FALSE;
8142 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
8144 infoPtr->hwndSelf = hwnd;
8145 infoPtr->dwStyle = lpcs->style; /* Note: may be changed in WM_CREATE */
8146 /* determine the type of structures to use */
8147 infoPtr->hwndNotify = lpcs->hwndParent;
8148 /* infoPtr->notifyFormat will be filled in WM_CREATE */
8150 /* initialize color information */
8151 infoPtr->clrBk = CLR_NONE;
8152 infoPtr->clrText = CLR_DEFAULT;
8153 infoPtr->clrTextBk = CLR_DEFAULT;
8154 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
8156 /* set default values */
8157 infoPtr->nFocusedItem = -1;
8158 infoPtr->nSelectionMark = -1;
8159 infoPtr->nHotItem = -1;
8160 infoPtr->bRedraw = TRUE;
8161 infoPtr->bNoItemMetrics = TRUE;
8162 infoPtr->bDoChangeNotify = TRUE;
8163 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
8164 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
8165 infoPtr->nEditLabelItem = -1;
8166 infoPtr->dwHoverTime = -1; /* default system hover time */
8167 infoPtr->nMeasureItemHeight = 0;
8168 infoPtr->xTrackLine = -1; /* no track line */
8169 infoPtr->itemEdit.fEnabled = FALSE;
8170 infoPtr->iVersion = COMCTL32_VERSION;
8172 /* get default font (icon title) */
8173 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
8174 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
8175 infoPtr->hFont = infoPtr->hDefaultFont;
8176 LISTVIEW_SaveTextMetrics(infoPtr);
8178 /* allocate memory for the data structure */
8179 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
8180 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
8181 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
8182 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
8183 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
8187 DestroyWindow(infoPtr->hwndHeader);
8188 ranges_destroy(infoPtr->selectionRanges);
8189 DPA_Destroy(infoPtr->hdpaItems);
8190 DPA_Destroy(infoPtr->hdpaPosX);
8191 DPA_Destroy(infoPtr->hdpaPosY);
8192 DPA_Destroy(infoPtr->hdpaColumns);
8199 * Creates the listview control - the WM_CREATE phase. Most of the data is
8200 * already set up in LISTVIEW_NCCreate
8203 * [I] hwnd : window handle
8204 * [I] lpcs : the create parameters
8210 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
8212 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8213 UINT uView = lpcs->style & LVS_TYPEMASK;
8215 TRACE("(lpcs=%p)\n", lpcs);
8217 infoPtr->dwStyle = lpcs->style;
8218 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
8219 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
8221 if ((uView == LVS_REPORT) && (lpcs->style & WS_VISIBLE))
8223 if (LISTVIEW_CreateHeader(infoPtr) < 0) return -1;
8226 infoPtr->hwndHeader = 0;
8228 /* init item size to avoid division by 0 */
8229 LISTVIEW_UpdateItemSize (infoPtr);
8231 if (uView == LVS_REPORT)
8233 if (!(LVS_NOCOLUMNHEADER & lpcs->style) && (WS_VISIBLE & lpcs->style))
8235 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8237 LISTVIEW_UpdateScroll(infoPtr);
8240 OpenThemeData(hwnd, themeClass);
8242 /* initialize the icon sizes */
8243 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
8244 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
8250 * Destroys the listview control.
8253 * [I] infoPtr : valid pointer to the listview structure
8259 static LRESULT LISTVIEW_Destroy(const LISTVIEW_INFO *infoPtr)
8261 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8262 CloseThemeData(theme);
8268 * Enables the listview control.
8271 * [I] infoPtr : valid pointer to the listview structure
8272 * [I] bEnable : specifies whether to enable or disable the window
8278 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr, BOOL bEnable)
8280 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
8281 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8287 * Erases the background of the listview control.
8290 * [I] infoPtr : valid pointer to the listview structure
8291 * [I] hdc : device context handle
8297 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
8301 TRACE("(hdc=%p)\n", hdc);
8303 if (!GetClipBox(hdc, &rc)) return FALSE;
8305 /* for double buffered controls we need to do this during refresh */
8306 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
8308 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
8314 * Helper function for LISTVIEW_[HV]Scroll *only*.
8315 * Performs vertical/horizontal scrolling by a give amount.
8318 * [I] infoPtr : valid pointer to the listview structure
8319 * [I] dx : amount of horizontal scroll
8320 * [I] dy : amount of vertical scroll
8322 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
8324 /* now we can scroll the list */
8325 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
8326 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
8327 /* if we have focus, adjust rect */
8328 OffsetRect(&infoPtr->rcFocus, dx, dy);
8329 UpdateWindow(infoPtr->hwndSelf);
8334 * Performs vertical scrolling.
8337 * [I] infoPtr : valid pointer to the listview structure
8338 * [I] nScrollCode : scroll code
8339 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8340 * [I] hScrollWnd : scrollbar control window handle
8346 * SB_LINEUP/SB_LINEDOWN:
8347 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8348 * for LVS_REPORT is 1 line
8349 * for LVS_LIST cannot occur
8352 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8353 INT nScrollDiff, HWND hScrollWnd)
8355 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8356 INT nOldScrollPos, nNewScrollPos;
8357 SCROLLINFO scrollInfo;
8360 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8361 debugscrollcode(nScrollCode), nScrollDiff);
8363 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8365 scrollInfo.cbSize = sizeof(SCROLLINFO);
8366 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8368 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
8370 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
8372 nOldScrollPos = scrollInfo.nPos;
8373 switch (nScrollCode)
8379 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
8383 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
8387 nScrollDiff = -scrollInfo.nPage;
8391 nScrollDiff = scrollInfo.nPage;
8394 case SB_THUMBPOSITION:
8396 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8403 /* quit right away if pos isn't changing */
8404 if (nScrollDiff == 0) return 0;
8406 /* calculate new position, and handle overflows */
8407 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8408 if (nScrollDiff > 0) {
8409 if (nNewScrollPos < nOldScrollPos ||
8410 nNewScrollPos > scrollInfo.nMax)
8411 nNewScrollPos = scrollInfo.nMax;
8413 if (nNewScrollPos > nOldScrollPos ||
8414 nNewScrollPos < scrollInfo.nMin)
8415 nNewScrollPos = scrollInfo.nMin;
8418 /* set the new position, and reread in case it changed */
8419 scrollInfo.fMask = SIF_POS;
8420 scrollInfo.nPos = nNewScrollPos;
8421 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
8423 /* carry on only if it really changed */
8424 if (nNewScrollPos == nOldScrollPos) return 0;
8426 /* now adjust to client coordinates */
8427 nScrollDiff = nOldScrollPos - nNewScrollPos;
8428 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
8430 /* and scroll the window */
8431 scroll_list(infoPtr, 0, nScrollDiff);
8438 * Performs horizontal scrolling.
8441 * [I] infoPtr : valid pointer to the listview structure
8442 * [I] nScrollCode : scroll code
8443 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8444 * [I] hScrollWnd : scrollbar control window handle
8450 * SB_LINELEFT/SB_LINERIGHT:
8451 * for LVS_ICON, LVS_SMALLICON 1 pixel
8452 * for LVS_REPORT is 1 pixel
8453 * for LVS_LIST is 1 column --> which is a 1 because the
8454 * scroll is based on columns not pixels
8457 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8458 INT nScrollDiff, HWND hScrollWnd)
8460 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8461 INT nOldScrollPos, nNewScrollPos;
8462 SCROLLINFO scrollInfo;
8464 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8465 debugscrollcode(nScrollCode), nScrollDiff);
8467 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8469 scrollInfo.cbSize = sizeof(SCROLLINFO);
8470 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8472 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
8474 nOldScrollPos = scrollInfo.nPos;
8476 switch (nScrollCode)
8490 nScrollDiff = -scrollInfo.nPage;
8494 nScrollDiff = scrollInfo.nPage;
8497 case SB_THUMBPOSITION:
8499 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8506 /* quit right away if pos isn't changing */
8507 if (nScrollDiff == 0) return 0;
8509 /* calculate new position, and handle overflows */
8510 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8511 if (nScrollDiff > 0) {
8512 if (nNewScrollPos < nOldScrollPos ||
8513 nNewScrollPos > scrollInfo.nMax)
8514 nNewScrollPos = scrollInfo.nMax;
8516 if (nNewScrollPos > nOldScrollPos ||
8517 nNewScrollPos < scrollInfo.nMin)
8518 nNewScrollPos = scrollInfo.nMin;
8521 /* set the new position, and reread in case it changed */
8522 scrollInfo.fMask = SIF_POS;
8523 scrollInfo.nPos = nNewScrollPos;
8524 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
8526 /* carry on only if it really changed */
8527 if (nNewScrollPos == nOldScrollPos) return 0;
8529 if(uView == LVS_REPORT)
8530 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
8532 /* now adjust to client coordinates */
8533 nScrollDiff = nOldScrollPos - nNewScrollPos;
8534 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
8536 /* and scroll the window */
8537 scroll_list(infoPtr, nScrollDiff, 0);
8542 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
8544 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8545 INT gcWheelDelta = 0;
8546 INT pulScrollLines = 3;
8547 SCROLLINFO scrollInfo;
8549 TRACE("(wheelDelta=%d)\n", wheelDelta);
8551 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8552 gcWheelDelta -= wheelDelta;
8554 scrollInfo.cbSize = sizeof(SCROLLINFO);
8555 scrollInfo.fMask = SIF_POS;
8562 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8563 * should be fixed in the future.
8565 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
8566 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8570 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8572 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
8573 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8574 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
8579 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8590 * [I] infoPtr : valid pointer to the listview structure
8591 * [I] nVirtualKey : virtual key
8592 * [I] lKeyData : key data
8597 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8599 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8600 HWND hwndSelf = infoPtr->hwndSelf;
8602 NMLVKEYDOWN nmKeyDown;
8604 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
8606 /* send LVN_KEYDOWN notification */
8607 nmKeyDown.wVKey = nVirtualKey;
8608 nmKeyDown.flags = 0;
8609 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8610 if (!IsWindow(hwndSelf))
8613 switch (nVirtualKey)
8616 nItem = infoPtr->nFocusedItem;
8617 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
8618 toggle_checkbox_state(infoPtr, infoPtr->nFocusedItem);
8622 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8624 if (!notify(infoPtr, NM_RETURN)) return 0;
8625 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8630 if (infoPtr->nItemCount > 0)
8635 if (infoPtr->nItemCount > 0)
8636 nItem = infoPtr->nItemCount - 1;
8640 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8644 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8648 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8652 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8656 if (uView == LVS_REPORT)
8658 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8659 if (infoPtr->nFocusedItem == topidx)
8660 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8665 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8666 * LISTVIEW_GetCountPerRow(infoPtr);
8667 if(nItem < 0) nItem = 0;
8671 if (uView == LVS_REPORT)
8673 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8674 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8675 if (infoPtr->nFocusedItem == topidx + cnt - 1)
8676 nItem = infoPtr->nFocusedItem + cnt - 1;
8678 nItem = topidx + cnt - 1;
8681 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8682 * LISTVIEW_GetCountPerRow(infoPtr);
8683 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8687 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8688 LISTVIEW_KeySelection(infoPtr, nItem, nVirtualKey == VK_SPACE);
8698 * [I] infoPtr : valid pointer to the listview structure
8703 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8707 /* if we did not have the focus, there's nothing to do */
8708 if (!infoPtr->bFocus) return 0;
8710 /* send NM_KILLFOCUS notification */
8711 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8713 /* if we have a focus rectangle, get rid of it */
8714 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8716 /* set window focus flag */
8717 infoPtr->bFocus = FALSE;
8719 /* invalidate the selected items before resetting focus flag */
8720 LISTVIEW_InvalidateSelectedItems(infoPtr);
8727 * Processes double click messages (left mouse button).
8730 * [I] infoPtr : valid pointer to the listview structure
8731 * [I] wKey : key flag
8732 * [I] x,y : mouse coordinate
8737 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8739 LVHITTESTINFO htInfo;
8741 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8743 /* Cancel the item edition if any */
8744 if (infoPtr->itemEdit.fEnabled)
8746 KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
8747 infoPtr->itemEdit.fEnabled = FALSE;
8750 /* send NM_RELEASEDCAPTURE notification */
8751 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8756 /* send NM_DBLCLK notification */
8757 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8758 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8760 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8761 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8768 * Processes mouse down messages (left mouse button).
8771 * infoPtr [I ] valid pointer to the listview structure
8772 * wKey [I ] key flag
8773 * x,y [I ] mouse coordinate
8778 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8780 LVHITTESTINFO lvHitTestInfo;
8781 static BOOL bGroupSelect = TRUE;
8782 POINT pt = { x, y };
8785 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8787 /* send NM_RELEASEDCAPTURE notification */
8788 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8790 /* set left button down flag and record the click position */
8791 infoPtr->bLButtonDown = TRUE;
8792 infoPtr->ptClickPos = pt;
8793 infoPtr->bDragging = FALSE;
8795 lvHitTestInfo.pt.x = x;
8796 lvHitTestInfo.pt.y = y;
8798 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8799 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
8800 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8802 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8804 toggle_checkbox_state(infoPtr, nItem);
8808 if (infoPtr->dwStyle & LVS_SINGLESEL)
8810 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8811 infoPtr->nEditLabelItem = nItem;
8813 LISTVIEW_SetSelection(infoPtr, nItem);
8817 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8821 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8822 LISTVIEW_SetItemFocus(infoPtr, nItem);
8823 infoPtr->nSelectionMark = nItem;
8829 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8830 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8832 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8833 infoPtr->nSelectionMark = nItem;
8836 else if (wKey & MK_CONTROL)
8840 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8842 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8843 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8844 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8845 infoPtr->nSelectionMark = nItem;
8847 else if (wKey & MK_SHIFT)
8849 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8853 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8854 infoPtr->nEditLabelItem = nItem;
8856 /* set selection (clears other pre-existing selections) */
8857 LISTVIEW_SetSelection(infoPtr, nItem);
8861 if (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE)
8862 if(lvHitTestInfo.iItem != -1) notify_itemactivate(infoPtr,&lvHitTestInfo);
8866 /* remove all selections */
8867 LISTVIEW_DeselectAll(infoPtr);
8876 * Processes mouse up messages (left mouse button).
8879 * infoPtr [I ] valid pointer to the listview structure
8880 * wKey [I ] key flag
8881 * x,y [I ] mouse coordinate
8886 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8888 LVHITTESTINFO lvHitTestInfo;
8890 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8892 if (!infoPtr->bLButtonDown) return 0;
8894 lvHitTestInfo.pt.x = x;
8895 lvHitTestInfo.pt.y = y;
8897 /* send NM_CLICK notification */
8898 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8899 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8901 /* set left button flag */
8902 infoPtr->bLButtonDown = FALSE;
8904 if (infoPtr->bDragging)
8906 infoPtr->bDragging = FALSE;
8910 /* if we clicked on a selected item, edit the label */
8911 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8913 /* we want to make sure the user doesn't want to do a double click. So we will
8914 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
8916 infoPtr->itemEdit.fEnabled = TRUE;
8917 infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
8918 SetTimer(infoPtr->hwndSelf,
8919 (UINT_PTR)&infoPtr->itemEdit,
8920 GetDoubleClickTime(),
8921 LISTVIEW_DelayedEditItem);
8924 if (!infoPtr->bFocus)
8925 SetFocus(infoPtr->hwndSelf);
8932 * Destroys the listview control (called after WM_DESTROY).
8935 * [I] infoPtr : valid pointer to the listview structure
8940 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8944 /* delete all items */
8945 LISTVIEW_DeleteAllItems(infoPtr, TRUE);
8947 /* destroy data structure */
8948 DPA_Destroy(infoPtr->hdpaItems);
8949 DPA_Destroy(infoPtr->hdpaPosX);
8950 DPA_Destroy(infoPtr->hdpaPosY);
8951 DPA_Destroy(infoPtr->hdpaColumns);
8952 ranges_destroy(infoPtr->selectionRanges);
8954 /* destroy image lists */
8955 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8957 if (infoPtr->himlNormal)
8958 ImageList_Destroy(infoPtr->himlNormal);
8959 if (infoPtr->himlSmall)
8960 ImageList_Destroy(infoPtr->himlSmall);
8961 if (infoPtr->himlState)
8962 ImageList_Destroy(infoPtr->himlState);
8965 /* destroy font, bkgnd brush */
8967 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8968 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8970 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8972 /* free listview info pointer*/
8980 * Handles notifications from header.
8983 * [I] infoPtr : valid pointer to the listview structure
8984 * [I] nCtrlId : control identifier
8985 * [I] lpnmh : notification information
8990 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8992 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8993 HWND hwndSelf = infoPtr->hwndSelf;
8995 TRACE("(lpnmh=%p)\n", lpnmh);
8997 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8999 switch (lpnmh->hdr.code)
9004 COLUMN_INFO *lpColumnInfo;
9008 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
9011 /* remove the old line (if any) */
9012 LISTVIEW_DrawTrackLine(infoPtr);
9014 /* compute & draw the new line */
9015 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
9016 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
9017 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
9018 infoPtr->xTrackLine = x + ptOrigin.x;
9019 LISTVIEW_DrawTrackLine(infoPtr);
9025 /* remove the track line (if any) */
9026 LISTVIEW_DrawTrackLine(infoPtr);
9027 infoPtr->xTrackLine = -1;
9031 FIXME("Changing column order not implemented\n");
9034 case HDN_ITEMCHANGINGW:
9035 case HDN_ITEMCHANGINGA:
9036 return notify_forward_header(infoPtr, lpnmh);
9038 case HDN_ITEMCHANGEDW:
9039 case HDN_ITEMCHANGEDA:
9041 COLUMN_INFO *lpColumnInfo;
9044 notify_forward_header(infoPtr, lpnmh);
9045 if (!IsWindow(hwndSelf))
9048 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
9052 hdi.mask = HDI_WIDTH;
9053 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, &hdi)) return 0;
9057 cxy = lpnmh->pitem->cxy;
9059 /* determine how much we change since the last know position */
9060 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
9061 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
9064 lpColumnInfo->rcHeader.right += dx;
9065 if (lpnmh->iItem + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
9066 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
9069 /* only needs to update the scrolls */
9070 infoPtr->nItemWidth += dx;
9071 LISTVIEW_UpdateScroll(infoPtr);
9073 LISTVIEW_UpdateItemSize(infoPtr);
9074 if (uView == LVS_REPORT && is_redrawing(infoPtr))
9077 RECT rcCol = lpColumnInfo->rcHeader;
9079 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
9080 OffsetRect(&rcCol, ptOrigin.x, 0);
9082 rcCol.top = infoPtr->rcList.top;
9083 rcCol.bottom = infoPtr->rcList.bottom;
9085 /* resizing left-aligned columns leaves most of the left side untouched */
9086 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
9088 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
9091 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
9094 /* when shrinking the last column clear the now unused field */
9095 if (lpnmh->iItem == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
9101 /* deal with right from rightmost column area */
9102 right.left = rcCol.right;
9103 right.top = rcCol.top;
9104 right.bottom = rcCol.bottom;
9105 right.right = infoPtr->rcList.right;
9107 LISTVIEW_InvalidateRect(infoPtr, &right);
9110 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
9116 case HDN_ITEMCLICKW:
9117 case HDN_ITEMCLICKA:
9119 /* Handle sorting by Header Column */
9122 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
9124 nmlv.iSubItem = lpnmh->iItem;
9125 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
9126 notify_forward_header(infoPtr, lpnmh);
9130 case HDN_DIVIDERDBLCLICKW:
9131 case HDN_DIVIDERDBLCLICKA:
9132 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
9141 * Paint non-client area of control.
9144 * [I] infoPtr : valid pointer to the listview structureof the sender
9145 * [I] region : update region
9148 * TRUE - frame was painted
9149 * FALSE - call default window proc
9151 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
9153 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
9157 int cxEdge = GetSystemMetrics (SM_CXEDGE),
9158 cyEdge = GetSystemMetrics (SM_CYEDGE);
9160 if (!theme) return FALSE;
9162 GetWindowRect(infoPtr->hwndSelf, &r);
9164 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
9165 r.right - cxEdge, r.bottom - cyEdge);
9166 if (region != (HRGN)1)
9167 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
9168 OffsetRect(&r, -r.left, -r.top);
9170 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
9171 OffsetRect(&r, -r.left, -r.top);
9173 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
9174 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
9175 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
9176 ReleaseDC(infoPtr->hwndSelf, dc);
9178 /* Call default proc to get the scrollbars etc. painted */
9179 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
9186 * Determines the type of structure to use.
9189 * [I] infoPtr : valid pointer to the listview structureof the sender
9190 * [I] hwndFrom : listview window handle
9191 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
9196 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
9198 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
9200 if (nCommand == NF_REQUERY)
9201 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
9203 return infoPtr->notifyFormat;
9208 * Paints/Repaints the listview control.
9211 * [I] infoPtr : valid pointer to the listview structure
9212 * [I] hdc : device context handle
9217 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
9219 TRACE("(hdc=%p)\n", hdc);
9221 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
9223 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9225 infoPtr->bNoItemMetrics = FALSE;
9226 LISTVIEW_UpdateItemSize(infoPtr);
9227 if (uView == LVS_ICON || uView == LVS_SMALLICON)
9228 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9229 LISTVIEW_UpdateScroll(infoPtr);
9232 if (infoPtr->hwndHeader) UpdateWindow(infoPtr->hwndHeader);
9235 LISTVIEW_Refresh(infoPtr, hdc, NULL);
9240 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
9242 LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
9243 EndPaint(infoPtr->hwndSelf, &ps);
9252 * Paints/Repaints the listview control.
9255 * [I] infoPtr : valid pointer to the listview structure
9256 * [I] hdc : device context handle
9257 * [I] options : drawing options
9262 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
9264 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
9266 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
9269 if (options & PRF_ERASEBKGND)
9270 LISTVIEW_EraseBkgnd(infoPtr, hdc);
9272 if (options & PRF_CLIENT)
9273 LISTVIEW_Paint(infoPtr, hdc);
9281 * Processes double click messages (right mouse button).
9284 * [I] infoPtr : valid pointer to the listview structure
9285 * [I] wKey : key flag
9286 * [I] x,y : mouse coordinate
9291 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9293 LVHITTESTINFO lvHitTestInfo;
9295 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9297 /* send NM_RELEASEDCAPTURE notification */
9298 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9300 /* send NM_RDBLCLK notification */
9301 lvHitTestInfo.pt.x = x;
9302 lvHitTestInfo.pt.y = y;
9303 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9304 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
9311 * Processes mouse down messages (right mouse button).
9314 * [I] infoPtr : valid pointer to the listview structure
9315 * [I] wKey : key flag
9316 * [I] x,y : mouse coordinate
9321 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9323 LVHITTESTINFO lvHitTestInfo;
9326 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9328 /* send NM_RELEASEDCAPTURE notification */
9329 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9331 /* make sure the listview control window has the focus */
9332 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
9334 /* set right button down flag */
9335 infoPtr->bRButtonDown = TRUE;
9337 /* determine the index of the selected item */
9338 lvHitTestInfo.pt.x = x;
9339 lvHitTestInfo.pt.y = y;
9340 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
9342 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9344 LISTVIEW_SetItemFocus(infoPtr, nItem);
9345 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9346 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9347 LISTVIEW_SetSelection(infoPtr, nItem);
9351 LISTVIEW_DeselectAll(infoPtr);
9359 * Processes mouse up messages (right mouse button).
9362 * [I] infoPtr : valid pointer to the listview structure
9363 * [I] wKey : key flag
9364 * [I] x,y : mouse coordinate
9369 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9371 LVHITTESTINFO lvHitTestInfo;
9374 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9376 if (!infoPtr->bRButtonDown) return 0;
9378 /* set button flag */
9379 infoPtr->bRButtonDown = FALSE;
9381 /* Send NM_RCLICK notification */
9382 lvHitTestInfo.pt.x = x;
9383 lvHitTestInfo.pt.y = y;
9384 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9385 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
9387 /* Change to screen coordinate for WM_CONTEXTMENU */
9388 pt = lvHitTestInfo.pt;
9389 ClientToScreen(infoPtr->hwndSelf, &pt);
9391 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9392 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
9393 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
9404 * [I] infoPtr : valid pointer to the listview structure
9405 * [I] hwnd : window handle of window containing the cursor
9406 * [I] nHittest : hit-test code
9407 * [I] wMouseMsg : ideintifier of the mouse message
9410 * TRUE if cursor is set
9413 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
9415 LVHITTESTINFO lvHitTestInfo;
9417 if(!(LISTVIEW_isHotTracking(infoPtr))) return FALSE;
9419 if(!infoPtr->hHotCursor) return FALSE;
9421 GetCursorPos(&lvHitTestInfo.pt);
9422 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
9424 SetCursor(infoPtr->hHotCursor);
9434 * [I] infoPtr : valid pointer to the listview structure
9435 * [I] hwndLoseFocus : handle of previously focused window
9440 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
9442 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
9444 /* if we have the focus already, there's nothing to do */
9445 if (infoPtr->bFocus) return 0;
9447 /* send NM_SETFOCUS notification */
9448 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
9450 /* set window focus flag */
9451 infoPtr->bFocus = TRUE;
9453 /* put the focus rect back on */
9454 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
9456 /* redraw all visible selected items */
9457 LISTVIEW_InvalidateSelectedItems(infoPtr);
9467 * [I] infoPtr : valid pointer to the listview structure
9468 * [I] fRedraw : font handle
9469 * [I] fRedraw : redraw flag
9474 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
9476 HFONT oldFont = infoPtr->hFont;
9478 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
9480 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9481 if (infoPtr->hFont == oldFont) return 0;
9483 LISTVIEW_SaveTextMetrics(infoPtr);
9485 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
9487 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
9488 LISTVIEW_UpdateSize(infoPtr);
9489 LISTVIEW_UpdateScroll(infoPtr);
9492 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
9499 * Message handling for WM_SETREDRAW.
9500 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9503 * [I] infoPtr : valid pointer to the listview structure
9504 * [I] bRedraw: state of redraw flag
9507 * DefWinProc return value
9509 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
9511 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
9513 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9514 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
9516 infoPtr->bRedraw = bRedraw;
9518 if(!bRedraw) return 0;
9520 if (is_autoarrange(infoPtr))
9521 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9522 LISTVIEW_UpdateScroll(infoPtr);
9524 /* despite what the WM_SETREDRAW docs says, apps expect us
9525 * to invalidate the listview here... stupid! */
9526 LISTVIEW_InvalidateList(infoPtr);
9533 * Resizes the listview control. This function processes WM_SIZE
9534 * messages. At this time, the width and height are not used.
9537 * [I] infoPtr : valid pointer to the listview structure
9538 * [I] Width : new width
9539 * [I] Height : new height
9544 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
9546 RECT rcOld = infoPtr->rcList;
9548 TRACE("(width=%d, height=%d)\n", Width, Height);
9550 LISTVIEW_UpdateSize(infoPtr);
9551 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
9553 /* do not bother with display related stuff if we're not redrawing */
9554 if (!is_redrawing(infoPtr)) return 0;
9556 if (is_autoarrange(infoPtr))
9557 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9559 LISTVIEW_UpdateScroll(infoPtr);
9561 /* refresh all only for lists whose height changed significantly */
9562 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
9563 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
9564 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
9565 LISTVIEW_InvalidateList(infoPtr);
9572 * Sets the size information.
9575 * [I] infoPtr : valid pointer to the listview structure
9580 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
9582 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9584 TRACE("uView=%d, rcList(old)=%s\n", uView, wine_dbgstr_rect(&infoPtr->rcList));
9586 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
9588 if (uView == LVS_LIST)
9590 /* Apparently the "LIST" style is supposed to have the same
9591 * number of items in a column even if there is no scroll bar.
9592 * Since if a scroll bar already exists then the bottom is already
9593 * reduced, only reduce if the scroll bar does not currently exist.
9594 * The "2" is there to mimic the native control. I think it may be
9595 * related to either padding or edges. (GLA 7/2002)
9597 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
9598 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
9599 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
9601 else if (uView == LVS_REPORT)
9606 hl.prc = &infoPtr->rcList;
9608 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9609 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp.flags, wp.x, wp.y, wp.cx, wp.cy);
9610 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy,
9611 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9612 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9613 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy);
9615 infoPtr->rcList.top = max(wp.cy, 0);
9616 infoPtr->rcList.top += (infoPtr->dwLvExStyle & LVS_EX_GRIDLINES) ? 2 : 0;
9619 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
9624 * Processes WM_STYLECHANGED messages.
9627 * [I] infoPtr : valid pointer to the listview structure
9628 * [I] wStyleType : window style type (normal or extended)
9629 * [I] lpss : window style information
9634 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9635 const STYLESTRUCT *lpss)
9637 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9638 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9641 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9642 wStyleType, lpss->styleOld, lpss->styleNew);
9644 if (wStyleType != GWL_STYLE) return 0;
9646 /* FIXME: if LVS_NOSORTHEADER changed, update header */
9647 /* or LVS_SORT{AS,DES}CENDING */
9649 infoPtr->dwStyle = lpss->styleNew;
9651 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9652 ((lpss->styleNew & WS_HSCROLL) == 0))
9653 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9655 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9656 ((lpss->styleNew & WS_VSCROLL) == 0))
9657 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9659 if (uNewView != uOldView)
9661 SIZE oldIconSize = infoPtr->iconSize;
9664 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9665 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9667 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9668 SetRectEmpty(&infoPtr->rcFocus);
9670 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9671 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
9673 if (uNewView == LVS_ICON)
9675 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
9677 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9678 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9679 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
9682 else if (uNewView == LVS_REPORT)
9687 LISTVIEW_CreateHeader( infoPtr );
9689 hl.prc = &infoPtr->rcList;
9691 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9692 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
9693 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9694 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9697 LISTVIEW_UpdateItemSize(infoPtr);
9700 if (uNewView == LVS_REPORT)
9702 if ((lpss->styleOld ^ lpss->styleNew) & LVS_NOCOLUMNHEADER)
9704 if (lpss->styleNew & LVS_NOCOLUMNHEADER)
9706 /* Turn off the header control */
9707 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
9708 TRACE("Hide header control, was 0x%08x\n", style);
9709 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style | HDS_HIDDEN);
9711 /* Turn on the header control */
9712 if ((style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE)) & HDS_HIDDEN)
9714 TRACE("Show header control, was 0x%08x\n", style);
9715 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, (style & ~HDS_HIDDEN) | WS_VISIBLE);
9721 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9722 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9723 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9725 /* update the size of the client area */
9726 LISTVIEW_UpdateSize(infoPtr);
9728 /* add scrollbars if needed */
9729 LISTVIEW_UpdateScroll(infoPtr);
9731 /* invalidate client area + erase background */
9732 LISTVIEW_InvalidateList(infoPtr);
9739 * Processes WM_STYLECHANGING messages.
9742 * [I] infoPtr : valid pointer to the listview structure
9743 * [I] wStyleType : window style type (normal or extended)
9744 * [I0] lpss : window style information
9749 static INT LISTVIEW_StyleChanging(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9752 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9753 wStyleType, lpss->styleOld, lpss->styleNew);
9755 /* don't forward LVS_OWNERDATA only if not already set to */
9756 if ((lpss->styleNew ^ lpss->styleOld) & LVS_OWNERDATA)
9758 if (lpss->styleOld & LVS_OWNERDATA)
9759 lpss->styleNew |= LVS_OWNERDATA;
9761 lpss->styleNew &= ~LVS_OWNERDATA;
9769 * Processes WM_SHOWWINDOW messages.
9772 * [I] infoPtr : valid pointer to the listview structure
9773 * [I] bShown : window is being shown (FALSE when hidden)
9774 * [I] iStatus : window show status
9779 static LRESULT LISTVIEW_ShowWindow(LISTVIEW_INFO *infoPtr, BOOL bShown, INT iStatus)
9781 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9783 /* header delayed creation */
9784 if ((uView == LVS_REPORT) && bShown)
9786 LISTVIEW_CreateHeader(infoPtr);
9788 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle))
9789 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9797 * Processes CCM_GETVERSION messages.
9800 * [I] infoPtr : valid pointer to the listview structure
9805 static inline LRESULT LISTVIEW_GetVersion(LISTVIEW_INFO *infoPtr)
9807 return infoPtr->iVersion;
9812 * Processes CCM_SETVERSION messages.
9815 * [I] infoPtr : valid pointer to the listview structure
9816 * [I] iVersion : version to be set
9819 * -1 when requested version is greater than DLL version;
9820 * previous version otherwise
9822 static LRESULT LISTVIEW_SetVersion(LISTVIEW_INFO *infoPtr, DWORD iVersion)
9824 INT iOldVersion = infoPtr->iVersion;
9826 if (iVersion > COMCTL32_VERSION)
9829 infoPtr->iVersion = iVersion;
9831 TRACE("new version %d\n", iVersion);
9838 * Window procedure of the listview control.
9841 static LRESULT WINAPI
9842 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9844 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9846 TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg, wParam, lParam);
9848 if (!infoPtr && (uMsg != WM_NCCREATE))
9849 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9853 case LVM_APPROXIMATEVIEWRECT:
9854 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9855 LOWORD(lParam), HIWORD(lParam));
9857 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9859 /* case LVM_CANCELEDITLABEL: */
9861 case LVM_CREATEDRAGIMAGE:
9862 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9864 case LVM_DELETEALLITEMS:
9865 return LISTVIEW_DeleteAllItems(infoPtr, FALSE);
9867 case LVM_DELETECOLUMN:
9868 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9870 case LVM_DELETEITEM:
9871 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9873 case LVM_EDITLABELW:
9874 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9876 case LVM_EDITLABELA:
9877 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9879 /* case LVM_ENABLEGROUPVIEW: */
9881 case LVM_ENSUREVISIBLE:
9882 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9885 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9888 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9890 case LVM_GETBKCOLOR:
9891 return infoPtr->clrBk;
9893 /* case LVM_GETBKIMAGE: */
9895 case LVM_GETCALLBACKMASK:
9896 return infoPtr->uCallbackMask;
9898 case LVM_GETCOLUMNA:
9899 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9901 case LVM_GETCOLUMNW:
9902 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9904 case LVM_GETCOLUMNORDERARRAY:
9905 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9907 case LVM_GETCOLUMNWIDTH:
9908 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9910 case LVM_GETCOUNTPERPAGE:
9911 return LISTVIEW_GetCountPerPage(infoPtr);
9913 case LVM_GETEDITCONTROL:
9914 return (LRESULT)infoPtr->hwndEdit;
9916 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9917 return infoPtr->dwLvExStyle;
9919 /* case LVM_GETGROUPINFO: */
9921 /* case LVM_GETGROUPMETRICS: */
9924 return (LRESULT)infoPtr->hwndHeader;
9926 case LVM_GETHOTCURSOR:
9927 return (LRESULT)infoPtr->hHotCursor;
9929 case LVM_GETHOTITEM:
9930 return infoPtr->nHotItem;
9932 case LVM_GETHOVERTIME:
9933 return infoPtr->dwHoverTime;
9935 case LVM_GETIMAGELIST:
9936 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9938 /* case LVM_GETINSERTMARK: */
9940 /* case LVM_GETINSERTMARKCOLOR: */
9942 /* case LVM_GETINSERTMARKRECT: */
9944 case LVM_GETISEARCHSTRINGA:
9945 case LVM_GETISEARCHSTRINGW:
9946 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9950 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9953 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9955 case LVM_GETITEMCOUNT:
9956 return infoPtr->nItemCount;
9958 case LVM_GETITEMPOSITION:
9959 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9961 case LVM_GETITEMRECT:
9962 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9964 case LVM_GETITEMSPACING:
9965 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9967 case LVM_GETITEMSTATE:
9968 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9970 case LVM_GETITEMTEXTA:
9971 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9973 case LVM_GETITEMTEXTW:
9974 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9976 case LVM_GETNEXTITEM:
9977 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9979 case LVM_GETNUMBEROFWORKAREAS:
9980 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9984 if (!lParam) return FALSE;
9985 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT ||
9986 (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST) return FALSE;
9987 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9990 /* case LVM_GETOUTLINECOLOR: */
9992 /* case LVM_GETSELECTEDCOLUMN: */
9994 case LVM_GETSELECTEDCOUNT:
9995 return LISTVIEW_GetSelectedCount(infoPtr);
9997 case LVM_GETSELECTIONMARK:
9998 return infoPtr->nSelectionMark;
10000 case LVM_GETSTRINGWIDTHA:
10001 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
10003 case LVM_GETSTRINGWIDTHW:
10004 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
10006 case LVM_GETSUBITEMRECT:
10007 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
10009 case LVM_GETTEXTBKCOLOR:
10010 return infoPtr->clrTextBk;
10012 case LVM_GETTEXTCOLOR:
10013 return infoPtr->clrText;
10015 /* case LVM_GETTILEINFO: */
10017 /* case LVM_GETTILEVIEWINFO: */
10019 case LVM_GETTOOLTIPS:
10020 if( !infoPtr->hwndToolTip )
10021 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
10022 return (LRESULT)infoPtr->hwndToolTip;
10024 case LVM_GETTOPINDEX:
10025 return LISTVIEW_GetTopIndex(infoPtr);
10027 case LVM_GETUNICODEFORMAT:
10028 return (infoPtr->notifyFormat == NFR_UNICODE);
10030 /* case LVM_GETVIEW: */
10032 case LVM_GETVIEWRECT:
10033 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
10035 case LVM_GETWORKAREAS:
10036 FIXME("LVM_GETWORKAREAS: unimplemented\n");
10039 /* case LVM_HASGROUP: */
10042 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
10044 case LVM_INSERTCOLUMNA:
10045 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10047 case LVM_INSERTCOLUMNW:
10048 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10050 /* case LVM_INSERTGROUP: */
10052 /* case LVM_INSERTGROUPSORTED: */
10054 case LVM_INSERTITEMA:
10055 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
10057 case LVM_INSERTITEMW:
10058 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
10060 /* case LVM_INSERTMARKHITTEST: */
10062 /* case LVM_ISGROUPVIEWENABLED: */
10064 /* case LVM_MAPIDTOINDEX: */
10066 /* case LVM_MAPINDEXTOID: */
10068 /* case LVM_MOVEGROUP: */
10070 /* case LVM_MOVEITEMTOGROUP: */
10072 case LVM_REDRAWITEMS:
10073 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
10075 /* case LVM_REMOVEALLGROUPS: */
10077 /* case LVM_REMOVEGROUP: */
10080 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
10082 case LVM_SETBKCOLOR:
10083 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
10085 /* case LVM_SETBKIMAGE: */
10087 case LVM_SETCALLBACKMASK:
10088 infoPtr->uCallbackMask = (UINT)wParam;
10091 case LVM_SETCOLUMNA:
10092 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10094 case LVM_SETCOLUMNW:
10095 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10097 case LVM_SETCOLUMNORDERARRAY:
10098 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
10100 case LVM_SETCOLUMNWIDTH:
10101 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
10103 case LVM_SETEXTENDEDLISTVIEWSTYLE:
10104 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
10106 /* case LVM_SETGROUPINFO: */
10108 /* case LVM_SETGROUPMETRICS: */
10110 case LVM_SETHOTCURSOR:
10111 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
10113 case LVM_SETHOTITEM:
10114 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
10116 case LVM_SETHOVERTIME:
10117 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
10119 case LVM_SETICONSPACING:
10120 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10122 case LVM_SETIMAGELIST:
10123 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
10125 /* case LVM_SETINFOTIP: */
10127 /* case LVM_SETINSERTMARK: */
10129 /* case LVM_SETINSERTMARKCOLOR: */
10134 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
10135 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, (uMsg == LVM_SETITEMW));
10138 case LVM_SETITEMCOUNT:
10139 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
10141 case LVM_SETITEMPOSITION:
10144 pt.x = (short)LOWORD(lParam);
10145 pt.y = (short)HIWORD(lParam);
10146 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
10149 case LVM_SETITEMPOSITION32:
10150 if (lParam == 0) return FALSE;
10151 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
10153 case LVM_SETITEMSTATE:
10154 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
10156 case LVM_SETITEMTEXTA:
10157 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
10159 case LVM_SETITEMTEXTW:
10160 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
10162 /* case LVM_SETOUTLINECOLOR: */
10164 /* case LVM_SETSELECTEDCOLUMN: */
10166 case LVM_SETSELECTIONMARK:
10167 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
10169 case LVM_SETTEXTBKCOLOR:
10170 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
10172 case LVM_SETTEXTCOLOR:
10173 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
10175 /* case LVM_SETTILEINFO: */
10177 /* case LVM_SETTILEVIEWINFO: */
10179 /* case LVM_SETTILEWIDTH: */
10181 case LVM_SETTOOLTIPS:
10182 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
10184 case LVM_SETUNICODEFORMAT:
10185 return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
10187 /* case LVM_SETVIEW: */
10189 /* case LVM_SETWORKAREAS: */
10191 /* case LVM_SORTGROUPS: */
10193 case LVM_SORTITEMS:
10194 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam, FALSE);
10196 case LVM_SORTITEMSEX:
10197 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam, TRUE);
10199 case LVM_SUBITEMHITTEST:
10200 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
10203 return LISTVIEW_Update(infoPtr, (INT)wParam);
10205 case CCM_GETVERSION:
10206 return LISTVIEW_GetVersion(infoPtr);
10208 case CCM_SETVERSION:
10209 return LISTVIEW_SetVersion(infoPtr, wParam);
10212 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
10215 return LISTVIEW_Command(infoPtr, wParam, lParam);
10218 return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
10221 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
10224 return LISTVIEW_Destroy(infoPtr);
10227 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
10229 case WM_ERASEBKGND:
10230 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
10232 case WM_GETDLGCODE:
10233 return DLGC_WANTCHARS | DLGC_WANTARROWS;
10236 return (LRESULT)infoPtr->hFont;
10239 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10242 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
10245 return LISTVIEW_KillFocus(infoPtr);
10247 case WM_LBUTTONDBLCLK:
10248 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10250 case WM_LBUTTONDOWN:
10251 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10254 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10257 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10259 case WM_MOUSEHOVER:
10260 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10263 return LISTVIEW_NCDestroy(infoPtr);
10266 if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
10271 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
10272 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
10275 case WM_NOTIFYFORMAT:
10276 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
10278 case WM_PRINTCLIENT:
10279 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
10282 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
10284 case WM_RBUTTONDBLCLK:
10285 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10287 case WM_RBUTTONDOWN:
10288 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10291 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10294 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
10299 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
10302 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
10305 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
10307 case WM_SHOWWINDOW:
10308 LISTVIEW_ShowWindow(infoPtr, (BOOL)wParam, (INT)lParam);
10309 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10312 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10314 case WM_STYLECHANGED:
10315 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10317 case WM_STYLECHANGING:
10318 return LISTVIEW_StyleChanging(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10320 case WM_SYSCOLORCHANGE:
10321 COMCTL32_RefreshSysColors();
10324 /* case WM_TIMER: */
10325 case WM_THEMECHANGED:
10326 return LISTVIEW_ThemeChanged(infoPtr);
10329 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10331 case WM_MOUSEWHEEL:
10332 if (wParam & (MK_SHIFT | MK_CONTROL))
10333 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10334 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
10336 case WM_WINDOWPOSCHANGED:
10337 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
10339 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
10340 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
10341 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
10343 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
10345 MEASUREITEMSTRUCT mis;
10346 mis.CtlType = ODT_LISTVIEW;
10347 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
10351 mis.itemHeight= infoPtr->nItemHeight;
10352 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
10353 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
10354 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
10357 LISTVIEW_UpdateSize(infoPtr);
10358 LISTVIEW_UpdateScroll(infoPtr);
10360 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10362 /* case WM_WININICHANGE: */
10365 if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
10366 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
10369 /* call default window procedure */
10370 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10377 * Registers the window class.
10385 void LISTVIEW_Register(void)
10387 WNDCLASSW wndClass;
10389 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
10390 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
10391 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
10392 wndClass.cbClsExtra = 0;
10393 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
10394 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
10395 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
10396 wndClass.lpszClassName = WC_LISTVIEWW;
10397 RegisterClassW(&wndClass);
10402 * Unregisters the window class.
10410 void LISTVIEW_Unregister(void)
10412 UnregisterClassW(WC_LISTVIEWW, NULL);
10417 * Handle any WM_COMMAND messages
10420 * [I] infoPtr : valid pointer to the listview structure
10421 * [I] wParam : the first message parameter
10422 * [I] lParam : the second message parameter
10427 static LRESULT LISTVIEW_Command(const LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
10429 switch (HIWORD(wParam))
10434 * Adjust the edit window size
10436 WCHAR buffer[1024];
10437 HDC hdc = GetDC(infoPtr->hwndEdit);
10438 HFONT hFont, hOldFont = 0;
10442 if (!infoPtr->hwndEdit || !hdc) return 0;
10443 GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
10444 GetWindowRect(infoPtr->hwndEdit, &rect);
10446 /* Select font to get the right dimension of the string */
10447 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
10450 hOldFont = SelectObject(hdc, hFont);
10453 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
10455 TEXTMETRICW textMetric;
10457 /* Add Extra spacing for the next character */
10458 GetTextMetricsW(hdc, &textMetric);
10459 sz.cx += (textMetric.tmMaxCharWidth * 2);
10467 rect.bottom - rect.top,
10468 SWP_DRAWFRAME|SWP_NOMOVE);
10471 SelectObject(hdc, hOldFont);
10473 ReleaseDC(infoPtr->hwndEdit, hdc);
10479 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
10488 * Subclassed edit control windproc function
10491 * [I] hwnd : the edit window handle
10492 * [I] uMsg : the message that is to be processed
10493 * [I] wParam : first message parameter
10494 * [I] lParam : second message parameter
10495 * [I] isW : TRUE if input is Unicode
10500 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
10502 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
10503 BOOL cancel = FALSE;
10505 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10506 hwnd, uMsg, wParam, lParam, isW);
10510 case WM_GETDLGCODE:
10511 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
10518 WNDPROC editProc = infoPtr->EditWndProc;
10519 infoPtr->EditWndProc = 0;
10520 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
10521 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
10525 if (VK_ESCAPE == (INT)wParam)
10530 else if (VK_RETURN == (INT)wParam)
10534 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
10537 /* kill the edit */
10538 if (infoPtr->hwndEdit)
10540 LPWSTR buffer = NULL;
10542 infoPtr->hwndEdit = 0;
10545 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
10549 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
10551 if (isW) GetWindowTextW(hwnd, buffer, len+1);
10552 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
10556 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
10561 SendMessageW(hwnd, WM_CLOSE, 0, 0);
10567 * Subclassed edit control Unicode windproc function
10570 * [I] hwnd : the edit window handle
10571 * [I] uMsg : the message that is to be processed
10572 * [I] wParam : first message parameter
10573 * [I] lParam : second message parameter
10577 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10579 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
10584 * Subclassed edit control ANSI windproc function
10587 * [I] hwnd : the edit window handle
10588 * [I] uMsg : the message that is to be processed
10589 * [I] wParam : first message parameter
10590 * [I] lParam : second message parameter
10594 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10596 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
10601 * Creates a subclassed edit control
10604 * [I] infoPtr : valid pointer to the listview structure
10605 * [I] text : initial text for the edit
10606 * [I] style : the window style
10607 * [I] isW : TRUE if input is Unicode
10611 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
10612 INT x, INT y, INT width, INT height, BOOL isW)
10614 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
10619 TEXTMETRICW textMetric;
10620 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
10622 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
10624 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
10625 hdc = GetDC(infoPtr->hwndSelf);
10627 /* Select the font to get appropriate metric dimensions */
10628 if(infoPtr->hFont != 0)
10629 hOldFont = SelectObject(hdc, infoPtr->hFont);
10631 /*Get String Length in pixels */
10632 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
10634 /*Add Extra spacing for the next character */
10635 GetTextMetricsW(hdc, &textMetric);
10636 sz.cx += (textMetric.tmMaxCharWidth * 2);
10638 if(infoPtr->hFont != 0)
10639 SelectObject(hdc, hOldFont);
10641 ReleaseDC(infoPtr->hwndSelf, hdc);
10643 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10645 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10647 if (!hedit) return 0;
10649 infoPtr->EditWndProc = (WNDPROC)
10650 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
10651 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
10653 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);