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
9 * Copyright 2009 Nikolay Sivov
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 * This code was audited for completeness against the documented features
28 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
30 * Unless otherwise noted, we believe this code to be complete, as per
31 * the specification mentioned above.
32 * If you discover missing features, or bugs, please note them below.
36 * Default Message Processing
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_WINDOWPOSCHANGED: arrange the list items if the current view is icon
40 * or small icon and the LVS_AUTOARRANGE style is specified.
45 * -- Hot item handling, mouse hovering
46 * -- Workareas support
51 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
52 * -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
53 * -- LVA_SNAPTOGRID not implemented
54 * -- LISTVIEW_ApproximateViewRect partially implemented
55 * -- LISTVIEW_[GS]etColumnOrderArray stubs
56 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
57 * -- LISTVIEW_SetIconSpacing is incomplete
58 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
61 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
62 * linear in the number of items in the list, and this is
63 * unacceptable for large lists.
64 * -- if list is sorted by item text LISTVIEW_InsertItemT could use
65 * binary search to calculate item index (e.g. DPA_Search()).
66 * This requires sorted state to be reliably tracked in item modifiers.
67 * -- we should keep an ordered array of coordinates in iconic mode
68 * this would allow to frame items (iterator_frameditems),
69 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
76 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
83 * -- LVS_NOSCROLL (see Q137520)
87 * -- LVS_EX_BORDERSELECT
89 * -- LVS_EX_HEADERDRAGDROP
92 * -- LVS_EX_MULTIWORKAREAS
94 * -- LVS_EX_SIMPLESELECT
95 * -- LVS_EX_TWOCLICKACTIVATE
96 * -- LVS_EX_UNDERLINECOLD
97 * -- LVS_EX_UNDERLINEHOT
100 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
103 * -- LVN_MARQUEEBEGIN
109 * -- LVM_ENABLEGROUPVIEW
110 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
111 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
112 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
113 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
114 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
115 * -- LVM_GETINSERTMARKRECT
116 * -- LVM_GETNUMBEROFWORKAREAS
117 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
118 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
119 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
120 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
121 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
122 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
123 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
124 * -- LVM_INSERTGROUPSORTED
125 * -- LVM_INSERTMARKHITTEST
126 * -- LVM_ISGROUPVIEWENABLED
128 * -- LVM_MOVEITEMTOGROUP
130 * -- LVM_SETTILEWIDTH
134 * -- ListView_GetHoverTime, ListView_SetHoverTime
135 * -- ListView_GetISearchString
136 * -- ListView_GetNumberOfWorkAreas
137 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
142 * Known differences in message stream from native control (not known if
143 * these differences cause problems):
144 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
145 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
146 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
147 * processing for "USEDOUBLECLICKTIME".
151 #include "wine/port.h"
166 #include "commctrl.h"
167 #include "comctl32.h"
170 #include "wine/debug.h"
171 #include "wine/unicode.h"
173 WINE_DEFAULT_DEBUG_CHANNEL(listview);
175 /* make sure you set this to 0 for production use! */
176 #define DEBUG_RANGES 1
178 typedef struct tagCOLUMN_INFO
180 RECT rcHeader; /* tracks the header's rectangle */
181 int fmt; /* same as LVCOLUMN.fmt */
184 typedef struct tagITEMHDR
188 } ITEMHDR, *LPITEMHDR;
190 typedef struct tagSUBITEM_INFO
196 typedef struct tagITEM_ID ITEM_ID;
198 typedef struct tagITEM_INFO
209 UINT id; /* item id */
210 HDPA item; /* link to item data */
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 DWORD uView; /* current view available through LVM_[G,S]ETVIEW */
287 INT nItemCount; /* the number of items in the list */
288 HDPA hdpaItems; /* array ITEM_INFO pointers */
289 HDPA hdpaItemIds; /* array of ITEM_ID pointers */
290 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
291 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
292 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
293 POINT currIconPos; /* this is the position next icon will be placed */
294 PFNLVCOMPARE pfnCompare;
299 INT nLButtonDownItem; /* tracks item to reset multiselection on WM_LBUTTONUP */
303 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
305 DWORD lastKeyPressTimestamp;
307 INT nSearchParamLength;
308 WCHAR szSearchParam[ MAX_PATH ];
310 INT nMeasureItemHeight;
311 INT xTrackLine; /* The x coefficient of the track line or -1 if none */
312 DELAYED_ITEM_EDIT itemEdit; /* Pointer to this structure will be the timer ID */
314 DWORD iVersion; /* CCM_[G,S]ETVERSION */
320 /* How many we debug buffer to allocate */
321 #define DEBUG_BUFFERS 20
322 /* The size of a single debug bbuffer */
323 #define DEBUG_BUFFER_SIZE 256
325 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
326 #define SB_INTERNAL -1
328 /* maximum size of a label */
329 #define DISP_TEXT_SIZE 512
331 /* padding for items in list and small icon display modes */
332 #define WIDTH_PADDING 12
334 /* padding for items in list, report and small icon display modes */
335 #define HEIGHT_PADDING 1
337 /* offset of items in report display mode */
338 #define REPORT_MARGINX 2
340 /* padding for icon in large icon display mode
341 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
342 * that HITTEST will see.
343 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
344 * ICON_TOP_PADDING - sum of the two above.
345 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
346 * LABEL_HOR_PADDING - between text and sides of box
347 * LABEL_VERT_PADDING - between bottom of text and end of box
349 * ICON_LR_PADDING - additional width above icon size.
350 * ICON_LR_HALF - half of the above value
352 #define ICON_TOP_PADDING_NOTHITABLE 2
353 #define ICON_TOP_PADDING_HITABLE 2
354 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
355 #define ICON_BOTTOM_PADDING 4
356 #define LABEL_HOR_PADDING 5
357 #define LABEL_VERT_PADDING 7
358 #define ICON_LR_PADDING 16
359 #define ICON_LR_HALF (ICON_LR_PADDING/2)
361 /* default label width for items in list and small icon display modes */
362 #define DEFAULT_LABEL_WIDTH 40
364 /* default column width for items in list display mode */
365 #define DEFAULT_COLUMN_WIDTH 128
367 /* Size of "line" scroll for V & H scrolls */
368 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
370 /* Padding between image and label */
371 #define IMAGE_PADDING 2
373 /* Padding behind the label */
374 #define TRAILING_LABEL_PADDING 12
375 #define TRAILING_HEADER_PADDING 11
377 /* Border for the icon caption */
378 #define CAPTION_BORDER 2
380 /* Standard DrawText flags */
381 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
382 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
383 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
385 /* Image index from state */
386 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
388 /* The time in milliseconds to reset the search in the list */
389 #define KEY_DELAY 450
391 /* Dump the LISTVIEW_INFO structure to the debug channel */
392 #define LISTVIEW_DUMP(iP) do { \
393 TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
394 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
395 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
396 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
397 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
398 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
399 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
400 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
401 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
402 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
405 static const WCHAR themeClass[] = {'L','i','s','t','V','i','e','w',0};
408 * forward declarations
410 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *, LPLVITEMW, BOOL);
411 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *, INT, LPRECT);
412 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *, INT, LPPOINT);
413 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *, INT, LPPOINT);
414 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *, INT, LPRECT);
415 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *, INT);
416 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *, LPPOINT);
417 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *, LPRECT);
418 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
419 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
420 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *, LPCWSTR, BOOL);
421 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT, BOOL);
422 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *, INT, UINT);
423 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
424 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
425 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
426 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
427 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, BOOL);
428 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
429 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
430 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *, BOOL, BOOL);
432 /******** Text handling functions *************************************/
434 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
435 * text string. The string may be ANSI or Unicode, in which case
436 * the boolean isW tells us the type of the string.
438 * The name of the function tell what type of strings it expects:
439 * W: Unicode, T: ANSI/Unicode - function of isW
442 static inline BOOL is_textW(LPCWSTR text)
444 return text != NULL && text != LPSTR_TEXTCALLBACKW;
447 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
449 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
450 return is_textW(text);
453 static inline int textlenT(LPCWSTR text, BOOL isW)
455 return !is_textT(text, isW) ? 0 :
456 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
459 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
462 if (isSrcW) lstrcpynW(dest, src, max);
463 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
465 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
466 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
469 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
471 LPWSTR wstr = (LPWSTR)text;
473 if (!isW && is_textT(text, isW))
475 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
476 wstr = Alloc(len * sizeof(WCHAR));
477 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
479 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
483 static inline void textfreeT(LPWSTR wstr, BOOL isW)
485 if (!isW && is_textT(wstr, isW)) Free (wstr);
489 * dest is a pointer to a Unicode string
490 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
492 static BOOL textsetptrT(LPWSTR *dest, LPCWSTR src, BOOL isW)
496 if (src == LPSTR_TEXTCALLBACKW)
498 if (is_textW(*dest)) Free(*dest);
499 *dest = LPSTR_TEXTCALLBACKW;
503 LPWSTR pszText = textdupTtoW(src, isW);
504 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
505 bResult = Str_SetPtrW(dest, pszText);
506 textfreeT(pszText, isW);
512 * compares a Unicode to a Unicode/ANSI text string
514 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
516 if (!aw) return bt ? -1 : 0;
517 if (!bt) return aw ? 1 : 0;
518 if (aw == LPSTR_TEXTCALLBACKW)
519 return bt == LPSTR_TEXTCALLBACKW ? 1 : -1;
520 if (bt != LPSTR_TEXTCALLBACKW)
522 LPWSTR bw = textdupTtoW(bt, isW);
523 int r = bw ? lstrcmpW(aw, bw) : 1;
531 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
535 n = min(min(n, lstrlenW(s1)), lstrlenW(s2));
536 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
537 return res ? res - sizeof(WCHAR) : res;
540 /******** Debugging functions *****************************************/
542 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
544 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
545 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
548 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
550 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
551 n = min(textlenT(text, isW), n);
552 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
555 static char* debug_getbuf(void)
557 static int index = 0;
558 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
559 return buffers[index++ % DEBUG_BUFFERS];
562 static inline const char* debugrange(const RANGE *lprng)
564 if (!lprng) return "(null)";
565 return wine_dbg_sprintf("[%d, %d]", lprng->lower, lprng->upper);
568 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
570 char* buf = debug_getbuf(), *text = buf;
571 int len, size = DEBUG_BUFFER_SIZE;
573 if (pScrollInfo == NULL) return "(null)";
574 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
575 if (len == -1) goto end; buf += len; size -= len;
576 if (pScrollInfo->fMask & SIF_RANGE)
577 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
579 if (len == -1) goto end; buf += len; size -= len;
580 if (pScrollInfo->fMask & SIF_PAGE)
581 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
583 if (len == -1) goto end; buf += len; size -= len;
584 if (pScrollInfo->fMask & SIF_POS)
585 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
587 if (len == -1) goto end; buf += len; size -= len;
588 if (pScrollInfo->fMask & SIF_TRACKPOS)
589 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
591 if (len == -1) goto end; buf += len; size -= len;
594 buf = text + strlen(text);
596 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
600 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
602 if (!plvnm) return "(null)";
603 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
604 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
605 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
606 plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam);
609 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
611 char* buf = debug_getbuf(), *text = buf;
612 int len, size = DEBUG_BUFFER_SIZE;
614 if (lpLVItem == NULL) return "(null)";
615 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
616 if (len == -1) goto end; buf += len; size -= len;
617 if (lpLVItem->mask & LVIF_STATE)
618 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
620 if (len == -1) goto end; buf += len; size -= len;
621 if (lpLVItem->mask & LVIF_TEXT)
622 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
624 if (len == -1) goto end; buf += len; size -= len;
625 if (lpLVItem->mask & LVIF_IMAGE)
626 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
628 if (len == -1) goto end; buf += len; size -= len;
629 if (lpLVItem->mask & LVIF_PARAM)
630 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
632 if (len == -1) goto end; buf += len; size -= len;
633 if (lpLVItem->mask & LVIF_INDENT)
634 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
636 if (len == -1) goto end; buf += len; size -= len;
639 buf = text + strlen(text);
641 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
645 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
647 char* buf = debug_getbuf(), *text = buf;
648 int len, size = DEBUG_BUFFER_SIZE;
650 if (lpColumn == NULL) return "(null)";
651 len = snprintf(buf, size, "{");
652 if (len == -1) goto end; buf += len; size -= len;
653 if (lpColumn->mask & LVCF_SUBITEM)
654 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
656 if (len == -1) goto end; buf += len; size -= len;
657 if (lpColumn->mask & LVCF_FMT)
658 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
660 if (len == -1) goto end; buf += len; size -= len;
661 if (lpColumn->mask & LVCF_WIDTH)
662 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
664 if (len == -1) goto end; buf += len; size -= len;
665 if (lpColumn->mask & LVCF_TEXT)
666 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
668 if (len == -1) goto end; buf += len; size -= len;
669 if (lpColumn->mask & LVCF_IMAGE)
670 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
672 if (len == -1) goto end; buf += len; size -= len;
673 if (lpColumn->mask & LVCF_ORDER)
674 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
676 if (len == -1) goto end; buf += len; size -= len;
679 buf = text + strlen(text);
681 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
685 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
687 if (!lpht) return "(null)";
689 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
690 wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
693 /* Return the corresponding text for a given scroll value */
694 static inline LPCSTR debugscrollcode(int nScrollCode)
698 case SB_LINELEFT: return "SB_LINELEFT";
699 case SB_LINERIGHT: return "SB_LINERIGHT";
700 case SB_PAGELEFT: return "SB_PAGELEFT";
701 case SB_PAGERIGHT: return "SB_PAGERIGHT";
702 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
703 case SB_THUMBTRACK: return "SB_THUMBTRACK";
704 case SB_ENDSCROLL: return "SB_ENDSCROLL";
705 case SB_INTERNAL: return "SB_INTERNAL";
706 default: return "unknown";
711 /******** Notification functions ************************************/
713 static int get_ansi_notification(UINT unicodeNotificationCode)
715 switch (unicodeNotificationCode)
717 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
718 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
719 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
720 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
721 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
722 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
723 /* header forwards */
724 case HDN_TRACKW: return HDN_TRACKA;
725 case HDN_ENDTRACKW: return HDN_ENDTRACKA;
726 case HDN_BEGINDRAG: return HDN_BEGINDRAG;
727 case HDN_ENDDRAG: return HDN_ENDDRAG;
728 case HDN_ITEMCHANGINGW: return HDN_ITEMCHANGINGA;
729 case HDN_ITEMCHANGEDW: return HDN_ITEMCHANGEDA;
730 case HDN_ITEMCLICKW: return HDN_ITEMCLICKA;
731 case HDN_DIVIDERDBLCLICKW: return HDN_DIVIDERDBLCLICKA;
733 ERR("unknown notification %x\n", unicodeNotificationCode);
738 /* forwards header notifications to listview parent */
739 static LRESULT notify_forward_header(const LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
743 HD_TEXTFILTERA textfilter;
744 LPSTR text = NULL, filter = NULL;
747 /* on unicode format exit earlier */
748 if (infoPtr->notifyFormat == NFR_UNICODE)
749 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
750 (WPARAM)lpnmh->hdr.idFrom, (LPARAM)lpnmh);
752 /* header always supplies unicode notifications,
753 all we have to do is to convert strings to ANSI */
754 nmhA = *(NMHEADERA*)lpnmh;
757 hditema = *(HDITEMA*)lpnmh->pitem;
758 nmhA.pitem = &hditema;
759 /* convert item text */
760 if (lpnmh->pitem->mask & HDI_TEXT)
762 Str_SetPtrWtoA(&hditema.pszText, lpnmh->pitem->pszText);
763 text = hditema.pszText;
765 /* convert filter text */
766 if ((lpnmh->pitem->mask & HDI_FILTER) && (lpnmh->pitem->type == HDFT_ISSTRING) &&
767 lpnmh->pitem->pvFilter)
769 hditema.pvFilter = &textfilter;
770 textfilter = *(HD_TEXTFILTERA*)(lpnmh->pitem->pvFilter);
771 Str_SetPtrWtoA(&textfilter.pszText, ((HD_TEXTFILTERW*)lpnmh->pitem->pvFilter)->pszText);
772 filter = textfilter.pszText;
775 nmhA.hdr.code = get_ansi_notification(lpnmh->hdr.code);
777 ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
778 (WPARAM)nmhA.hdr.idFrom, (LPARAM)&nmhA);
787 static LRESULT notify_hdr(const LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
791 TRACE("(code=%d)\n", code);
793 pnmh->hwndFrom = infoPtr->hwndSelf;
794 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
796 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, pnmh->idFrom, (LPARAM)pnmh);
798 TRACE(" <= %ld\n", result);
803 static inline BOOL notify(const LISTVIEW_INFO *infoPtr, INT code)
806 HWND hwnd = infoPtr->hwndSelf;
807 notify_hdr(infoPtr, code, &nmh);
808 return IsWindow(hwnd);
811 static inline void notify_itemactivate(const LISTVIEW_INFO *infoPtr, const LVHITTESTINFO *htInfo)
822 item.mask = LVIF_PARAM|LVIF_STATE;
823 item.iItem = htInfo->iItem;
825 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
826 nmia.lParam = item.lParam;
827 nmia.uOldState = item.state;
828 nmia.uNewState = item.state | LVIS_ACTIVATING;
829 nmia.uChanged = LVIF_STATE;
832 nmia.iItem = htInfo->iItem;
833 nmia.iSubItem = htInfo->iSubItem;
834 nmia.ptAction = htInfo->pt;
836 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
837 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
838 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
840 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
843 static inline LRESULT notify_listview(const LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
845 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
846 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
849 static BOOL notify_click(const LISTVIEW_INFO *infoPtr, INT code, const LVHITTESTINFO *lvht)
853 HWND hwnd = infoPtr->hwndSelf;
855 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
856 ZeroMemory(&nmia, sizeof(nmia));
857 nmia.iItem = lvht->iItem;
858 nmia.iSubItem = lvht->iSubItem;
859 nmia.ptAction = lvht->pt;
860 item.mask = LVIF_PARAM;
861 item.iItem = lvht->iItem;
863 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmia.lParam = item.lParam;
864 notify_hdr(infoPtr, code, (LPNMHDR)&nmia);
865 return IsWindow(hwnd);
868 static BOOL notify_deleteitem(const LISTVIEW_INFO *infoPtr, INT nItem)
872 HWND hwnd = infoPtr->hwndSelf;
874 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
876 item.mask = LVIF_PARAM;
879 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
880 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
881 return IsWindow(hwnd);
885 Send notification. depends on dispinfoW having same
886 structure as dispinfoA.
887 infoPtr : listview struct
888 notificationCode : *Unicode* notification code
889 pdi : dispinfo structure (can be unicode or ansi)
890 isW : TRUE if dispinfo is Unicode
892 static BOOL notify_dispinfoT(const LISTVIEW_INFO *infoPtr, UINT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
894 BOOL bResult = FALSE;
895 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
896 INT cchTempBufMax = 0, savCchTextMax = 0;
898 LPWSTR pszTempBuf = NULL, savPszText = NULL;
900 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
902 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
903 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
906 if (convertToAnsi || convertToUnicode)
908 if (notificationCode != LVN_GETDISPINFOW)
910 cchTempBufMax = convertToUnicode ?
911 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
912 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
916 cchTempBufMax = pdi->item.cchTextMax;
917 *pdi->item.pszText = 0; /* make sure we don't process garbage */
920 pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
921 if (!pszTempBuf) return FALSE;
923 if (convertToUnicode)
924 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
925 pszTempBuf, cchTempBufMax);
927 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
928 cchTempBufMax, NULL, NULL);
930 savCchTextMax = pdi->item.cchTextMax;
931 savPszText = pdi->item.pszText;
932 pdi->item.pszText = pszTempBuf;
933 pdi->item.cchTextMax = cchTempBufMax;
936 if (infoPtr->notifyFormat == NFR_ANSI)
937 realNotifCode = get_ansi_notification(notificationCode);
939 realNotifCode = notificationCode;
940 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
941 bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
943 if (convertToUnicode || convertToAnsi)
945 if (convertToUnicode) /* note : pointer can be changed by app ! */
946 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
947 savCchTextMax, NULL, NULL);
949 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
950 savPszText, savCchTextMax);
951 pdi->item.pszText = savPszText; /* restores our buffer */
952 pdi->item.cchTextMax = savCchTextMax;
958 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, const LISTVIEW_INFO *infoPtr, HDC hdc,
959 const RECT *rcBounds, const LVITEMW *lplvItem)
961 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
962 lpnmlvcd->nmcd.hdc = hdc;
963 lpnmlvcd->nmcd.rc = *rcBounds;
964 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
965 lpnmlvcd->clrText = infoPtr->clrText;
966 if (!lplvItem) return;
967 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
968 lpnmlvcd->iSubItem = lplvItem->iSubItem;
969 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
970 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
971 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
972 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
975 static inline DWORD notify_customdraw (const LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
977 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
980 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
981 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
982 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
983 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
984 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
985 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
989 static void prepaint_setup (const LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd, BOOL SubItem)
991 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
992 lpnmlvcd->clrTextBk = comctl32_color.clrWindow;
993 if (lpnmlvcd->clrText == CLR_DEFAULT)
994 lpnmlvcd->clrText = comctl32_color.clrWindowText;
996 /* apparently, for selected items, we have to override the returned values */
999 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
1001 if (infoPtr->bFocus)
1003 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
1004 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
1006 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
1008 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
1009 lpnmlvcd->clrText = comctl32_color.clrBtnText;
1014 /* Set the text attributes */
1015 if (lpnmlvcd->clrTextBk != CLR_NONE)
1017 SetBkMode(hdc, OPAQUE);
1018 SetBkColor(hdc,lpnmlvcd->clrTextBk);
1021 SetBkMode(hdc, TRANSPARENT);
1022 SetTextColor(hdc, lpnmlvcd->clrText);
1025 static inline DWORD notify_postpaint (const LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
1027 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
1030 /* returns TRUE when repaint needed, FALSE otherwise */
1031 static BOOL notify_measureitem(LISTVIEW_INFO *infoPtr)
1033 MEASUREITEMSTRUCT mis;
1034 mis.CtlType = ODT_LISTVIEW;
1035 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1039 mis.itemHeight= infoPtr->nItemHeight;
1040 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
1041 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
1043 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
1049 /******** Item iterator functions **********************************/
1051 static RANGES ranges_create(int count);
1052 static void ranges_destroy(RANGES ranges);
1053 static BOOL ranges_add(RANGES ranges, RANGE range);
1054 static BOOL ranges_del(RANGES ranges, RANGE range);
1055 static void ranges_dump(RANGES ranges);
1057 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
1059 RANGE range = { nItem, nItem + 1 };
1061 return ranges_add(ranges, range);
1064 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
1066 RANGE range = { nItem, nItem + 1 };
1068 return ranges_del(ranges, range);
1072 * ITERATOR DOCUMENTATION
1074 * The iterator functions allow for easy, and convenient iteration
1075 * over items of interest in the list. Typically, you create a
1076 * iterator, use it, and destroy it, as such:
1079 * iterator_xxxitems(&i, ...);
1080 * while (iterator_{prev,next}(&i)
1082 * //code which uses i.nItem
1084 * iterator_destroy(&i);
1086 * where xxx is either: framed, or visible.
1087 * Note that it is important that the code destroys the iterator
1088 * after it's done with it, as the creation of the iterator may
1089 * allocate memory, which thus needs to be freed.
1091 * You can iterate both forwards, and backwards through the list,
1092 * by using iterator_next or iterator_prev respectively.
1094 * Lower numbered items are draw on top of higher number items in
1095 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1096 * items may overlap). So, to test items, you should use
1098 * which lists the items top to bottom (in Z-order).
1099 * For drawing items, you should use
1101 * which lists the items bottom to top (in Z-order).
1102 * If you keep iterating over the items after the end-of-items
1103 * marker (-1) is returned, the iterator will start from the
1104 * beginning. Typically, you don't need to test for -1,
1105 * because iterator_{next,prev} will return TRUE if more items
1106 * are to be iterated over, or FALSE otherwise.
1108 * Note: the iterator is defined to be bidirectional. That is,
1109 * any number of prev followed by any number of next, or
1110 * five versa, should leave the iterator at the same item:
1111 * prev * n, next * n = next * n, prev * n
1113 * The iterator has a notion of an out-of-order, special item,
1114 * which sits at the start of the list. This is used in
1115 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1116 * which needs to be first, as it may overlap other items.
1118 * The code is a bit messy because we have:
1119 * - a special item to deal with
1120 * - simple range, or composite range
1122 * If you find bugs, or want to add features, please make sure you
1123 * always check/modify *both* iterator_prev, and iterator_next.
1127 * This function iterates through the items in increasing order,
1128 * but prefixed by the special item, then -1. That is:
1129 * special, 1, 2, 3, ..., n, -1.
1130 * Each item is listed only once.
1132 static inline BOOL iterator_next(ITERATOR* i)
1136 i->nItem = i->nSpecial;
1137 if (i->nItem != -1) return TRUE;
1139 if (i->nItem == i->nSpecial)
1141 if (i->ranges) i->index = 0;
1147 if (i->nItem == i->nSpecial) i->nItem++;
1148 if (i->nItem < i->range.upper) return TRUE;
1153 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1154 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1157 else if (i->nItem >= i->range.upper) goto end;
1159 i->nItem = i->range.lower;
1160 if (i->nItem >= 0) goto testitem;
1167 * This function iterates through the items in decreasing order,
1168 * followed by the special item, then -1. That is:
1169 * n, n-1, ..., 3, 2, 1, special, -1.
1170 * Each item is listed only once.
1172 static inline BOOL iterator_prev(ITERATOR* i)
1179 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1182 if (i->nItem == i->nSpecial)
1190 if (i->nItem == i->nSpecial) i->nItem--;
1191 if (i->nItem >= i->range.lower) return TRUE;
1197 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1200 else if (!start && i->nItem < i->range.lower) goto end;
1202 i->nItem = i->range.upper;
1203 if (i->nItem > 0) goto testitem;
1205 return (i->nItem = i->nSpecial) != -1;
1208 static RANGE iterator_range(const ITERATOR *i)
1212 if (!i->ranges) return i->range;
1214 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1216 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1217 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1219 else range.lower = range.upper = 0;
1225 * Releases resources associated with this ierator.
1227 static inline void iterator_destroy(const ITERATOR *i)
1229 ranges_destroy(i->ranges);
1233 * Create an empty iterator.
1235 static inline BOOL iterator_empty(ITERATOR* i)
1237 ZeroMemory(i, sizeof(*i));
1238 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1243 * Create an iterator over a range.
1245 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1253 * Create an iterator over a bunch of ranges.
1254 * Please note that the iterator will take ownership of the ranges,
1255 * and will free them upon destruction.
1257 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1265 * Creates an iterator over the items which intersect lprc.
1267 static BOOL iterator_frameditems(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *lprc)
1269 RECT frame = *lprc, rcItem, rcTemp;
1272 /* in case we fail, we want to return an empty iterator */
1273 if (!iterator_empty(i)) return FALSE;
1275 LISTVIEW_GetOrigin(infoPtr, &Origin);
1277 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc));
1278 OffsetRect(&frame, -Origin.x, -Origin.y);
1280 if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)
1284 if (infoPtr->uView == LV_VIEW_ICON && infoPtr->nFocusedItem != -1)
1286 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1287 if (IntersectRect(&rcTemp, &rcItem, lprc))
1288 i->nSpecial = infoPtr->nFocusedItem;
1290 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1291 /* to do better here, we need to have PosX, and PosY sorted */
1292 TRACE("building icon ranges:\n");
1293 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1295 rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1296 rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1297 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1298 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1299 if (IntersectRect(&rcTemp, &rcItem, &frame))
1300 ranges_additem(i->ranges, nItem);
1304 else if (infoPtr->uView == LV_VIEW_DETAILS)
1308 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1309 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1311 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1312 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1313 if (range.upper <= range.lower) return TRUE;
1314 if (!iterator_rangeitems(i, range)) return FALSE;
1315 TRACE(" report=%s\n", debugrange(&i->range));
1319 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1320 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1321 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1328 if (infoPtr->nItemWidth)
1330 nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1331 nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1335 nFirstCol = max(frame.left, 0);
1336 nLastCol = min(frame.right - 1, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1339 lower = nFirstCol * nPerCol + nFirstRow;
1341 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1342 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1344 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1346 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1347 TRACE("building list ranges:\n");
1348 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1350 item_range.lower = nCol * nPerCol + nFirstRow;
1351 if(item_range.lower >= infoPtr->nItemCount) break;
1352 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1353 TRACE(" list=%s\n", debugrange(&item_range));
1354 ranges_add(i->ranges, item_range);
1362 * Creates an iterator over the items which intersect the visible region of hdc.
1364 static BOOL iterator_visibleitems(ITERATOR *i, const LISTVIEW_INFO *infoPtr, HDC hdc)
1366 POINT Origin, Position;
1367 RECT rcItem, rcClip;
1370 rgntype = GetClipBox(hdc, &rcClip);
1371 if (rgntype == NULLREGION) return iterator_empty(i);
1372 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1373 if (rgntype == SIMPLEREGION) return TRUE;
1375 /* first deal with the special item */
1376 if (i->nSpecial != -1)
1378 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1379 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1382 /* if we can't deal with the region, we'll just go with the simple range */
1383 LISTVIEW_GetOrigin(infoPtr, &Origin);
1384 TRACE("building visible range:\n");
1385 if (!i->ranges && i->range.lower < i->range.upper)
1387 if (!(i->ranges = ranges_create(50))) return TRUE;
1388 if (!ranges_add(i->ranges, i->range))
1390 ranges_destroy(i->ranges);
1396 /* now delete the invisible items from the list */
1397 while(iterator_next(i))
1399 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1400 rcItem.left = Position.x + Origin.x;
1401 rcItem.top = Position.y + Origin.y;
1402 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1403 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1404 if (!RectVisible(hdc, &rcItem))
1405 ranges_delitem(i->ranges, i->nItem);
1407 /* the iterator should restart on the next iterator_next */
1413 /******** Misc helper functions ************************************/
1415 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1416 WPARAM wParam, LPARAM lParam, BOOL isW)
1418 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1419 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1422 static inline BOOL is_autoarrange(const LISTVIEW_INFO *infoPtr)
1424 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1425 (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON);
1428 static void toggle_checkbox_state(LISTVIEW_INFO *infoPtr, INT nItem)
1430 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
1431 if(state == 1 || state == 2)
1435 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
1436 lvitem.stateMask = LVIS_STATEIMAGEMASK;
1437 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
1441 /* this should be called after window style got updated,
1442 it used to reset view state to match current window style */
1443 static inline void map_style_view(LISTVIEW_INFO *infoPtr)
1445 switch (infoPtr->dwStyle & LVS_TYPEMASK)
1448 infoPtr->uView = LV_VIEW_ICON;
1451 infoPtr->uView = LV_VIEW_DETAILS;
1454 infoPtr->uView = LV_VIEW_SMALLICON;
1457 infoPtr->uView = LV_VIEW_LIST;
1461 /* computes next item id value */
1462 static DWORD get_next_itemid(const LISTVIEW_INFO *infoPtr)
1464 INT count = DPA_GetPtrCount(infoPtr->hdpaItemIds);
1468 ITEM_ID *lpID = DPA_GetPtr(infoPtr->hdpaItemIds, count - 1);
1469 return lpID->id + 1;
1474 /******** Internal API functions ************************************/
1476 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(const LISTVIEW_INFO *infoPtr, INT nSubItem)
1478 static COLUMN_INFO mainItem;
1480 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1481 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1482 return DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1485 static INT LISTVIEW_CreateHeader(LISTVIEW_INFO *infoPtr)
1487 DWORD dFlags = WS_CHILD | HDS_HORZ | HDS_FULLDRAG | HDS_DRAGDROP;
1490 if (infoPtr->hwndHeader) return 0;
1492 TRACE("Creating header for list %p\n", infoPtr->hwndSelf);
1494 /* setup creation flags */
1495 dFlags |= (LVS_NOSORTHEADER & infoPtr->dwStyle) ? 0 : HDS_BUTTONS;
1496 dFlags |= (LVS_NOCOLUMNHEADER & infoPtr->dwStyle) ? HDS_HIDDEN : 0;
1498 hInst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
1501 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL, dFlags,
1502 0, 0, 0, 0, infoPtr->hwndSelf, NULL, hInst, NULL);
1503 if (!infoPtr->hwndHeader) return -1;
1505 /* set header unicode format */
1506 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, TRUE, 0);
1508 /* set header font */
1509 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
1511 LISTVIEW_UpdateSize(infoPtr);
1516 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO *infoPtr, INT nSubItem, LPRECT lprc)
1518 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1521 static inline BOOL LISTVIEW_GetItemW(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1523 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1526 /* used to handle collapse main item column case */
1527 static inline BOOL LISTVIEW_DrawFocusRect(const LISTVIEW_INFO *infoPtr, HDC hdc)
1529 return (infoPtr->rcFocus.left < infoPtr->rcFocus.right) ?
1530 DrawFocusRect(hdc, &infoPtr->rcFocus) : FALSE;
1533 /* Listview invalidation functions: use _only_ these functions to invalidate */
1535 static inline BOOL is_redrawing(const LISTVIEW_INFO *infoPtr)
1537 return infoPtr->bRedraw;
1540 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO *infoPtr, const RECT* rect)
1542 if(!is_redrawing(infoPtr)) return;
1543 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect));
1544 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1547 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO *infoPtr, INT nItem)
1551 if(!is_redrawing(infoPtr)) return;
1552 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1553 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1556 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1558 POINT Origin, Position;
1561 if(!is_redrawing(infoPtr)) return;
1562 assert (infoPtr->uView == LV_VIEW_DETAILS);
1563 LISTVIEW_GetOrigin(infoPtr, &Origin);
1564 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1565 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1567 rcBox.bottom = infoPtr->nItemHeight;
1568 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1569 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1572 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO *infoPtr)
1574 LISTVIEW_InvalidateRect(infoPtr, NULL);
1577 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO *infoPtr, INT nColumn)
1581 if(!is_redrawing(infoPtr)) return;
1582 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1583 rcCol.top = infoPtr->rcList.top;
1584 rcCol.bottom = infoPtr->rcList.bottom;
1585 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1590 * Retrieves the number of items that can fit vertically in the client area.
1593 * [I] infoPtr : valid pointer to the listview structure
1596 * Number of items per row.
1598 static inline INT LISTVIEW_GetCountPerRow(const LISTVIEW_INFO *infoPtr)
1600 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1602 return max(nListWidth/(infoPtr->nItemWidth ? infoPtr->nItemWidth : 1), 1);
1607 * Retrieves the number of items that can fit horizontally in the client
1611 * [I] infoPtr : valid pointer to the listview structure
1614 * Number of items per column.
1616 static inline INT LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO *infoPtr)
1618 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1620 return max(nListHeight / infoPtr->nItemHeight, 1);
1624 /*************************************************************************
1625 * LISTVIEW_ProcessLetterKeys
1627 * Processes keyboard messages generated by pressing the letter keys
1629 * What this does is perform a case insensitive search from the
1630 * current position with the following quirks:
1631 * - If two chars or more are pressed in quick succession we search
1632 * for the corresponding string (e.g. 'abc').
1633 * - If there is a delay we wipe away the current search string and
1634 * restart with just that char.
1635 * - If the user keeps pressing the same character, whether slowly or
1636 * fast, so that the search string is entirely composed of this
1637 * character ('aaaaa' for instance), then we search for first item
1638 * that starting with that character.
1639 * - If the user types the above character in quick succession, then
1640 * we must also search for the corresponding string ('aaaaa'), and
1641 * go to that string if there is a match.
1644 * [I] hwnd : handle to the window
1645 * [I] charCode : the character code, the actual character
1646 * [I] keyData : key data
1654 * - The current implementation has a list of characters it will
1655 * accept and it ignores everything else. In particular it will
1656 * ignore accentuated characters which seems to match what
1657 * Windows does. But I'm not sure it makes sense to follow
1659 * - We don't sound a beep when the search fails.
1663 * TREEVIEW_ProcessLetterKeys
1665 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1670 WCHAR buffer[MAX_PATH];
1671 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1673 /* simple parameter checking */
1674 if (!charCode || !keyData) return 0;
1676 /* only allow the valid WM_CHARs through */
1677 if (!isalnumW(charCode) &&
1678 charCode != '.' && charCode != '`' && charCode != '!' &&
1679 charCode != '@' && charCode != '#' && charCode != '$' &&
1680 charCode != '%' && charCode != '^' && charCode != '&' &&
1681 charCode != '*' && charCode != '(' && charCode != ')' &&
1682 charCode != '-' && charCode != '_' && charCode != '+' &&
1683 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1684 charCode != '}' && charCode != '[' && charCode != '{' &&
1685 charCode != '/' && charCode != '?' && charCode != '>' &&
1686 charCode != '<' && charCode != ',' && charCode != '~')
1689 /* if there's one item or less, there is no where to go */
1690 if (infoPtr->nItemCount <= 1) return 0;
1692 /* update the search parameters */
1693 infoPtr->lastKeyPressTimestamp = GetTickCount();
1694 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1695 if (infoPtr->nSearchParamLength < MAX_PATH-1)
1696 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1697 if (infoPtr->charCode != charCode)
1698 infoPtr->charCode = charCode = 0;
1700 infoPtr->charCode=charCode;
1701 infoPtr->szSearchParam[0]=charCode;
1702 infoPtr->nSearchParamLength=1;
1703 /* Redundant with the 1 char string */
1707 /* and search from the current position */
1709 if (infoPtr->nFocusedItem >= 0) {
1710 endidx=infoPtr->nFocusedItem;
1712 /* if looking for single character match,
1713 * then we must always move forward
1715 if (infoPtr->nSearchParamLength == 1)
1718 endidx=infoPtr->nItemCount;
1722 /* Let application handle this for virtual listview */
1723 if (infoPtr->dwStyle & LVS_OWNERDATA)
1728 ZeroMemory(&lvfi, sizeof(lvfi));
1729 lvfi.flags = (LVFI_WRAP | LVFI_PARTIAL);
1730 infoPtr->szSearchParam[infoPtr->nSearchParamLength] = '\0';
1731 lvfi.psz = infoPtr->szSearchParam;
1735 nItem = notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
1738 LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1744 if (idx == infoPtr->nItemCount) {
1745 if (endidx == infoPtr->nItemCount || endidx == 0)
1751 item.mask = LVIF_TEXT;
1754 item.pszText = buffer;
1755 item.cchTextMax = MAX_PATH;
1756 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1758 /* check for a match */
1759 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1762 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1763 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1764 /* This would work but we must keep looking for a longer match */
1768 } while (idx != endidx);
1771 LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1776 /*************************************************************************
1777 * LISTVIEW_UpdateHeaderSize [Internal]
1779 * Function to resize the header control
1782 * [I] hwnd : handle to a window
1783 * [I] nNewScrollPos : scroll pos to set
1788 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1793 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1795 if (!infoPtr->hwndHeader) return;
1797 GetWindowRect(infoPtr->hwndHeader, &winRect);
1798 point[0].x = winRect.left;
1799 point[0].y = winRect.top;
1800 point[1].x = winRect.right;
1801 point[1].y = winRect.bottom;
1803 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1804 point[0].x = -nNewScrollPos;
1805 point[1].x += nNewScrollPos;
1807 SetWindowPos(infoPtr->hwndHeader,0,
1808 point[0].x,point[0].y,point[1].x,point[1].y,
1809 (infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW |
1810 SWP_NOZORDER | SWP_NOACTIVATE);
1815 * Update the scrollbars. This functions should be called whenever
1816 * the content, size or view changes.
1819 * [I] infoPtr : valid pointer to the listview structure
1824 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *infoPtr)
1826 SCROLLINFO horzInfo, vertInfo;
1829 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1831 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1832 horzInfo.cbSize = sizeof(SCROLLINFO);
1833 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1835 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1836 if (infoPtr->uView == LV_VIEW_LIST)
1838 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1839 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1841 /* scroll by at least one column per page */
1842 if(horzInfo.nPage < infoPtr->nItemWidth)
1843 horzInfo.nPage = infoPtr->nItemWidth;
1845 if (infoPtr->nItemWidth)
1846 horzInfo.nPage /= infoPtr->nItemWidth;
1848 else if (infoPtr->uView == LV_VIEW_DETAILS)
1850 horzInfo.nMax = infoPtr->nItemWidth;
1852 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
1856 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1859 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1860 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1861 dx = GetScrollPos(infoPtr->hwndSelf, SB_HORZ);
1862 dx -= SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1863 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1865 /* Setting the horizontal scroll can change the listview size
1866 * (and potentially everything else) so we need to recompute
1867 * everything again for the vertical scroll
1870 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1871 vertInfo.cbSize = sizeof(SCROLLINFO);
1872 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1874 if (infoPtr->uView == LV_VIEW_DETAILS)
1876 vertInfo.nMax = infoPtr->nItemCount;
1878 /* scroll by at least one page */
1879 if(vertInfo.nPage < infoPtr->nItemHeight)
1880 vertInfo.nPage = infoPtr->nItemHeight;
1882 if (infoPtr->nItemHeight > 0)
1883 vertInfo.nPage /= infoPtr->nItemHeight;
1885 else if (infoPtr->uView != LV_VIEW_LIST) /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
1889 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1892 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1893 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1894 dy = GetScrollPos(infoPtr->hwndSelf, SB_VERT);
1895 dy -= SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1896 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1898 /* Change of the range may have changed the scroll pos. If so move the content */
1899 if (dx != 0 || dy != 0)
1902 listRect = infoPtr->rcList;
1903 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &listRect, &listRect, 0, 0,
1904 SW_ERASE | SW_INVALIDATE);
1907 /* Update the Header Control */
1908 if (infoPtr->uView == LV_VIEW_DETAILS)
1910 horzInfo.fMask = SIF_POS;
1911 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1912 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1919 * Shows/hides the focus rectangle.
1922 * [I] infoPtr : valid pointer to the listview structure
1923 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1928 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO *infoPtr, BOOL fShow)
1932 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1934 if (infoPtr->nFocusedItem < 0) return;
1936 /* we need some gymnastics in ICON mode to handle large items */
1937 if (infoPtr->uView == LV_VIEW_ICON)
1941 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1942 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1944 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1949 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1951 /* for some reason, owner draw should work only in report mode */
1952 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
1957 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1958 HFONT hOldFont = SelectObject(hdc, hFont);
1960 item.iItem = infoPtr->nFocusedItem;
1962 item.mask = LVIF_PARAM;
1963 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1965 ZeroMemory(&dis, sizeof(dis));
1966 dis.CtlType = ODT_LISTVIEW;
1967 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1968 dis.itemID = item.iItem;
1969 dis.itemAction = ODA_FOCUS;
1970 if (fShow) dis.itemState |= ODS_FOCUS;
1971 dis.hwndItem = infoPtr->hwndSelf;
1973 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1974 dis.itemData = item.lParam;
1976 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1978 SelectObject(hdc, hOldFont);
1982 LISTVIEW_DrawFocusRect(infoPtr, hdc);
1985 ReleaseDC(infoPtr->hwndSelf, hdc);
1989 * Invalidates all visible selected items.
1991 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO *infoPtr)
1995 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1996 while(iterator_next(&i))
1998 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1999 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
2001 iterator_destroy(&i);
2006 * DESCRIPTION: [INTERNAL]
2007 * Computes an item's (left,top) corner, relative to rcView.
2008 * That is, the position has NOT been made relative to the Origin.
2009 * This is deliberate, to avoid computing the Origin over, and
2010 * over again, when this function is called in a loop. Instead,
2011 * one can factor the computation of the Origin before the loop,
2012 * and offset the value returned by this function, on every iteration.
2015 * [I] infoPtr : valid pointer to the listview structure
2016 * [I] nItem : item number
2017 * [O] lpptOrig : item top, left corner
2022 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
2024 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
2026 if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
2028 lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2029 lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2031 else if (infoPtr->uView == LV_VIEW_LIST)
2033 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
2034 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
2035 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
2037 else /* LV_VIEW_DETAILS */
2039 lpptPosition->x = REPORT_MARGINX;
2040 /* item is always at zero indexed column */
2041 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2042 lpptPosition->x += LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left;
2043 lpptPosition->y = nItem * infoPtr->nItemHeight;
2048 * DESCRIPTION: [INTERNAL]
2049 * Compute the rectangles of an item. This is to localize all
2050 * the computations in one place. If you are not interested in some
2051 * of these values, simply pass in a NULL -- the function is smart
2052 * enough to compute only what's necessary. The function computes
2053 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
2054 * one, the BOX rectangle. This rectangle is very cheap to compute,
2055 * and is guaranteed to contain all the other rectangles. Computing
2056 * the ICON rect is also cheap, but all the others are potentially
2057 * expensive. This gives an easy and effective optimization when
2058 * searching (like point inclusion, or rectangle intersection):
2059 * first test against the BOX, and if TRUE, test against the desired
2061 * If the function does not have all the necessary information
2062 * to computed the requested rectangles, will crash with a
2063 * failed assertion. This is done so we catch all programming
2064 * errors, given that the function is called only from our code.
2066 * We have the following 'special' meanings for a few fields:
2067 * * If LVIS_FOCUSED is set, we assume the item has the focus
2068 * This is important in ICON mode, where it might get a larger
2069 * then usual rectangle
2071 * Please note that subitem support works only in REPORT mode.
2074 * [I] infoPtr : valid pointer to the listview structure
2075 * [I] lpLVItem : item to compute the measures for
2076 * [O] lprcBox : ptr to Box rectangle
2077 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
2078 * [0] lprcSelectBox : ptr to select box rectangle
2079 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
2080 * [O] lprcIcon : ptr to Icon rectangle
2081 * Same as LVM_GETITEMRECT with LVIR_ICON
2082 * [O] lprcStateIcon: ptr to State Icon rectangle
2083 * [O] lprcLabel : ptr to Label rectangle
2084 * Same as LVM_GETITEMRECT with LVIR_LABEL
2089 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
2090 LPRECT lprcBox, LPRECT lprcSelectBox,
2091 LPRECT lprcIcon, LPRECT lprcStateIcon, LPRECT lprcLabel)
2093 BOOL doSelectBox = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
2094 RECT Box, SelectBox, Icon, Label;
2095 COLUMN_INFO *lpColumnInfo = NULL;
2096 SIZE labelSize = { 0, 0 };
2098 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
2100 /* Be smart and try to figure out the minimum we have to do */
2101 if (lpLVItem->iSubItem) assert(infoPtr->uView == LV_VIEW_DETAILS);
2102 if (infoPtr->uView == LV_VIEW_ICON && (lprcBox || lprcLabel))
2104 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
2105 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
2107 if (lprcSelectBox) doSelectBox = TRUE;
2108 if (lprcLabel) doLabel = TRUE;
2109 if (doLabel || lprcIcon || lprcStateIcon) doIcon = TRUE;
2116 /************************************************************/
2117 /* compute the box rectangle (it should be cheap to do) */
2118 /************************************************************/
2119 if (lpLVItem->iSubItem || infoPtr->uView == LV_VIEW_DETAILS)
2120 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
2122 if (lpLVItem->iSubItem)
2124 Box = lpColumnInfo->rcHeader;
2129 Box.right = infoPtr->nItemWidth;
2132 Box.bottom = infoPtr->nItemHeight;
2134 /******************************************************************/
2135 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
2136 /******************************************************************/
2139 LONG state_width = 0;
2141 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
2142 state_width = infoPtr->iconStateSize.cx;
2144 if (infoPtr->uView == LV_VIEW_ICON)
2146 Icon.left = Box.left + state_width;
2147 if (infoPtr->himlNormal)
2148 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx - state_width) / 2;
2149 Icon.top = Box.top + ICON_TOP_PADDING;
2150 Icon.right = Icon.left;
2151 Icon.bottom = Icon.top;
2152 if (infoPtr->himlNormal)
2154 Icon.right += infoPtr->iconSize.cx;
2155 Icon.bottom += infoPtr->iconSize.cy;
2158 else /* LV_VIEW_SMALLICON, LV_VIEW_LIST or LV_VIEW_DETAILS */
2160 Icon.left = Box.left + state_width;
2162 if (infoPtr->uView == LV_VIEW_DETAILS && lpLVItem->iSubItem == 0)
2164 /* we need the indent in report mode */
2165 assert(lpLVItem->mask & LVIF_INDENT);
2166 Icon.left += infoPtr->iconSize.cx * lpLVItem->iIndent + REPORT_MARGINX;
2170 Icon.right = Icon.left;
2171 if (infoPtr->himlSmall &&
2172 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
2173 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
2174 Icon.right += infoPtr->iconSize.cx;
2175 Icon.bottom = Icon.top + infoPtr->iconSize.cy;
2177 if(lprcIcon) *lprcIcon = Icon;
2178 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon));
2180 /* TODO: is this correct? */
2183 lprcStateIcon->left = Icon.left - state_width;
2184 lprcStateIcon->right = Icon.left;
2185 lprcStateIcon->top = Icon.top;
2186 lprcStateIcon->bottom = lprcStateIcon->top + infoPtr->iconSize.cy;
2187 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon));
2190 else Icon.right = 0;
2192 /************************************************************/
2193 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2194 /************************************************************/
2197 /* calculate how far to the right can the label stretch */
2198 Label.right = Box.right;
2199 if (infoPtr->uView == LV_VIEW_DETAILS)
2201 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
2204 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && infoPtr->uView == LV_VIEW_DETAILS))
2206 labelSize.cx = infoPtr->nItemWidth;
2207 labelSize.cy = infoPtr->nItemHeight;
2211 /* we need the text in non owner draw mode */
2212 assert(lpLVItem->mask & LVIF_TEXT);
2213 if (is_textT(lpLVItem->pszText, TRUE))
2215 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2216 HDC hdc = GetDC(infoPtr->hwndSelf);
2217 HFONT hOldFont = SelectObject(hdc, hFont);
2221 /* compute rough rectangle where the label will go */
2222 SetRectEmpty(&rcText);
2223 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2224 rcText.bottom = infoPtr->nItemHeight;
2225 if (infoPtr->uView == LV_VIEW_ICON)
2226 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2228 /* now figure out the flags */
2229 if (infoPtr->uView == LV_VIEW_ICON)
2230 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2232 uFormat = LV_SL_DT_FLAGS;
2234 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2236 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2237 labelSize.cy = rcText.bottom - rcText.top;
2239 SelectObject(hdc, hOldFont);
2240 ReleaseDC(infoPtr->hwndSelf, hdc);
2244 if (infoPtr->uView == LV_VIEW_ICON)
2246 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2247 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2248 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2249 Label.right = Label.left + labelSize.cx;
2250 Label.bottom = Label.top + infoPtr->nItemHeight;
2251 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2253 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2254 labelSize.cy /= infoPtr->ntmHeight;
2255 labelSize.cy = max(labelSize.cy, 1);
2256 labelSize.cy *= infoPtr->ntmHeight;
2258 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2260 else if (infoPtr->uView == LV_VIEW_DETAILS)
2262 Label.left = Icon.right;
2263 Label.top = Box.top;
2264 Label.right = lpColumnInfo->rcHeader.right;
2265 Label.bottom = Label.top + infoPtr->nItemHeight;
2267 else /* LV_VIEW_SMALLICON or LV_VIEW_LIST */
2269 Label.left = Icon.right;
2270 Label.top = Box.top;
2271 Label.right = min(Label.left + labelSize.cx, Label.right);
2272 Label.bottom = Label.top + infoPtr->nItemHeight;
2275 if (lprcLabel) *lprcLabel = Label;
2276 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label));
2279 /************************************************************/
2280 /* compute SELECT bounding box */
2281 /************************************************************/
2284 if (infoPtr->uView == LV_VIEW_DETAILS)
2286 SelectBox.left = Icon.left;
2287 SelectBox.top = Box.top;
2288 SelectBox.bottom = Box.bottom;
2289 SelectBox.right = min(Label.left + labelSize.cx, Label.right);
2293 UnionRect(&SelectBox, &Icon, &Label);
2295 if (lprcSelectBox) *lprcSelectBox = SelectBox;
2296 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox));
2299 /* Fix the Box if necessary */
2302 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2303 else *lprcBox = Box;
2305 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box));
2309 * DESCRIPTION: [INTERNAL]
2312 * [I] infoPtr : valid pointer to the listview structure
2313 * [I] nItem : item number
2314 * [O] lprcBox : ptr to Box rectangle
2319 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2321 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2322 POINT Position, Origin;
2325 LISTVIEW_GetOrigin(infoPtr, &Origin);
2326 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2328 /* Be smart and try to figure out the minimum we have to do */
2330 if (infoPtr->uView == LV_VIEW_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2331 lvItem.mask |= LVIF_TEXT;
2332 lvItem.iItem = nItem;
2333 lvItem.iSubItem = 0;
2334 lvItem.pszText = szDispText;
2335 lvItem.cchTextMax = DISP_TEXT_SIZE;
2336 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2337 if (infoPtr->uView == LV_VIEW_ICON)
2339 lvItem.mask |= LVIF_STATE;
2340 lvItem.stateMask = LVIS_FOCUSED;
2341 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2343 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0, 0);
2345 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT &&
2346 SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 0, 0))
2348 OffsetRect(lprcBox, Origin.x, Position.y + Origin.y);
2351 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2354 /* LISTVIEW_MapIdToIndex helper */
2355 INT CALLBACK MapIdSearchCompare(LPVOID p1, LPVOID p2, LPARAM lParam)
2357 ITEM_ID *id1 = (ITEM_ID*)p1;
2358 ITEM_ID *id2 = (ITEM_ID*)p2;
2360 if (id1->id == id2->id) return 0;
2362 return (id1->id < id2->id) ? -1 : 1;
2367 * Returns the item index for id specified.
2370 * [I] infoPtr : valid pointer to the listview structure
2371 * [I] iID : item id to get index for
2374 * Item index, or -1 on failure.
2376 static INT LISTVIEW_MapIdToIndex(const LISTVIEW_INFO *infoPtr, UINT iID)
2381 TRACE("iID=%d\n", iID);
2383 if (infoPtr->dwStyle & LVS_OWNERDATA) return -1;
2384 if (infoPtr->nItemCount == 0) return -1;
2387 index = DPA_Search(infoPtr->hdpaItemIds, &ID, -1, &MapIdSearchCompare, 0, DPAS_SORTED);
2391 ITEM_ID *lpID = DPA_GetPtr(infoPtr->hdpaItemIds, index);
2392 return DPA_GetPtrIndex(infoPtr->hdpaItems, lpID->item);
2400 * Returns the item id for index given.
2403 * [I] infoPtr : valid pointer to the listview structure
2404 * [I] iItem : item index to get id for
2409 static DWORD LISTVIEW_MapIndexToId(const LISTVIEW_INFO *infoPtr, INT iItem)
2414 TRACE("iItem=%d\n", iItem);
2416 if (infoPtr->dwStyle & LVS_OWNERDATA) return -1;
2417 if (iItem < 0 || iItem >= infoPtr->nItemCount) return -1;
2419 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, iItem);
2420 lpItem = DPA_GetPtr(hdpaSubItems, 0);
2422 return lpItem->id->id;
2427 * Returns the current icon position, and advances it along the top.
2428 * The returned position is not offset by Origin.
2431 * [I] infoPtr : valid pointer to the listview structure
2432 * [O] lpPos : will get the current icon position
2437 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2439 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2441 *lpPos = infoPtr->currIconPos;
2443 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2444 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2446 infoPtr->currIconPos.x = 0;
2447 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2453 * Returns the current icon position, and advances it down the left edge.
2454 * The returned position is not offset by Origin.
2457 * [I] infoPtr : valid pointer to the listview structure
2458 * [O] lpPos : will get the current icon position
2463 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2465 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2467 *lpPos = infoPtr->currIconPos;
2469 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2470 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2472 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2473 infoPtr->currIconPos.y = 0;
2479 * Moves an icon to the specified position.
2480 * It takes care of invalidating the item, etc.
2483 * [I] infoPtr : valid pointer to the listview structure
2484 * [I] nItem : the item to move
2485 * [I] lpPos : the new icon position
2486 * [I] isNew : flags the item as being new
2492 static BOOL LISTVIEW_MoveIconTo(const LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2498 old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2499 old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2501 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2502 LISTVIEW_InvalidateItem(infoPtr, nItem);
2505 /* Allocating a POINTER for every item is too resource intensive,
2506 * so we'll keep the (x,y) in different arrays */
2507 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2508 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2510 LISTVIEW_InvalidateItem(infoPtr, nItem);
2517 * Arranges listview items in icon display mode.
2520 * [I] infoPtr : valid pointer to the listview structure
2521 * [I] nAlignCode : alignment code
2527 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2529 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2533 if (infoPtr->uView != LV_VIEW_ICON && infoPtr->uView != LV_VIEW_SMALLICON) return FALSE;
2535 TRACE("nAlignCode=%d\n", nAlignCode);
2537 if (nAlignCode == LVA_DEFAULT)
2539 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2540 else nAlignCode = LVA_ALIGNTOP;
2545 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2546 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2547 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2548 default: return FALSE;
2551 infoPtr->bAutoarrange = TRUE;
2552 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2553 for (i = 0; i < infoPtr->nItemCount; i++)
2555 next_pos(infoPtr, &pos);
2556 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2564 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2565 * For LVS_REPORT always returns empty rectangle.
2568 * [I] infoPtr : valid pointer to the listview structure
2569 * [O] lprcView : bounding rectangle
2575 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2579 SetRectEmpty(lprcView);
2581 switch (infoPtr->uView)
2584 case LV_VIEW_SMALLICON:
2585 for (i = 0; i < infoPtr->nItemCount; i++)
2587 x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2588 y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2589 lprcView->right = max(lprcView->right, x);
2590 lprcView->bottom = max(lprcView->bottom, y);
2592 if (infoPtr->nItemCount > 0)
2594 lprcView->right += infoPtr->nItemWidth;
2595 lprcView->bottom += infoPtr->nItemHeight;
2600 y = LISTVIEW_GetCountPerColumn(infoPtr);
2601 x = infoPtr->nItemCount / y;
2602 if (infoPtr->nItemCount % y) x++;
2603 lprcView->right = x * infoPtr->nItemWidth;
2604 lprcView->bottom = y * infoPtr->nItemHeight;
2611 * Retrieves the bounding rectangle of all the items.
2614 * [I] infoPtr : valid pointer to the listview structure
2615 * [O] lprcView : bounding rectangle
2621 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2625 TRACE("(lprcView=%p)\n", lprcView);
2627 if (!lprcView) return FALSE;
2629 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2631 if (infoPtr->uView != LV_VIEW_DETAILS)
2633 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2634 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2637 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView));
2644 * Retrieves the subitem pointer associated with the subitem index.
2647 * [I] hdpaSubItems : DPA handle for a specific item
2648 * [I] nSubItem : index of subitem
2651 * SUCCESS : subitem pointer
2654 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2656 SUBITEM_INFO *lpSubItem;
2659 /* we should binary search here if need be */
2660 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2662 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
2663 if (lpSubItem->iSubItem == nSubItem)
2673 * Calculates the desired item width.
2676 * [I] infoPtr : valid pointer to the listview structure
2679 * The desired item width.
2681 static INT LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO *infoPtr)
2685 TRACE("uView=%d\n", infoPtr->uView);
2687 if (infoPtr->uView == LV_VIEW_ICON)
2688 nItemWidth = infoPtr->iconSpacing.cx;
2689 else if (infoPtr->uView == LV_VIEW_DETAILS)
2691 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2696 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
2697 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
2699 LISTVIEW_GetHeaderRect(infoPtr, index, &rcHeader);
2700 nItemWidth = rcHeader.right;
2703 else /* LV_VIEW_SMALLICON, or LV_VIEW_LIST */
2707 for (i = 0; i < infoPtr->nItemCount; i++)
2708 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2710 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2711 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2713 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2721 * Calculates the desired item height.
2724 * [I] infoPtr : valid pointer to the listview structure
2727 * The desired item height.
2729 static INT LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO *infoPtr)
2733 TRACE("uView=%d\n", infoPtr->uView);
2735 if (infoPtr->uView == LV_VIEW_ICON)
2736 nItemHeight = infoPtr->iconSpacing.cy;
2739 nItemHeight = infoPtr->ntmHeight;
2740 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
2742 if (infoPtr->himlState)
2743 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2744 if (infoPtr->himlSmall)
2745 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2746 if (infoPtr->himlState || infoPtr->himlSmall)
2747 nItemHeight += HEIGHT_PADDING;
2748 if (infoPtr->nMeasureItemHeight > 0)
2749 nItemHeight = infoPtr->nMeasureItemHeight;
2752 return max(nItemHeight, 1);
2757 * Updates the width, and height of an item.
2760 * [I] infoPtr : valid pointer to the listview structure
2765 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2767 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2768 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2774 * Retrieves and saves important text metrics info for the current
2778 * [I] infoPtr : valid pointer to the listview structure
2781 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2783 HDC hdc = GetDC(infoPtr->hwndSelf);
2784 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2785 HFONT hOldFont = SelectObject(hdc, hFont);
2789 if (GetTextMetricsW(hdc, &tm))
2791 infoPtr->ntmHeight = tm.tmHeight;
2792 infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
2795 if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
2796 infoPtr->nEllipsisWidth = sz.cx;
2798 SelectObject(hdc, hOldFont);
2799 ReleaseDC(infoPtr->hwndSelf, hdc);
2801 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2806 * A compare function for ranges
2809 * [I] range1 : pointer to range 1;
2810 * [I] range2 : pointer to range 2;
2814 * > 0 : if range 1 > range 2
2815 * < 0 : if range 2 > range 1
2816 * = 0 : if range intersects range 2
2818 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2822 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2824 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2829 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1), debugrange(range2), cmp);
2835 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2837 #define ranges_check(ranges, desc) do { } while(0)
2840 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2845 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2847 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2848 ranges_dump(ranges);
2849 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2851 prev = DPA_GetPtr(ranges->hdpa, 0);
2852 assert (prev->lower >= 0 && prev->lower < prev->upper);
2853 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2855 curr = DPA_GetPtr(ranges->hdpa, i);
2856 assert (prev->upper <= curr->lower);
2857 assert (curr->lower < curr->upper);
2861 TRACE("--- Done checking---\n");
2864 static RANGES ranges_create(int count)
2866 RANGES ranges = Alloc(sizeof(struct tagRANGES));
2867 if (!ranges) return NULL;
2868 ranges->hdpa = DPA_Create(count);
2869 if (ranges->hdpa) return ranges;
2874 static void ranges_clear(RANGES ranges)
2878 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2879 Free(DPA_GetPtr(ranges->hdpa, i));
2880 DPA_DeleteAllPtrs(ranges->hdpa);
2884 static void ranges_destroy(RANGES ranges)
2886 if (!ranges) return;
2887 ranges_clear(ranges);
2888 DPA_Destroy(ranges->hdpa);
2892 static RANGES ranges_clone(RANGES ranges)
2897 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2899 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2901 RANGE *newrng = Alloc(sizeof(RANGE));
2902 if (!newrng) goto fail;
2903 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2904 DPA_SetPtr(clone->hdpa, i, newrng);
2909 TRACE ("clone failed\n");
2910 ranges_destroy(clone);
2914 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2918 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2919 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2924 static void ranges_dump(RANGES ranges)
2928 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2929 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2932 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2934 RANGE srchrng = { nItem, nItem + 1 };
2936 TRACE("(nItem=%d)\n", nItem);
2937 ranges_check(ranges, "before contain");
2938 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2941 static INT ranges_itemcount(RANGES ranges)
2945 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2947 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2948 count += sel->upper - sel->lower;
2954 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2956 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2959 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2960 if (index == -1) return TRUE;
2962 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2964 chkrng = DPA_GetPtr(ranges->hdpa, index);
2965 if (chkrng->lower >= nItem)
2966 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2967 if (chkrng->upper > nItem)
2968 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2973 static BOOL ranges_add(RANGES ranges, RANGE range)
2978 TRACE("(%s)\n", debugrange(&range));
2979 ranges_check(ranges, "before add");
2981 /* try find overlapping regions first */
2982 srchrgn.lower = range.lower - 1;
2983 srchrgn.upper = range.upper + 1;
2984 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2990 TRACE("Adding new range\n");
2992 /* create the brand new range to insert */
2993 newrgn = Alloc(sizeof(RANGE));
2994 if(!newrgn) goto fail;
2997 /* figure out where to insert it */
2998 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2999 TRACE("index=%d\n", index);
3000 if (index == -1) index = 0;
3002 /* and get it over with */
3003 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
3011 RANGE *chkrgn, *mrgrgn;
3012 INT fromindex, mergeindex;
3014 chkrgn = DPA_GetPtr(ranges->hdpa, index);
3015 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
3017 chkrgn->lower = min(range.lower, chkrgn->lower);
3018 chkrgn->upper = max(range.upper, chkrgn->upper);
3020 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
3022 /* merge now common ranges */
3024 srchrgn.lower = chkrgn->lower - 1;
3025 srchrgn.upper = chkrgn->upper + 1;
3029 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
3030 if (mergeindex == -1) break;
3031 if (mergeindex == index)
3033 fromindex = index + 1;
3037 TRACE("Merge with index %i\n", mergeindex);
3039 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
3040 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
3041 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
3043 DPA_DeletePtr(ranges->hdpa, mergeindex);
3044 if (mergeindex < index) index --;
3048 ranges_check(ranges, "after add");
3052 ranges_check(ranges, "failed add");
3056 static BOOL ranges_del(RANGES ranges, RANGE range)
3061 TRACE("(%s)\n", debugrange(&range));
3062 ranges_check(ranges, "before del");
3064 /* we don't use DPAS_SORTED here, since we need *
3065 * to find the first overlapping range */
3066 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
3069 chkrgn = DPA_GetPtr(ranges->hdpa, index);
3071 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
3073 /* case 1: Same range */
3074 if ( (chkrgn->upper == range.upper) &&
3075 (chkrgn->lower == range.lower) )
3077 DPA_DeletePtr(ranges->hdpa, index);
3080 /* case 2: engulf */
3081 else if ( (chkrgn->upper <= range.upper) &&
3082 (chkrgn->lower >= range.lower) )
3084 DPA_DeletePtr(ranges->hdpa, index);
3086 /* case 3: overlap upper */
3087 else if ( (chkrgn->upper <= range.upper) &&
3088 (chkrgn->lower < range.lower) )
3090 chkrgn->upper = range.lower;
3092 /* case 4: overlap lower */
3093 else if ( (chkrgn->upper > range.upper) &&
3094 (chkrgn->lower >= range.lower) )
3096 chkrgn->lower = range.upper;
3099 /* case 5: fully internal */
3102 RANGE tmprgn = *chkrgn, *newrgn;
3104 if (!(newrgn = Alloc(sizeof(RANGE)))) goto fail;
3105 newrgn->lower = chkrgn->lower;
3106 newrgn->upper = range.lower;
3107 chkrgn->lower = range.upper;
3108 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
3117 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
3120 ranges_check(ranges, "after del");
3124 ranges_check(ranges, "failed del");
3130 * Removes all selection ranges
3133 * [I] infoPtr : valid pointer to the listview structure
3134 * [I] toSkip : item range to skip removing the selection
3140 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
3149 lvItem.stateMask = LVIS_SELECTED;
3151 /* need to clone the DPA because callbacks can change it */
3152 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
3153 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
3154 while(iterator_next(&i))
3155 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
3156 /* note that the iterator destructor will free the cloned range */
3157 iterator_destroy(&i);
3162 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
3166 if (!(toSkip = ranges_create(1))) return FALSE;
3167 if (nItem != -1) ranges_additem(toSkip, nItem);
3168 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
3169 ranges_destroy(toSkip);
3173 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
3175 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
3180 * Retrieves the number of items that are marked as selected.
3183 * [I] infoPtr : valid pointer to the listview structure
3186 * Number of items selected.
3188 static INT LISTVIEW_GetSelectedCount(const LISTVIEW_INFO *infoPtr)
3190 INT nSelectedCount = 0;
3192 if (infoPtr->uCallbackMask & LVIS_SELECTED)
3195 for (i = 0; i < infoPtr->nItemCount; i++)
3197 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
3202 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
3204 TRACE("nSelectedCount=%d\n", nSelectedCount);
3205 return nSelectedCount;
3210 * Manages the item focus.
3213 * [I] infoPtr : valid pointer to the listview structure
3214 * [I] nItem : item index
3217 * TRUE : focused item changed
3218 * FALSE : focused item has NOT changed
3220 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
3222 INT oldFocus = infoPtr->nFocusedItem;
3225 if (nItem == infoPtr->nFocusedItem) return FALSE;
3227 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
3228 lvItem.stateMask = LVIS_FOCUSED;
3229 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
3231 return oldFocus != infoPtr->nFocusedItem;
3234 /* Helper function for LISTVIEW_ShiftIndices *only* */
3235 static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
3237 if (nShiftItem < nItem) return nShiftItem;
3239 if (nShiftItem > nItem) return nShiftItem + direction;
3241 if (direction > 0) return nShiftItem + direction;
3243 return min(nShiftItem, infoPtr->nItemCount - 1);
3248 * Updates the various indices after an item has been inserted or deleted.
3251 * [I] infoPtr : valid pointer to the listview structure
3252 * [I] nItem : item index
3253 * [I] direction : Direction of shift, +1 or -1.
3258 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
3263 /* temporarily disable change notification while shifting items */
3264 bOldChange = infoPtr->bDoChangeNotify;
3265 infoPtr->bDoChangeNotify = FALSE;
3267 TRACE("Shifting %iu, %i steps\n", nItem, direction);
3269 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
3271 assert(abs(direction) == 1);
3273 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
3275 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
3276 if (nNewFocus != infoPtr->nFocusedItem)
3277 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
3279 /* But we are not supposed to modify nHotItem! */
3281 infoPtr->bDoChangeNotify = bOldChange;
3287 * Adds a block of selections.
3290 * [I] infoPtr : valid pointer to the listview structure
3291 * [I] nItem : item index
3294 * Whether the window is still valid.
3296 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3298 INT nFirst = min(infoPtr->nSelectionMark, nItem);
3299 INT nLast = max(infoPtr->nSelectionMark, nItem);
3300 HWND hwndSelf = infoPtr->hwndSelf;
3301 NMLVODSTATECHANGE nmlv;
3306 /* Temporarily disable change notification
3307 * If the control is LVS_OWNERDATA, we need to send
3308 * only one LVN_ODSTATECHANGED notification.
3309 * See MSDN documentation for LVN_ITEMCHANGED.
3311 bOldChange = infoPtr->bDoChangeNotify;
3312 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3314 if (nFirst == -1) nFirst = nItem;
3316 item.state = LVIS_SELECTED;
3317 item.stateMask = LVIS_SELECTED;
3319 for (i = nFirst; i <= nLast; i++)
3320 LISTVIEW_SetItemState(infoPtr,i,&item);
3322 ZeroMemory(&nmlv, sizeof(nmlv));
3323 nmlv.iFrom = nFirst;
3326 nmlv.uOldState = item.state;
3328 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3329 if (!IsWindow(hwndSelf))
3331 infoPtr->bDoChangeNotify = bOldChange;
3338 * Sets a single group selection.
3341 * [I] infoPtr : valid pointer to the listview structure
3342 * [I] nItem : item index
3347 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3354 if (!(selection = ranges_create(100))) return;
3356 item.state = LVIS_SELECTED;
3357 item.stateMask = LVIS_SELECTED;
3359 if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
3361 if (infoPtr->nSelectionMark == -1)
3363 infoPtr->nSelectionMark = nItem;
3364 ranges_additem(selection, nItem);
3370 sel.lower = min(infoPtr->nSelectionMark, nItem);
3371 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3372 ranges_add(selection, sel);
3377 RECT rcItem, rcSel, rcSelMark;
3380 rcItem.left = LVIR_BOUNDS;
3381 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3382 rcSelMark.left = LVIR_BOUNDS;
3383 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3384 UnionRect(&rcSel, &rcItem, &rcSelMark);
3385 iterator_frameditems(&i, infoPtr, &rcSel);
3386 while(iterator_next(&i))
3388 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3389 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3391 iterator_destroy(&i);
3394 /* disable per item notifications on LVS_OWNERDATA style
3395 FIXME: single LVN_ODSTATECHANGED should be used */
3396 bOldChange = infoPtr->bDoChangeNotify;
3397 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3399 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3402 iterator_rangesitems(&i, selection);
3403 while(iterator_next(&i))
3404 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3405 /* this will also destroy the selection */
3406 iterator_destroy(&i);
3408 infoPtr->bDoChangeNotify = bOldChange;
3410 LISTVIEW_SetItemFocus(infoPtr, nItem);
3415 * Sets a single selection.
3418 * [I] infoPtr : valid pointer to the listview structure
3419 * [I] nItem : item index
3424 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3428 TRACE("nItem=%d\n", nItem);
3430 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3432 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3433 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3434 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3436 infoPtr->nSelectionMark = nItem;
3441 * Set selection(s) with keyboard.
3444 * [I] infoPtr : valid pointer to the listview structure
3445 * [I] nItem : item index
3446 * [I] space : VK_SPACE code sent
3449 * SUCCESS : TRUE (needs to be repainted)
3450 * FAILURE : FALSE (nothing has changed)
3452 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem, BOOL space)
3454 /* FIXME: pass in the state */
3455 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3456 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3457 BOOL bResult = FALSE;
3459 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3460 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3464 if (infoPtr->dwStyle & LVS_SINGLESEL || (wShift == 0 && wCtrl == 0))
3465 LISTVIEW_SetSelection(infoPtr, nItem);
3469 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3473 lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3474 lvItem.stateMask = LVIS_SELECTED;
3477 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3478 if (lvItem.state & LVIS_SELECTED)
3479 infoPtr->nSelectionMark = nItem;
3481 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3484 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3487 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3491 static BOOL LISTVIEW_GetItemAtPt(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3493 LVHITTESTINFO lvHitTestInfo;
3495 ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3496 lvHitTestInfo.pt.x = pt.x;
3497 lvHitTestInfo.pt.y = pt.y;
3499 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3501 lpLVItem->mask = LVIF_PARAM;
3502 lpLVItem->iItem = lvHitTestInfo.iItem;
3503 lpLVItem->iSubItem = 0;
3505 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3508 static inline BOOL LISTVIEW_isHotTracking(const LISTVIEW_INFO *infoPtr)
3510 return ((infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) ||
3511 (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE) ||
3512 (infoPtr->dwLvExStyle & LVS_EX_TWOCLICKACTIVATE));
3517 * Called when the mouse is being actively tracked and has hovered for a specified
3521 * [I] infoPtr : valid pointer to the listview structure
3522 * [I] fwKeys : key indicator
3523 * [I] x,y : mouse position
3526 * 0 if the message was processed, non-zero if there was an error
3529 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3530 * over the item for a certain period of time.
3533 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3535 if (LISTVIEW_isHotTracking(infoPtr))
3543 if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3544 LISTVIEW_SetSelection(infoPtr, item.iItem);
3552 * Called whenever WM_MOUSEMOVE is received.
3555 * [I] infoPtr : valid pointer to the listview structure
3556 * [I] fwKeys : key indicator
3557 * [I] x,y : mouse position
3560 * 0 if the message is processed, non-zero if there was an error
3562 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3564 TRACKMOUSEEVENT trackinfo;
3566 if (!(fwKeys & MK_LBUTTON))
3567 infoPtr->bLButtonDown = FALSE;
3569 if (infoPtr->bLButtonDown)
3573 LVHITTESTINFO lvHitTestInfo;
3574 WORD wDragWidth = GetSystemMetrics(SM_CXDRAG);
3575 WORD wDragHeight= GetSystemMetrics(SM_CYDRAG);
3577 rect.left = infoPtr->ptClickPos.x - wDragWidth;
3578 rect.right = infoPtr->ptClickPos.x + wDragWidth;
3579 rect.top = infoPtr->ptClickPos.y - wDragHeight;
3580 rect.bottom = infoPtr->ptClickPos.y + wDragHeight;
3585 lvHitTestInfo.pt = tmp;
3586 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3588 /* reset item marker */
3589 if (infoPtr->nLButtonDownItem != lvHitTestInfo.iItem)
3590 infoPtr->nLButtonDownItem = -1;
3592 if (!PtInRect(&rect, tmp))
3594 /* this path covers the following:
3595 1. WM_LBUTTONDOWN over selected item (sets focus on it)
3596 2. change focus with keys
3597 3. move mouse over item from step 1 selects it and moves focus on it */
3598 if (infoPtr->nLButtonDownItem != -1 &&
3599 !LISTVIEW_GetItemState(infoPtr, infoPtr->nLButtonDownItem, LVIS_SELECTED))
3603 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3604 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3606 LISTVIEW_SetItemState(infoPtr, infoPtr->nLButtonDownItem, &lvItem);
3607 infoPtr->nLButtonDownItem = -1;
3610 if (!infoPtr->bDragging)
3614 lvHitTestInfo.pt = infoPtr->ptClickPos;
3615 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3617 ZeroMemory(&nmlv, sizeof(nmlv));
3618 nmlv.iItem = lvHitTestInfo.iItem;
3619 nmlv.ptAction = infoPtr->ptClickPos;
3621 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3622 infoPtr->bDragging = TRUE;
3629 /* see if we are supposed to be tracking mouse hovering */
3630 if (LISTVIEW_isHotTracking(infoPtr)) {
3631 /* fill in the trackinfo struct */
3632 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3633 trackinfo.dwFlags = TME_QUERY;
3634 trackinfo.hwndTrack = infoPtr->hwndSelf;
3635 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3637 /* see if we are already tracking this hwnd */
3638 _TrackMouseEvent(&trackinfo);
3640 if(!(trackinfo.dwFlags & TME_HOVER)) {
3641 trackinfo.dwFlags = TME_HOVER;
3643 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3644 _TrackMouseEvent(&trackinfo);
3653 * Tests whether the item is assignable to a list with style lStyle
3655 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3657 if ( (lpLVItem->mask & LVIF_TEXT) &&
3658 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3659 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3667 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3670 * [I] infoPtr : valid pointer to the listview structure
3671 * [I] lpLVItem : valid pointer to new item attributes
3672 * [I] isNew : the item being set is being inserted
3673 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3674 * [O] bChanged : will be set to TRUE if the item really changed
3680 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3686 /* stateMask is ignored for LVM_INSERTITEM */
3687 UINT stateMask = isNew ? ~0 : lpLVItem->stateMask;
3691 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3693 if (lpLVItem->mask == 0) return TRUE;
3695 if (infoPtr->dwStyle & LVS_OWNERDATA)
3697 /* a virtual listview only stores selection and focus */
3698 if (lpLVItem->mask & ~LVIF_STATE)
3704 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3705 lpItem = DPA_GetPtr(hdpaSubItems, 0);
3709 /* we need to get the lParam and state of the item */
3710 item.iItem = lpLVItem->iItem;
3711 item.iSubItem = lpLVItem->iSubItem;
3712 item.mask = LVIF_STATE | LVIF_PARAM;
3713 item.stateMask = (infoPtr->dwStyle & LVS_OWNERDATA) ? LVIS_FOCUSED | LVIS_SELECTED : ~0;
3717 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3719 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3720 /* determine what fields will change */
3721 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & stateMask & ~infoPtr->uCallbackMask))
3722 uChanged |= LVIF_STATE;
3724 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3725 uChanged |= LVIF_IMAGE;
3727 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3728 uChanged |= LVIF_PARAM;
3730 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3731 uChanged |= LVIF_INDENT;
3733 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3734 uChanged |= LVIF_TEXT;
3736 TRACE("uChanged=0x%x\n", uChanged);
3737 if (!uChanged) return TRUE;
3740 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3741 nmlv.iItem = lpLVItem->iItem;
3742 nmlv.uNewState = (item.state & ~stateMask) | (lpLVItem->state & stateMask);
3743 nmlv.uOldState = item.state;
3744 nmlv.uChanged = uChanged;
3745 nmlv.lParam = item.lParam;
3747 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3748 /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
3750 if(lpItem && !isNew && infoPtr->bDoChangeNotify)
3752 HWND hwndSelf = infoPtr->hwndSelf;
3754 if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3756 if (!IsWindow(hwndSelf))
3760 /* copy information */
3761 if (lpLVItem->mask & LVIF_TEXT)
3762 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3764 if (lpLVItem->mask & LVIF_IMAGE)
3765 lpItem->hdr.iImage = lpLVItem->iImage;
3767 if (lpLVItem->mask & LVIF_PARAM)
3768 lpItem->lParam = lpLVItem->lParam;
3770 if (lpLVItem->mask & LVIF_INDENT)
3771 lpItem->iIndent = lpLVItem->iIndent;
3773 if (uChanged & LVIF_STATE)
3775 if (lpItem && (stateMask & ~infoPtr->uCallbackMask))
3777 lpItem->state &= ~stateMask;
3778 lpItem->state |= (lpLVItem->state & stateMask);
3780 if (lpLVItem->state & stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3782 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3783 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3785 else if (stateMask & LVIS_SELECTED)
3787 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3789 /* if we are asked to change focus, and we manage it, do it */
3790 if (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3792 if (lpLVItem->state & LVIS_FOCUSED)
3794 if (infoPtr->nFocusedItem != -1)
3796 /* remove current focus */
3797 item.mask = LVIF_STATE;
3799 item.stateMask = LVIS_FOCUSED;
3801 /* recurse with redrawing an item */
3802 LISTVIEW_SetItemState(infoPtr, infoPtr->nFocusedItem, &item);
3805 infoPtr->nFocusedItem = lpLVItem->iItem;
3806 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, infoPtr->uView == LV_VIEW_LIST);
3808 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3810 infoPtr->nFocusedItem = -1;
3815 /* if we're inserting the item, we're done */
3816 if (isNew) return TRUE;
3818 /* send LVN_ITEMCHANGED notification */
3819 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3820 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3827 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3830 * [I] infoPtr : valid pointer to the listview structure
3831 * [I] lpLVItem : valid pointer to new subitem attributes
3832 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3833 * [O] bChanged : will be set to TRUE if the item really changed
3839 static BOOL set_sub_item(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3842 SUBITEM_INFO *lpSubItem;
3844 /* we do not support subitems for virtual listviews */
3845 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3847 /* set subitem only if column is present */
3848 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3850 /* First do some sanity checks */
3851 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
3852 particularly useful. We currently do not actually do anything with
3853 the flag on subitems.
3855 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)) return FALSE;
3856 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))) return TRUE;
3858 /* get the subitem structure, and create it if not there */
3859 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3860 assert (hdpaSubItems);
3862 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3865 SUBITEM_INFO *tmpSubItem;
3868 lpSubItem = Alloc(sizeof(SUBITEM_INFO));
3869 if (!lpSubItem) return FALSE;
3870 /* we could binary search here, if need be...*/
3871 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3873 tmpSubItem = DPA_GetPtr(hdpaSubItems, i);
3874 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3876 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3881 lpSubItem->iSubItem = lpLVItem->iSubItem;
3882 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3886 if (lpLVItem->mask & LVIF_IMAGE)
3887 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3889 lpSubItem->hdr.iImage = lpLVItem->iImage;
3893 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpSubItem->hdr.pszText, lpLVItem->pszText, isW))
3895 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3904 * Sets item attributes.
3907 * [I] infoPtr : valid pointer to the listview structure
3908 * [I] lpLVItem : new item attributes
3909 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3915 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem, BOOL isW)
3917 HWND hwndSelf = infoPtr->hwndSelf;
3918 LPWSTR pszText = NULL;
3919 BOOL bResult, bChanged = FALSE;
3921 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3923 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3926 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3927 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3929 pszText = lpLVItem->pszText;
3930 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
3933 /* actually set the fields */
3934 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3936 if (lpLVItem->iSubItem)
3937 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3939 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3940 if (!IsWindow(hwndSelf))
3943 /* redraw item, if necessary */
3944 if (bChanged && !infoPtr->bIsDrawing)
3946 /* this little optimization eliminates some nasty flicker */
3947 if ( infoPtr->uView == LV_VIEW_DETAILS && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3948 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
3949 lpLVItem->iSubItem > 0 && lpLVItem->iSubItem <= DPA_GetPtrCount(infoPtr->hdpaColumns) )
3950 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3952 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3957 textfreeT(lpLVItem->pszText, isW);
3958 lpLVItem->pszText = pszText;
3966 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3969 * [I] infoPtr : valid pointer to the listview structure
3974 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *infoPtr)
3977 SCROLLINFO scrollInfo;
3979 scrollInfo.cbSize = sizeof(SCROLLINFO);
3980 scrollInfo.fMask = SIF_POS;
3982 if (infoPtr->uView == LV_VIEW_LIST)
3984 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3985 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3987 else if (infoPtr->uView == LV_VIEW_DETAILS)
3989 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3990 nItem = scrollInfo.nPos;
3994 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3995 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3998 TRACE("nItem=%d\n", nItem);
4006 * Erases the background of the given rectangle
4009 * [I] infoPtr : valid pointer to the listview structure
4010 * [I] hdc : device context handle
4011 * [I] lprcBox : clipping rectangle
4017 static inline BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
4019 if (!infoPtr->hBkBrush) return FALSE;
4021 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
4023 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
4031 * [I] infoPtr : valid pointer to the listview structure
4032 * [I] hdc : device context handle
4033 * [I] nItem : item index
4034 * [I] nSubItem : subitem index
4035 * [I] pos : item position in client coordinates
4036 * [I] cdmode : custom draw mode
4042 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
4045 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4046 static WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
4047 DWORD cdsubitemmode = CDRF_DODEFAULT;
4049 RECT rcSelect, rcBox, rcIcon, rcLabel, rcStateIcon;
4050 NMLVCUSTOMDRAW nmlvcd;
4055 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
4057 /* get information needed for drawing the item */
4058 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
4059 if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
4060 if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
4061 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
4062 lvItem.iItem = nItem;
4063 lvItem.iSubItem = nSubItem;
4066 lvItem.cchTextMax = DISP_TEXT_SIZE;
4067 lvItem.pszText = szDispText;
4068 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
4069 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
4070 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
4071 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
4072 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
4074 /* now check if we need to update the focus rectangle */
4075 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
4077 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
4078 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
4079 OffsetRect(&rcBox, pos.x, pos.y);
4080 OffsetRect(&rcSelect, pos.x, pos.y);
4081 OffsetRect(&rcIcon, pos.x, pos.y);
4082 OffsetRect(&rcStateIcon, pos.x, pos.y);
4083 OffsetRect(&rcLabel, pos.x, pos.y);
4084 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
4085 wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect),
4086 wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
4088 /* fill in the custom draw structure */
4089 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
4091 hOldFont = GetCurrentObject(hdc, OBJ_FONT);
4092 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
4093 if (cdmode & CDRF_SKIPDEFAULT) goto postpaint;
4094 if (cdmode & CDRF_NOTIFYITEMDRAW)
4095 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4096 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
4097 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
4098 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
4099 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
4101 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
4102 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
4104 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
4105 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4106 else if ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) == FALSE)
4107 prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE);
4109 /* in full row select, subitems, will just use main item's colors */
4110 if (nSubItem && infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
4111 nmlvcd.clrTextBk = CLR_NONE;
4114 if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && (nSubItem == 0))
4116 UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
4119 TRACE("uStateImage=%d\n", uStateImage);
4120 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
4121 rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
4126 himl = (infoPtr->uView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
4127 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
4129 TRACE("iImage=%d\n", lvItem.iImage);
4130 ImageList_DrawEx(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
4131 rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk, CLR_DEFAULT,
4132 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
4135 /* Don't bother painting item being edited */
4136 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
4138 /* FIXME: temporary hack */
4139 rcSelect.left = rcLabel.left;
4141 /* draw the selection background, if we're drawing the main item */
4144 /* in icon mode, the label rect is really what we want to draw the
4146 if (infoPtr->uView == LV_VIEW_ICON)
4149 if (infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
4150 rcSelect.right = rcBox.right;
4152 if (nmlvcd.clrTextBk != CLR_NONE)
4153 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
4154 /* store new focus rectangle */
4155 if (infoPtr->nFocusedItem == nItem) infoPtr->rcFocus = rcSelect;
4158 /* figure out the text drawing flags */
4159 uFormat = (infoPtr->uView == LV_VIEW_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
4160 if (infoPtr->uView == LV_VIEW_ICON)
4161 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
4164 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
4166 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
4167 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
4168 default: uFormat |= DT_LEFT;
4171 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
4173 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
4174 else rcLabel.left += LABEL_HOR_PADDING;
4176 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
4178 /* for GRIDLINES reduce the bottom so the text formats correctly */
4179 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
4182 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
4185 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
4186 notify_postpaint(infoPtr, &nmlvcd);
4187 if (cdsubitemmode & CDRF_NEWFONT)
4188 SelectObject(hdc, hOldFont);
4194 * Draws listview items when in owner draw mode.
4197 * [I] infoPtr : valid pointer to the listview structure
4198 * [I] hdc : device context handle
4203 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4205 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
4206 DWORD cditemmode = CDRF_DODEFAULT;
4207 NMLVCUSTOMDRAW nmlvcd;
4208 POINT Origin, Position;
4214 ZeroMemory(&dis, sizeof(dis));
4216 /* Get scroll info once before loop */
4217 LISTVIEW_GetOrigin(infoPtr, &Origin);
4219 /* iterate through the invalidated rows */
4220 while(iterator_next(i))
4222 item.iItem = i->nItem;
4224 item.mask = LVIF_PARAM | LVIF_STATE;
4225 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4226 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
4228 dis.CtlType = ODT_LISTVIEW;
4230 dis.itemID = item.iItem;
4231 dis.itemAction = ODA_DRAWENTIRE;
4233 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
4234 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
4235 dis.hwndItem = infoPtr->hwndSelf;
4237 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
4238 dis.rcItem.left = Position.x + Origin.x;
4239 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
4240 dis.rcItem.top = Position.y + Origin.y;
4241 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
4242 dis.itemData = item.lParam;
4244 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
4247 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
4248 * structure for the rest. of the paint cycle
4250 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
4251 if (cdmode & CDRF_NOTIFYITEMDRAW)
4252 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4254 if (!(cditemmode & CDRF_SKIPDEFAULT))
4256 prepaint_setup (infoPtr, hdc, &nmlvcd, FALSE);
4257 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
4260 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
4261 notify_postpaint(infoPtr, &nmlvcd);
4267 * Draws listview items when in report display mode.
4270 * [I] infoPtr : valid pointer to the listview structure
4271 * [I] hdc : device context handle
4272 * [I] cdmode : custom draw mode
4277 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4280 RECT rcClip, rcItem;
4281 POINT Origin, Position;
4288 /* figure out what to draw */
4289 rgntype = GetClipBox(hdc, &rcClip);
4290 if (rgntype == NULLREGION) return;
4292 /* Get scroll info once before loop */
4293 LISTVIEW_GetOrigin(infoPtr, &Origin);
4295 colRanges = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns));
4297 /* narrow down the columns we need to paint */
4298 for(col = 0; col < DPA_GetPtrCount(infoPtr->hdpaColumns); col++)
4300 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, col, 0);
4302 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
4303 if ((rcItem.right + Origin.x >= rcClip.left) && (rcItem.left + Origin.x < rcClip.right))
4304 ranges_additem(colRanges, index);
4306 iterator_rangesitems(&j, colRanges);
4308 /* in full row select, we _have_ to draw the main item */
4309 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
4312 /* iterate through the invalidated rows */
4313 while(iterator_next(i))
4315 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4316 Position.y += Origin.y;
4318 /* iterate through the invalidated columns */
4319 while(iterator_next(&j))
4321 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4322 Position.x = (j.nItem == 0) ? rcItem.left + Origin.x : Origin.x;
4324 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
4327 rcItem.bottom = infoPtr->nItemHeight;
4328 OffsetRect(&rcItem, Position.x, Position.y);
4329 if (!RectVisible(hdc, &rcItem)) continue;
4332 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
4335 iterator_destroy(&j);
4340 * Draws the gridlines if necessary when in report display mode.
4343 * [I] infoPtr : valid pointer to the listview structure
4344 * [I] hdc : device context handle
4349 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO *infoPtr, HDC hdc)
4355 RECT rcClip, rcItem = {0};
4363 /* figure out what to draw */
4364 rgntype = GetClipBox(hdc, &rcClip);
4365 if (rgntype == NULLREGION) return;
4367 /* Get scroll info once before loop */
4368 LISTVIEW_GetOrigin(infoPtr, &Origin);
4370 colRanges = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns));
4372 /* narrow down the columns we need to paint */
4373 for(col = 0; col < DPA_GetPtrCount(infoPtr->hdpaColumns); col++)
4375 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, col, 0);
4377 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
4378 if ((rcItem.right + Origin.x >= rcClip.left) && (rcItem.left + Origin.x < rcClip.right))
4379 ranges_additem(colRanges, index);
4382 /* is right most vertical line visible? */
4383 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
4385 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
4386 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
4387 rmost = (rcItem.right + Origin.x < rcClip.right);
4390 if ((hPen = CreatePen( PS_SOLID, 1, comctl32_color.clr3dFace )))
4392 hOldPen = SelectObject ( hdc, hPen );
4394 /* draw the vertical lines for the columns */
4395 iterator_rangesitems(&j, colRanges);
4396 while(iterator_next(&j))
4398 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4399 if (rcItem.left == 0) continue; /* skip leftmost column */
4400 rcItem.left += Origin.x;
4401 rcItem.right += Origin.x;
4402 rcItem.top = infoPtr->rcList.top;
4403 rcItem.bottom = infoPtr->rcList.bottom;
4404 TRACE("vert col=%d, rcItem=%s\n", j.nItem, wine_dbgstr_rect(&rcItem));
4405 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4406 LineTo (hdc, rcItem.left, rcItem.bottom);
4408 iterator_destroy(&j);
4409 /* draw rightmost grid line if visible */
4412 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
4413 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
4414 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
4416 rcItem.right += Origin.x;
4418 MoveToEx (hdc, rcItem.right, infoPtr->rcList.top, NULL);
4419 LineTo (hdc, rcItem.right, infoPtr->rcList.bottom);
4422 /* draw the horizontial lines for the rows */
4423 itemheight = LISTVIEW_CalculateItemHeight(infoPtr);
4424 rcItem.left = infoPtr->rcList.left;
4425 rcItem.right = infoPtr->rcList.right;
4426 rcItem.bottom = rcItem.top = Origin.y - 1;
4427 MoveToEx(hdc, rcItem.left, rcItem.top, NULL);
4428 LineTo(hdc, rcItem.right, rcItem.top);
4429 for(y=itemheight-1+Origin.y; y<=infoPtr->rcList.bottom; y+=itemheight)
4431 rcItem.bottom = rcItem.top = y;
4432 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem));
4433 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4434 LineTo (hdc, rcItem.right, rcItem.top);
4437 SelectObject( hdc, hOldPen );
4438 DeleteObject( hPen );
4444 * Draws listview items when in list display mode.
4447 * [I] infoPtr : valid pointer to the listview structure
4448 * [I] hdc : device context handle
4449 * [I] cdmode : custom draw mode
4454 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4456 POINT Origin, Position;
4458 /* Get scroll info once before loop */
4459 LISTVIEW_GetOrigin(infoPtr, &Origin);
4461 while(iterator_prev(i))
4463 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4464 Position.x += Origin.x;
4465 Position.y += Origin.y;
4467 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
4474 * Draws listview items.
4477 * [I] infoPtr : valid pointer to the listview structure
4478 * [I] hdc : device context handle
4479 * [I] prcErase : rect to be erased before refresh (may be NULL)
4484 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcErase)
4486 COLORREF oldTextColor = 0, oldBkColor = 0, oldClrTextBk, oldClrText;
4487 NMLVCUSTOMDRAW nmlvcd;
4494 HBITMAP hbmp = NULL;
4497 LISTVIEW_DUMP(infoPtr);
4499 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4500 TRACE("double buffering\n");
4502 hdc = CreateCompatibleDC(hdcOrig);
4504 ERR("Failed to create DC for backbuffer\n");
4507 hbmp = CreateCompatibleBitmap(hdcOrig, infoPtr->rcList.right,
4508 infoPtr->rcList.bottom);
4510 ERR("Failed to create bitmap for backbuffer\n");
4515 SelectObject(hdc, hbmp);
4516 SelectObject(hdc, infoPtr->hFont);
4518 /* Save dc values we're gonna trash while drawing
4519 * FIXME: Should be done in LISTVIEW_DrawItem() */
4520 hOldFont = SelectObject(hdc, infoPtr->hFont);
4521 oldBkMode = GetBkMode(hdc);
4522 oldBkColor = GetBkColor(hdc);
4523 oldTextColor = GetTextColor(hdc);
4526 infoPtr->bIsDrawing = TRUE;
4529 LISTVIEW_FillBkgnd(infoPtr, hdc, prcErase);
4530 } else if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4531 /* If no erasing was done (usually because RedrawWindow was called
4532 * with RDW_INVALIDATE only) we need to copy the old contents into
4533 * the backbuffer before continuing. */
4534 BitBlt(hdc, infoPtr->rcList.left, infoPtr->rcList.top,
4535 infoPtr->rcList.right - infoPtr->rcList.left,
4536 infoPtr->rcList.bottom - infoPtr->rcList.top,
4537 hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4540 /* FIXME: Shouldn't need to do this */
4541 oldClrTextBk = infoPtr->clrTextBk;
4542 oldClrText = infoPtr->clrText;
4544 infoPtr->cditemmode = CDRF_DODEFAULT;
4546 GetClientRect(infoPtr->hwndSelf, &rcClient);
4547 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4548 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4549 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4550 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4552 /* Use these colors to draw the items */
4553 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4554 infoPtr->clrText = nmlvcd.clrText;
4556 /* nothing to draw */
4557 if(infoPtr->nItemCount == 0) goto enddraw;
4559 /* figure out what we need to draw */
4560 iterator_visibleitems(&i, infoPtr, hdc);
4561 range = iterator_range(&i);
4563 /* send cache hint notification */
4564 if (infoPtr->dwStyle & LVS_OWNERDATA)
4568 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4569 nmlv.iFrom = range.lower;
4570 nmlv.iTo = range.upper - 1;
4571 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4574 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
4575 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4578 if (infoPtr->uView == LV_VIEW_DETAILS)
4579 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4580 else /* LV_VIEW_LIST, LV_VIEW_ICON or LV_VIEW_SMALLICON */
4581 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4583 /* if we have a focus rect and it's visible, draw it */
4584 if (infoPtr->bFocus && range.lower <= infoPtr->nFocusedItem &&
4585 (range.upper - 1) >= infoPtr->nFocusedItem)
4586 LISTVIEW_DrawFocusRect(infoPtr, hdc);
4588 iterator_destroy(&i);
4591 /* For LVS_EX_GRIDLINES go and draw lines */
4592 /* This includes the case where there were *no* items */
4593 if ((infoPtr->uView == LV_VIEW_DETAILS) && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
4594 LISTVIEW_RefreshReportGrid(infoPtr, hdc);
4596 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4597 notify_postpaint(infoPtr, &nmlvcd);
4599 infoPtr->clrTextBk = oldClrTextBk;
4600 infoPtr->clrText = oldClrText;
4603 BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top,
4604 infoPtr->rcList.right - infoPtr->rcList.left,
4605 infoPtr->rcList.bottom - infoPtr->rcList.top,
4606 hdc, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4611 SelectObject(hdc, hOldFont);
4612 SetBkMode(hdc, oldBkMode);
4613 SetBkColor(hdc, oldBkColor);
4614 SetTextColor(hdc, oldTextColor);
4617 infoPtr->bIsDrawing = FALSE;
4623 * Calculates the approximate width and height of a given number of items.
4626 * [I] infoPtr : valid pointer to the listview structure
4627 * [I] nItemCount : number of items
4628 * [I] wWidth : width
4629 * [I] wHeight : height
4632 * Returns a DWORD. The width in the low word and the height in high word.
4634 static DWORD LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO *infoPtr, INT nItemCount,
4635 WORD wWidth, WORD wHeight)
4637 INT nItemCountPerColumn = 1;
4638 INT nColumnCount = 0;
4639 DWORD dwViewRect = 0;
4641 if (nItemCount == -1)
4642 nItemCount = infoPtr->nItemCount;
4644 if (infoPtr->uView == LV_VIEW_LIST)
4646 if (wHeight == 0xFFFF)
4648 /* use current height */
4649 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4652 if (wHeight < infoPtr->nItemHeight)
4653 wHeight = infoPtr->nItemHeight;
4657 if (infoPtr->nItemHeight > 0)
4659 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4660 if (nItemCountPerColumn == 0)
4661 nItemCountPerColumn = 1;
4663 if (nItemCount % nItemCountPerColumn != 0)
4664 nColumnCount = nItemCount / nItemCountPerColumn;
4666 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4670 /* Microsoft padding magic */
4671 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4672 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4674 dwViewRect = MAKELONG(wWidth, wHeight);
4676 else if (infoPtr->uView == LV_VIEW_DETAILS)
4680 if (infoPtr->nItemCount > 0)
4682 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4683 wWidth = rcBox.right - rcBox.left;
4684 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4688 /* use current height and width */
4689 if (wHeight == 0xffff)
4690 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4691 if (wWidth == 0xffff)
4692 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4695 dwViewRect = MAKELONG(wWidth, wHeight);
4697 else if (infoPtr->uView == LV_VIEW_SMALLICON)
4698 FIXME("uView == LV_VIEW_SMALLICON: not implemented\n");
4699 else if (infoPtr->uView == LV_VIEW_ICON)
4700 FIXME("uView == LV_VIEW_ICON: not implemented\n");
4707 * Cancel edit label with saving item text.
4710 * [I] infoPtr : valid pointer to the listview structure
4713 * Always returns TRUE.
4715 static LRESULT LISTVIEW_CancelEditLabel(LISTVIEW_INFO *infoPtr)
4717 /* handle value will be lost after LISTVIEW_EndEditLabelT */
4718 HWND edit = infoPtr->hwndEdit;
4720 LISTVIEW_EndEditLabelT(infoPtr, TRUE, IsWindowUnicode(infoPtr->hwndEdit));
4721 SendMessageW(edit, WM_CLOSE, 0, 0);
4728 * Create a drag image list for the specified item.
4731 * [I] infoPtr : valid pointer to the listview structure
4732 * [I] iItem : index of item
4733 * [O] lppt : Upper-left corner of the image
4736 * Returns a handle to the image list if successful, NULL otherwise.
4738 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4744 HBITMAP hbmp, hOldbmp;
4745 HIMAGELIST dragList = 0;
4746 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
4748 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4751 rcItem.left = LVIR_BOUNDS;
4752 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4755 lppt->x = rcItem.left;
4756 lppt->y = rcItem.top;
4758 size.cx = rcItem.right - rcItem.left;
4759 size.cy = rcItem.bottom - rcItem.top;
4761 hdcOrig = GetDC(infoPtr->hwndSelf);
4762 hdc = CreateCompatibleDC(hdcOrig);
4763 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4764 hOldbmp = SelectObject(hdc, hbmp);
4766 rcItem.left = rcItem.top = 0;
4767 rcItem.right = size.cx;
4768 rcItem.bottom = size.cy;
4769 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4772 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4774 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4775 SelectObject(hdc, hOldbmp);
4776 ImageList_Add(dragList, hbmp, 0);
4779 SelectObject(hdc, hOldbmp);
4783 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4785 TRACE("ret=%p\n", dragList);
4793 * Removes all listview items and subitems.
4796 * [I] infoPtr : valid pointer to the listview structure
4802 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr, BOOL destroy)
4805 HDPA hdpaSubItems = NULL;
4812 /* we do it directly, to avoid notifications */
4813 ranges_clear(infoPtr->selectionRanges);
4814 infoPtr->nSelectionMark = -1;
4815 infoPtr->nFocusedItem = -1;
4816 SetRectEmpty(&infoPtr->rcFocus);
4817 /* But we are supposed to leave nHotItem as is! */
4820 /* send LVN_DELETEALLITEMS notification */
4821 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4823 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4825 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4827 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4829 /* send LVN_DELETEITEM notification, if not suppressed
4830 and if it is not a virtual listview */
4831 if (!bSuppress) notify_deleteitem(infoPtr, i);
4832 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
4833 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4835 hdrItem = DPA_GetPtr(hdpaSubItems, j);
4836 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4839 DPA_Destroy(hdpaSubItems);
4840 DPA_DeletePtr(infoPtr->hdpaItems, i);
4842 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4843 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4844 infoPtr->nItemCount --;
4849 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4850 LISTVIEW_UpdateScroll(infoPtr);
4852 LISTVIEW_InvalidateList(infoPtr);
4859 * Scrolls, and updates the columns, when a column is changing width.
4862 * [I] infoPtr : valid pointer to the listview structure
4863 * [I] nColumn : column to scroll
4864 * [I] dx : amount of scroll, in pixels
4869 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4871 COLUMN_INFO *lpColumnInfo;
4876 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4877 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4878 rcCol = lpColumnInfo->rcHeader;
4879 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4880 rcCol.left = rcCol.right;
4882 /* adjust the other columns */
4883 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4885 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4886 lpColumnInfo->rcHeader.left += dx;
4887 lpColumnInfo->rcHeader.right += dx;
4890 /* do not update screen if not in report mode */
4891 if (!is_redrawing(infoPtr) || infoPtr->uView != LV_VIEW_DETAILS) return;
4893 /* Need to reset the item width when inserting a new column */
4894 infoPtr->nItemWidth += dx;
4896 LISTVIEW_UpdateScroll(infoPtr);
4897 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4899 /* scroll to cover the deleted column, and invalidate for redraw */
4900 rcOld = infoPtr->rcList;
4901 rcOld.left = ptOrigin.x + rcCol.left + dx;
4902 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4907 * Removes a column from the listview control.
4910 * [I] infoPtr : valid pointer to the listview structure
4911 * [I] nColumn : column index
4917 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4921 TRACE("nColumn=%d\n", nColumn);
4923 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4924 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4926 /* While the MSDN specifically says that column zero should not be deleted,
4927 what actually happens is that the column itself is deleted but no items or subitems
4931 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4933 if (!SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nColumn, 0))
4936 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4937 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4939 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4941 SUBITEM_INFO *lpSubItem, *lpDelItem;
4943 INT nItem, nSubItem, i;
4945 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4947 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
4950 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4952 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
4953 if (lpSubItem->iSubItem == nColumn)
4956 lpDelItem = lpSubItem;
4958 else if (lpSubItem->iSubItem > nColumn)
4960 lpSubItem->iSubItem--;
4964 /* if we found our subitem, zapp it */
4968 if (is_textW(lpDelItem->hdr.pszText))
4969 Free(lpDelItem->hdr.pszText);
4974 /* free dpa memory */
4975 DPA_DeletePtr(hdpaSubItems, nSubItem);
4980 /* update the other column info */
4981 LISTVIEW_UpdateItemSize(infoPtr);
4982 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4983 LISTVIEW_InvalidateList(infoPtr);
4985 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4992 * Invalidates the listview after an item's insertion or deletion.
4995 * [I] infoPtr : valid pointer to the listview structure
4996 * [I] nItem : item index
4997 * [I] dir : -1 if deleting, 1 if inserting
5002 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
5004 INT nPerCol, nItemCol, nItemRow;
5008 /* if we don't refresh, what's the point of scrolling? */
5009 if (!is_redrawing(infoPtr)) return;
5011 assert (abs(dir) == 1);
5013 /* arrange icons if autoarrange is on */
5014 if (is_autoarrange(infoPtr))
5016 BOOL arrange = TRUE;
5017 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
5018 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
5019 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
5022 /* scrollbars need updating */
5023 LISTVIEW_UpdateScroll(infoPtr);
5025 /* figure out the item's position */
5026 if (infoPtr->uView == LV_VIEW_DETAILS)
5027 nPerCol = infoPtr->nItemCount + 1;
5028 else if (infoPtr->uView == LV_VIEW_LIST)
5029 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
5030 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
5033 nItemCol = nItem / nPerCol;
5034 nItemRow = nItem % nPerCol;
5035 LISTVIEW_GetOrigin(infoPtr, &Origin);
5037 /* move the items below up a slot */
5038 rcScroll.left = nItemCol * infoPtr->nItemWidth;
5039 rcScroll.top = nItemRow * infoPtr->nItemHeight;
5040 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
5041 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
5042 OffsetRect(&rcScroll, Origin.x, Origin.y);
5043 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
5044 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
5046 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
5047 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
5048 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
5051 /* report has only that column, so we're done */
5052 if (infoPtr->uView == LV_VIEW_DETAILS) return;
5054 /* now for LISTs, we have to deal with the columns to the right */
5055 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
5057 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
5058 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
5059 OffsetRect(&rcScroll, Origin.x, Origin.y);
5060 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
5061 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
5062 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
5067 * Removes an item from the listview control.
5070 * [I] infoPtr : valid pointer to the listview structure
5071 * [I] nItem : item index
5077 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
5080 const BOOL is_icon = (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON);
5082 TRACE("(nItem=%d)\n", nItem);
5084 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5086 /* remove selection, and focus */
5088 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
5089 LISTVIEW_SetItemState(infoPtr, nItem, &item);
5091 /* send LVN_DELETEITEM notification. */
5092 if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
5094 /* we need to do this here, because we'll be deleting stuff */
5096 LISTVIEW_InvalidateItem(infoPtr, nItem);
5098 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
5106 hdpaSubItems = DPA_DeletePtr(infoPtr->hdpaItems, nItem);
5107 lpItem = DPA_GetPtr(hdpaSubItems, 0);
5109 /* free id struct */
5110 i = DPA_GetPtrIndex(infoPtr->hdpaItemIds, lpItem->id);
5111 lpID = DPA_GetPtr(infoPtr->hdpaItemIds, i);
5112 DPA_DeletePtr(infoPtr->hdpaItemIds, i);
5114 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
5116 hdrItem = DPA_GetPtr(hdpaSubItems, i);
5117 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
5120 DPA_Destroy(hdpaSubItems);
5125 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5126 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
5129 infoPtr->nItemCount--;
5130 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
5132 /* now is the invalidation fun */
5134 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
5141 * Callback implementation for editlabel control
5144 * [I] infoPtr : valid pointer to the listview structure
5145 * [I] storeText : store edit box text as item text
5146 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
5152 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, BOOL storeText, BOOL isW)
5154 HWND hwndSelf = infoPtr->hwndSelf;
5155 NMLVDISPINFOW dispInfo;
5156 INT editedItem = infoPtr->nEditLabelItem;
5158 WCHAR *pszText = NULL;
5163 DWORD len = isW ? GetWindowTextLengthW(infoPtr->hwndEdit) : GetWindowTextLengthA(infoPtr->hwndEdit);
5167 if ((pszText = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))))
5169 if (isW) GetWindowTextW(infoPtr->hwndEdit, pszText, len+1);
5170 else GetWindowTextA(infoPtr->hwndEdit, (CHAR*)pszText, len+1);
5175 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
5177 infoPtr->nEditLabelItem = -1;
5178 infoPtr->hwndEdit = 0;
5180 ZeroMemory(&dispInfo, sizeof(dispInfo));
5181 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
5182 dispInfo.item.iItem = editedItem;
5183 dispInfo.item.iSubItem = 0;
5184 dispInfo.item.stateMask = ~0;
5185 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item))
5192 bSame = (lstrcmpW(dispInfo.item.pszText, pszText) == 0);
5195 LPWSTR tmp = textdupTtoW(pszText, FALSE);
5196 bSame = (lstrcmpW(dispInfo.item.pszText, tmp) == 0);
5197 textfreeT(tmp, FALSE);
5205 /* add the text from the edit in */
5206 dispInfo.item.mask |= LVIF_TEXT;
5207 dispInfo.item.pszText = pszText;
5208 dispInfo.item.cchTextMax = textlenT(pszText, isW);
5210 /* Do we need to update the Item Text */
5211 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW))
5216 if (!IsWindow(hwndSelf))
5221 if (!pszText) return TRUE;
5223 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
5225 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, editedItem);
5226 ITEM_INFO* lpItem = DPA_GetPtr(hdpaSubItems, 0);
5227 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
5229 LISTVIEW_InvalidateItem(infoPtr, editedItem);
5235 ZeroMemory(&dispInfo, sizeof(dispInfo));
5236 dispInfo.item.mask = LVIF_TEXT;
5237 dispInfo.item.iItem = editedItem;
5238 dispInfo.item.iSubItem = 0;
5239 dispInfo.item.pszText = pszText;
5240 dispInfo.item.cchTextMax = textlenT(pszText, isW);
5241 res = LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
5251 * Begin in place editing of specified list view item
5254 * [I] infoPtr : valid pointer to the listview structure
5255 * [I] nItem : item index
5256 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
5262 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
5264 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
5265 NMLVDISPINFOW dispInfo;
5268 HWND hwndSelf = infoPtr->hwndSelf;
5270 HFONT hOldFont = NULL;
5271 TEXTMETRICW textMetric;
5273 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
5275 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
5277 /* Is the EditBox still there, if so remove it */
5278 if(infoPtr->hwndEdit != 0)
5280 SetFocus(infoPtr->hwndSelf);
5281 infoPtr->hwndEdit = 0;
5284 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5286 infoPtr->nEditLabelItem = nItem;
5288 LISTVIEW_SetSelection(infoPtr, nItem);
5289 LISTVIEW_SetItemFocus(infoPtr, nItem);
5290 LISTVIEW_InvalidateItem(infoPtr, nItem);
5292 rect.left = LVIR_LABEL;
5293 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
5295 ZeroMemory(&dispInfo, sizeof(dispInfo));
5296 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
5297 dispInfo.item.iItem = nItem;
5298 dispInfo.item.iSubItem = 0;
5299 dispInfo.item.stateMask = ~0;
5300 dispInfo.item.pszText = szDispText;
5301 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
5302 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
5304 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE, isW);
5305 if (!infoPtr->hwndEdit) return 0;
5307 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
5309 if (!IsWindow(hwndSelf))
5311 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
5312 infoPtr->hwndEdit = 0;
5316 /* Now position and display edit box */
5317 hdc = GetDC(infoPtr->hwndSelf);
5319 /* Select the font to get appropriate metric dimensions */
5320 if(infoPtr->hFont != 0)
5321 hOldFont = SelectObject(hdc, infoPtr->hFont);
5323 /* Get String Length in pixels */
5324 GetTextExtentPoint32W(hdc, dispInfo.item.pszText, lstrlenW(dispInfo.item.pszText), &sz);
5326 /* Add Extra spacing for the next character */
5327 GetTextMetricsW(hdc, &textMetric);
5328 sz.cx += (textMetric.tmMaxCharWidth * 2);
5330 if(infoPtr->hFont != 0)
5331 SelectObject(hdc, hOldFont);
5333 ReleaseDC(infoPtr->hwndSelf, hdc);
5335 MoveWindow(infoPtr->hwndEdit, rect.left - 2, rect.top - 1, sz.cx,
5336 rect.bottom - rect.top + 2, FALSE);
5337 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
5338 SetFocus(infoPtr->hwndEdit);
5339 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
5340 return infoPtr->hwndEdit;
5346 * Ensures the specified item is visible, scrolling into view if necessary.
5349 * [I] infoPtr : valid pointer to the listview structure
5350 * [I] nItem : item index
5351 * [I] bPartial : partially or entirely visible
5357 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
5359 INT nScrollPosHeight = 0;
5360 INT nScrollPosWidth = 0;
5361 INT nHorzAdjust = 0;
5362 INT nVertAdjust = 0;
5365 RECT rcItem, rcTemp;
5367 rcItem.left = LVIR_BOUNDS;
5368 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
5370 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
5372 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
5374 /* scroll left/right, but in LV_VIEW_DETAILS mode */
5375 if (infoPtr->uView == LV_VIEW_LIST)
5376 nScrollPosWidth = infoPtr->nItemWidth;
5377 else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
5378 nScrollPosWidth = 1;
5380 if (rcItem.left < infoPtr->rcList.left)
5383 if (infoPtr->uView != LV_VIEW_DETAILS) nHorzDiff = rcItem.left - infoPtr->rcList.left;
5388 if (infoPtr->uView != LV_VIEW_DETAILS) nHorzDiff = rcItem.right - infoPtr->rcList.right;
5392 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
5394 /* scroll up/down, but not in LVS_LIST mode */
5395 if (infoPtr->uView == LV_VIEW_DETAILS)
5396 nScrollPosHeight = infoPtr->nItemHeight;
5397 else if ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON))
5398 nScrollPosHeight = 1;
5400 if (rcItem.top < infoPtr->rcList.top)
5403 if (infoPtr->uView != LV_VIEW_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
5408 if (infoPtr->uView != LV_VIEW_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
5412 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
5414 if (nScrollPosWidth)
5416 INT diff = nHorzDiff / nScrollPosWidth;
5417 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
5418 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
5421 if (nScrollPosHeight)
5423 INT diff = nVertDiff / nScrollPosHeight;
5424 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
5425 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
5433 * Searches for an item with specific characteristics.
5436 * [I] hwnd : window handle
5437 * [I] nStart : base item index
5438 * [I] lpFindInfo : item information to look for
5441 * SUCCESS : index of item
5444 static INT LISTVIEW_FindItemW(const LISTVIEW_INFO *infoPtr, INT nStart,
5445 const LVFINDINFOW *lpFindInfo)
5447 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5448 BOOL bWrap = FALSE, bNearest = FALSE;
5449 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
5450 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
5451 POINT Position, Destination;
5454 /* Search in virtual listviews should be done by application, not by
5455 listview control, so we just send LVN_ODFINDITEMW and return the result */
5456 if (infoPtr->dwStyle & LVS_OWNERDATA)
5460 nmlv.iStart = nStart;
5461 nmlv.lvfi = *lpFindInfo;
5462 return notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
5465 if (!lpFindInfo || nItem < 0) return -1;
5468 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
5470 lvItem.mask |= LVIF_TEXT;
5471 lvItem.pszText = szDispText;
5472 lvItem.cchTextMax = DISP_TEXT_SIZE;
5475 if (lpFindInfo->flags & LVFI_WRAP)
5478 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
5479 (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON))
5484 LISTVIEW_GetOrigin(infoPtr, &Origin);
5485 Destination.x = lpFindInfo->pt.x - Origin.x;
5486 Destination.y = lpFindInfo->pt.y - Origin.y;
5487 switch(lpFindInfo->vkDirection)
5489 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
5490 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
5491 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
5492 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
5493 case VK_HOME: Destination.x = Destination.y = 0; break;
5494 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5495 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5497 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
5498 Destination.x = rcArea.right;
5499 Destination.y = rcArea.bottom;
5501 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
5505 else Destination.x = Destination.y = 0;
5507 /* if LVFI_PARAM is specified, all other flags are ignored */
5508 if (lpFindInfo->flags & LVFI_PARAM)
5510 lvItem.mask |= LVIF_PARAM;
5512 lvItem.mask &= ~LVIF_TEXT;
5516 for (; nItem < nLast; nItem++)
5518 lvItem.iItem = nItem;
5519 lvItem.iSubItem = 0;
5520 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
5522 if (lvItem.mask & LVIF_PARAM)
5524 if (lpFindInfo->lParam == lvItem.lParam)
5530 if (lvItem.mask & LVIF_TEXT)
5532 if (lpFindInfo->flags & LVFI_PARTIAL)
5534 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
5538 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
5542 if (!bNearest) return nItem;
5544 /* This is very inefficient. To do a good job here,
5545 * we need a sorted array of (x,y) item positions */
5546 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5548 /* compute the distance^2 to the destination */
5549 xdist = Destination.x - Position.x;
5550 ydist = Destination.y - Position.y;
5551 dist = xdist * xdist + ydist * ydist;
5553 /* remember the distance, and item if it's closer */
5557 nNearestItem = nItem;
5564 nLast = min(nStart + 1, infoPtr->nItemCount);
5569 return nNearestItem;
5574 * Searches for an item with specific characteristics.
5577 * [I] hwnd : window handle
5578 * [I] nStart : base item index
5579 * [I] lpFindInfo : item information to look for
5582 * SUCCESS : index of item
5585 static INT LISTVIEW_FindItemA(const LISTVIEW_INFO *infoPtr, INT nStart,
5586 const LVFINDINFOA *lpFindInfo)
5588 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
5593 memcpy(&fiw, lpFindInfo, sizeof(fiw));
5594 if (hasText) fiw.psz = strW = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
5595 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
5596 textfreeT(strW, FALSE);
5602 * Retrieves the background image of the listview control.
5605 * [I] infoPtr : valid pointer to the listview structure
5606 * [O] lpBkImage : background image attributes
5612 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
5614 /* FIXME (listview, "empty stub!\n"); */
5620 * Retrieves column attributes.
5623 * [I] infoPtr : valid pointer to the listview structure
5624 * [I] nColumn : column index
5625 * [IO] lpColumn : column information
5626 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5627 * otherwise it is in fact a LPLVCOLUMNA
5633 static BOOL LISTVIEW_GetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
5635 COLUMN_INFO *lpColumnInfo;
5638 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5639 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
5641 /* initialize memory */
5642 ZeroMemory(&hdi, sizeof(hdi));
5644 if (lpColumn->mask & LVCF_TEXT)
5646 hdi.mask |= HDI_TEXT;
5647 hdi.pszText = lpColumn->pszText;
5648 hdi.cchTextMax = lpColumn->cchTextMax;
5651 if (lpColumn->mask & LVCF_IMAGE)
5652 hdi.mask |= HDI_IMAGE;
5654 if (lpColumn->mask & LVCF_ORDER)
5655 hdi.mask |= HDI_ORDER;
5657 if (lpColumn->mask & LVCF_SUBITEM)
5658 hdi.mask |= HDI_LPARAM;
5660 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
5662 if (lpColumn->mask & LVCF_FMT)
5663 lpColumn->fmt = lpColumnInfo->fmt;
5665 if (lpColumn->mask & LVCF_WIDTH)
5666 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
5668 if (lpColumn->mask & LVCF_IMAGE)
5669 lpColumn->iImage = hdi.iImage;
5671 if (lpColumn->mask & LVCF_ORDER)
5672 lpColumn->iOrder = hdi.iOrder;
5674 if (lpColumn->mask & LVCF_SUBITEM)
5675 lpColumn->iSubItem = hdi.lParam;
5681 static BOOL LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5683 TRACE("iCount=%d, lpiArray=%p\n", iCount, lpiArray);
5688 return SendMessageW(infoPtr->hwndHeader, HDM_GETORDERARRAY, iCount, (LPARAM)lpiArray);
5693 * Retrieves the column width.
5696 * [I] infoPtr : valid pointer to the listview structure
5697 * [I] int : column index
5700 * SUCCESS : column width
5703 static INT LISTVIEW_GetColumnWidth(const LISTVIEW_INFO *infoPtr, INT nColumn)
5705 INT nColumnWidth = 0;
5708 TRACE("nColumn=%d\n", nColumn);
5710 /* we have a 'column' in LIST and REPORT mode only */
5711 switch(infoPtr->uView)
5714 nColumnWidth = infoPtr->nItemWidth;
5716 case LV_VIEW_DETAILS:
5717 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDM_ITEMCHANGED.
5718 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
5719 * HDM_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
5721 * TODO: should we do the same in LVM_GETCOLUMN?
5723 hdItem.mask = HDI_WIDTH;
5724 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdItem))
5726 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr->hwndSelf, nColumn);
5729 nColumnWidth = hdItem.cxy;
5733 TRACE("nColumnWidth=%d\n", nColumnWidth);
5734 return nColumnWidth;
5739 * In list or report display mode, retrieves the number of items that can fit
5740 * vertically in the visible area. In icon or small icon display mode,
5741 * retrieves the total number of visible items.
5744 * [I] infoPtr : valid pointer to the listview structure
5747 * Number of fully visible items.
5749 static INT LISTVIEW_GetCountPerPage(const LISTVIEW_INFO *infoPtr)
5751 switch (infoPtr->uView)
5754 case LV_VIEW_SMALLICON:
5755 return infoPtr->nItemCount;
5756 case LV_VIEW_DETAILS:
5757 return LISTVIEW_GetCountPerColumn(infoPtr);
5759 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5767 * Retrieves an image list handle.
5770 * [I] infoPtr : valid pointer to the listview structure
5771 * [I] nImageList : image list identifier
5774 * SUCCESS : image list handle
5777 static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImageList)
5781 case LVSIL_NORMAL: return infoPtr->himlNormal;
5782 case LVSIL_SMALL: return infoPtr->himlSmall;
5783 case LVSIL_STATE: return infoPtr->himlState;
5788 /* LISTVIEW_GetISearchString */
5792 * Retrieves item attributes.
5795 * [I] hwnd : window handle
5796 * [IO] lpLVItem : item info
5797 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5798 * if FALSE, then lpLVItem is a LPLVITEMA.
5801 * This is the internal 'GetItem' interface -- it tries to
5802 * be smart and avoid text copies, if possible, by modifying
5803 * lpLVItem->pszText to point to the text string. Please note
5804 * that this is not always possible (e.g. OWNERDATA), so on
5805 * entry you *must* supply valid values for pszText, and cchTextMax.
5806 * The only difference to the documented interface is that upon
5807 * return, you should use *only* the lpLVItem->pszText, rather than
5808 * the buffer pointer you provided on input. Most code already does
5809 * that, so it's not a problem.
5810 * For the two cases when the text must be copied (that is,
5811 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5817 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5819 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5820 NMLVDISPINFOW dispInfo;
5826 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5828 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5831 if (lpLVItem->mask == 0) return TRUE;
5833 /* make a local copy */
5834 isubitem = lpLVItem->iSubItem;
5836 /* a quick optimization if all we're asked is the focus state
5837 * these queries are worth optimising since they are common,
5838 * and can be answered in constant time, without the heavy accesses */
5839 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5840 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5842 lpLVItem->state = 0;
5843 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5844 lpLVItem->state |= LVIS_FOCUSED;
5848 ZeroMemory(&dispInfo, sizeof(dispInfo));
5850 /* if the app stores all the data, handle it separately */
5851 if (infoPtr->dwStyle & LVS_OWNERDATA)
5853 dispInfo.item.state = 0;
5855 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5856 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) ||
5857 ((lpLVItem->mask & LVIF_STATE) && (infoPtr->uCallbackMask & lpLVItem->stateMask)))
5859 UINT mask = lpLVItem->mask;
5861 /* NOTE: copy only fields which we _know_ are initialized, some apps
5862 * depend on the uninitialized fields being 0 */
5863 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5864 dispInfo.item.iItem = lpLVItem->iItem;
5865 dispInfo.item.iSubItem = isubitem;
5866 if (lpLVItem->mask & LVIF_TEXT)
5868 if (lpLVItem->mask & LVIF_NORECOMPUTE)
5870 dispInfo.item.mask &= ~(LVIF_TEXT | LVIF_NORECOMPUTE);
5873 dispInfo.item.pszText = lpLVItem->pszText;
5874 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5877 if (lpLVItem->mask & LVIF_STATE)
5878 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5879 /* could be zeroed on LVIF_NORECOMPUTE case */
5880 if (dispInfo.item.mask != 0)
5882 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5883 dispInfo.item.stateMask = lpLVItem->stateMask;
5884 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5886 /* full size structure expected - _WIN32IE >= 0x560 */
5887 *lpLVItem = dispInfo.item;
5889 else if (lpLVItem->mask & LVIF_INDENT)
5891 /* indent member expected - _WIN32IE >= 0x300 */
5892 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5896 /* minimal structure expected */
5897 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5899 lpLVItem->mask = mask;
5900 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5904 /* make sure lParam is zeroed out */
5905 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5907 /* callback marked pointer required here */
5908 if ((lpLVItem->mask & LVIF_TEXT) && (lpLVItem->mask & LVIF_NORECOMPUTE))
5909 lpLVItem->pszText = LPSTR_TEXTCALLBACKW;
5911 /* we store only a little state, so if we're not asked, we're done */
5912 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5914 /* if focus is handled by us, report it */
5915 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5917 lpLVItem->state &= ~LVIS_FOCUSED;
5918 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5919 lpLVItem->state |= LVIS_FOCUSED;
5922 /* and do the same for selection, if we handle it */
5923 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5925 lpLVItem->state &= ~LVIS_SELECTED;
5926 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5927 lpLVItem->state |= LVIS_SELECTED;
5933 /* find the item and subitem structures before we proceed */
5934 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5935 lpItem = DPA_GetPtr(hdpaSubItems, 0);
5940 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5941 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5944 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5949 pItemHdr = &lpItem->hdr;
5951 /* Do we need to query the state from the app? */
5952 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5954 dispInfo.item.mask |= LVIF_STATE;
5955 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5958 /* Do we need to enquire about the image? */
5959 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5960 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5962 dispInfo.item.mask |= LVIF_IMAGE;
5963 dispInfo.item.iImage = I_IMAGECALLBACK;
5966 /* Only items support indentation */
5967 if ((lpLVItem->mask & LVIF_INDENT) && lpItem->iIndent == I_INDENTCALLBACK &&
5970 dispInfo.item.mask |= LVIF_INDENT;
5971 dispInfo.item.iIndent = I_INDENTCALLBACK;
5974 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5975 if ((lpLVItem->mask & LVIF_TEXT) && !(lpLVItem->mask & LVIF_NORECOMPUTE) &&
5976 !is_textW(pItemHdr->pszText))
5978 dispInfo.item.mask |= LVIF_TEXT;
5979 dispInfo.item.pszText = lpLVItem->pszText;
5980 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5981 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5982 *dispInfo.item.pszText = '\0';
5985 /* If we don't have all the requested info, query the application */
5986 if (dispInfo.item.mask != 0)
5988 dispInfo.item.iItem = lpLVItem->iItem;
5989 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5990 dispInfo.item.lParam = lpItem->lParam;
5991 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5992 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5995 /* we should not store values for subitems */
5996 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5998 /* Now, handle the iImage field */
5999 if (dispInfo.item.mask & LVIF_IMAGE)
6001 lpLVItem->iImage = dispInfo.item.iImage;
6002 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
6003 pItemHdr->iImage = dispInfo.item.iImage;
6005 else if (lpLVItem->mask & LVIF_IMAGE)
6007 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
6008 lpLVItem->iImage = pItemHdr->iImage;
6010 lpLVItem->iImage = 0;
6013 /* The pszText field */
6014 if (dispInfo.item.mask & LVIF_TEXT)
6016 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
6017 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
6019 lpLVItem->pszText = dispInfo.item.pszText;
6021 else if (lpLVItem->mask & LVIF_TEXT)
6023 /* if LVN_GETDISPINFO's disabled with LVIF_NORECOMPUTE return callback placeholder */
6024 if (isW || !is_textW(pItemHdr->pszText)) lpLVItem->pszText = pItemHdr->pszText;
6025 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
6028 /* Next is the lParam field */
6029 if (dispInfo.item.mask & LVIF_PARAM)
6031 lpLVItem->lParam = dispInfo.item.lParam;
6032 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
6033 lpItem->lParam = dispInfo.item.lParam;
6035 else if (lpLVItem->mask & LVIF_PARAM)
6036 lpLVItem->lParam = lpItem->lParam;
6038 /* if this is a subitem, we're done */
6039 if (isubitem) return TRUE;
6041 /* ... the state field (this one is different due to uCallbackmask) */
6042 if (lpLVItem->mask & LVIF_STATE)
6044 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
6045 if (dispInfo.item.mask & LVIF_STATE)
6047 lpLVItem->state &= ~dispInfo.item.stateMask;
6048 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
6050 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
6052 lpLVItem->state &= ~LVIS_FOCUSED;
6053 if (infoPtr->nFocusedItem == lpLVItem->iItem)
6054 lpLVItem->state |= LVIS_FOCUSED;
6056 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
6058 lpLVItem->state &= ~LVIS_SELECTED;
6059 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
6060 lpLVItem->state |= LVIS_SELECTED;
6064 /* and last, but not least, the indent field */
6065 if (dispInfo.item.mask & LVIF_INDENT)
6067 lpLVItem->iIndent = dispInfo.item.iIndent;
6068 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && lpItem->iIndent == I_INDENTCALLBACK)
6069 lpItem->iIndent = dispInfo.item.iIndent;
6071 else if (lpLVItem->mask & LVIF_INDENT)
6073 lpLVItem->iIndent = lpItem->iIndent;
6081 * Retrieves item attributes.
6084 * [I] hwnd : window handle
6085 * [IO] lpLVItem : item info
6086 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
6087 * if FALSE, then lpLVItem is a LPLVITEMA.
6090 * This is the external 'GetItem' interface -- it properly copies
6091 * the text in the provided buffer.
6097 static BOOL LISTVIEW_GetItemExtT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
6102 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
6105 pszText = lpLVItem->pszText;
6106 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
6107 if (bResult && lpLVItem->pszText != pszText)
6109 if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW)
6110 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
6112 pszText = LPSTR_TEXTCALLBACKW;
6114 lpLVItem->pszText = pszText;
6122 * Retrieves the position (upper-left) of the listview control item.
6123 * Note that for LVS_ICON style, the upper-left is that of the icon
6124 * and not the bounding box.
6127 * [I] infoPtr : valid pointer to the listview structure
6128 * [I] nItem : item index
6129 * [O] lpptPosition : coordinate information
6135 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
6139 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
6141 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
6143 LISTVIEW_GetOrigin(infoPtr, &Origin);
6144 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
6146 if (infoPtr->uView == LV_VIEW_ICON)
6148 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
6149 lpptPosition->y += ICON_TOP_PADDING;
6151 lpptPosition->x += Origin.x;
6152 lpptPosition->y += Origin.y;
6154 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
6161 * Retrieves the bounding rectangle for a listview control item.
6164 * [I] infoPtr : valid pointer to the listview structure
6165 * [I] nItem : item index
6166 * [IO] lprc : bounding rectangle coordinates
6167 * lprc->left specifies the portion of the item for which the bounding
6168 * rectangle will be retrieved.
6170 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
6171 * including the icon and label.
6174 * * Experiment shows that native control returns:
6175 * * width = min (48, length of text line)
6176 * * .left = position.x - (width - iconsize.cx)/2
6177 * * .right = .left + width
6178 * * height = #lines of text * ntmHeight + icon height + 8
6179 * * .top = position.y - 2
6180 * * .bottom = .top + height
6181 * * separation between items .y = itemSpacing.cy - height
6182 * * .x = itemSpacing.cx - width
6183 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
6186 * * Experiment shows that native control returns:
6187 * * width = iconSize.cx + 16
6188 * * .left = position.x - (width - iconsize.cx)/2
6189 * * .right = .left + width
6190 * * height = iconSize.cy + 4
6191 * * .top = position.y - 2
6192 * * .bottom = .top + height
6193 * * separation between items .y = itemSpacing.cy - height
6194 * * .x = itemSpacing.cx - width
6195 * LVIR_LABEL Returns the bounding rectangle of the item text.
6198 * * Experiment shows that native control returns:
6199 * * width = text length
6200 * * .left = position.x - width/2
6201 * * .right = .left + width
6202 * * height = ntmH * linecount + 2
6203 * * .top = position.y + iconSize.cy + 6
6204 * * .bottom = .top + height
6205 * * separation between items .y = itemSpacing.cy - height
6206 * * .x = itemSpacing.cx - width
6207 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
6208 * rectangles, but excludes columns in report view.
6215 * Note that the bounding rectangle of the label in the LVS_ICON view depends
6216 * upon whether the window has the focus currently and on whether the item
6217 * is the one with the focus. Ensure that the control's record of which
6218 * item has the focus agrees with the items' records.
6220 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
6222 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6223 BOOL doLabel = TRUE, oversizedBox = FALSE;
6224 POINT Position, Origin;
6227 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
6229 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
6231 LISTVIEW_GetOrigin(infoPtr, &Origin);
6232 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
6234 /* Be smart and try to figure out the minimum we have to do */
6235 if (lprc->left == LVIR_ICON) doLabel = FALSE;
6236 if (infoPtr->uView == LV_VIEW_DETAILS && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
6237 if (infoPtr->uView == LV_VIEW_ICON && lprc->left != LVIR_ICON &&
6238 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
6239 oversizedBox = TRUE;
6241 /* get what we need from the item before hand, so we make
6242 * only one request. This can speed up things, if data
6243 * is stored on the app side */
6245 if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
6246 if (doLabel) lvItem.mask |= LVIF_TEXT;
6247 lvItem.iItem = nItem;
6248 lvItem.iSubItem = 0;
6249 lvItem.pszText = szDispText;
6250 lvItem.cchTextMax = DISP_TEXT_SIZE;
6251 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
6252 /* we got the state already up, simulate it here, to avoid a reget */
6253 if (infoPtr->uView == LV_VIEW_ICON && (lprc->left != LVIR_ICON))
6255 lvItem.mask |= LVIF_STATE;
6256 lvItem.stateMask = LVIS_FOCUSED;
6257 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
6260 if (infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
6261 lprc->left = LVIR_BOUNDS;
6265 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
6269 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc);
6273 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
6276 case LVIR_SELECTBOUNDS:
6277 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL);
6281 WARN("Unknown value: %d\n", lprc->left);
6285 if (infoPtr->uView == LV_VIEW_DETAILS)
6286 OffsetRect(lprc, Origin.x, Position.y + Origin.y);
6288 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
6290 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
6297 * Retrieves the spacing between listview control items.
6300 * [I] infoPtr : valid pointer to the listview structure
6301 * [IO] lprc : rectangle to receive the output
6302 * on input, lprc->top = nSubItem
6303 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
6305 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
6306 * not only those of the first column.
6307 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
6313 static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
6319 if (!lprc) return FALSE;
6321 nColumn = lprc->top;
6323 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
6324 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
6326 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
6328 if (infoPtr->uView != LV_VIEW_DETAILS) return FALSE;
6330 /* special case for header items */
6333 if (lprc->left != LVIR_BOUNDS)
6335 FIXME("Only LVIR_BOUNDS is implemented for header, got %d\n", lprc->left);
6339 if (infoPtr->hwndHeader)
6340 return SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, lprc->top, (LPARAM)lprc);
6343 memset(lprc, 0, sizeof(RECT));
6348 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
6350 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6353 lvItem.iItem = nItem;
6354 lvItem.iSubItem = nColumn;
6356 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
6360 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
6365 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
6369 ERR("Unknown bounds=%d\n", lprc->left);
6373 OffsetRect(lprc, 0, Position.y);
6380 * Retrieves the width of a label.
6383 * [I] infoPtr : valid pointer to the listview structure
6386 * SUCCESS : string width (in pixels)
6389 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *infoPtr, INT nItem)
6391 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6394 TRACE("(nItem=%d)\n", nItem);
6396 lvItem.mask = LVIF_TEXT;
6397 lvItem.iItem = nItem;
6398 lvItem.iSubItem = 0;
6399 lvItem.pszText = szDispText;
6400 lvItem.cchTextMax = DISP_TEXT_SIZE;
6401 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6403 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6408 * Retrieves the spacing between listview control items.
6411 * [I] infoPtr : valid pointer to the listview structure
6412 * [I] bSmall : flag for small or large icon
6415 * Horizontal + vertical spacing
6417 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall)
6423 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6427 if (infoPtr->uView == LV_VIEW_ICON)
6428 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6430 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6437 * Retrieves the state of a listview control item.
6440 * [I] infoPtr : valid pointer to the listview structure
6441 * [I] nItem : item index
6442 * [I] uMask : state mask
6445 * State specified by the mask.
6447 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
6451 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6453 lvItem.iItem = nItem;
6454 lvItem.iSubItem = 0;
6455 lvItem.mask = LVIF_STATE;
6456 lvItem.stateMask = uMask;
6457 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6459 return lvItem.state & uMask;
6464 * Retrieves the text of a listview control item or subitem.
6467 * [I] hwnd : window handle
6468 * [I] nItem : item index
6469 * [IO] lpLVItem : item information
6470 * [I] isW : TRUE if lpLVItem is Unicode
6473 * SUCCESS : string length
6476 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6478 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6480 lpLVItem->mask = LVIF_TEXT;
6481 lpLVItem->iItem = nItem;
6482 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
6484 return textlenT(lpLVItem->pszText, isW);
6489 * Searches for an item based on properties + relationships.
6492 * [I] infoPtr : valid pointer to the listview structure
6493 * [I] nItem : item index
6494 * [I] uFlags : relationship flag
6497 * SUCCESS : item index
6500 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
6503 LVFINDINFOW lvFindInfo;
6504 INT nCountPerColumn;
6508 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
6509 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
6511 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6513 if (uFlags & LVNI_CUT)
6516 if (uFlags & LVNI_DROPHILITED)
6517 uMask |= LVIS_DROPHILITED;
6519 if (uFlags & LVNI_FOCUSED)
6520 uMask |= LVIS_FOCUSED;
6522 if (uFlags & LVNI_SELECTED)
6523 uMask |= LVIS_SELECTED;
6525 /* if we're asked for the focused item, that's only one,
6526 * so it's worth optimizing */
6527 if (uFlags & LVNI_FOCUSED)
6529 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
6530 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
6533 if (uFlags & LVNI_ABOVE)
6535 if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
6540 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6546 /* Special case for autoarrange - move 'til the top of a list */
6547 if (is_autoarrange(infoPtr))
6549 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6550 while (nItem - nCountPerRow >= 0)
6552 nItem -= nCountPerRow;
6553 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6558 lvFindInfo.flags = LVFI_NEARESTXY;
6559 lvFindInfo.vkDirection = VK_UP;
6560 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
6561 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
6563 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6568 else if (uFlags & LVNI_BELOW)
6570 if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
6572 while (nItem < infoPtr->nItemCount)
6575 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6581 /* Special case for autoarrange - move 'til the bottom of a list */
6582 if (is_autoarrange(infoPtr))
6584 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6585 while (nItem + nCountPerRow < infoPtr->nItemCount )
6587 nItem += nCountPerRow;
6588 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6593 lvFindInfo.flags = LVFI_NEARESTXY;
6594 lvFindInfo.vkDirection = VK_DOWN;
6595 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
6596 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
6598 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6603 else if (uFlags & LVNI_TOLEFT)
6605 if (infoPtr->uView == LV_VIEW_LIST)
6607 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6608 while (nItem - nCountPerColumn >= 0)
6610 nItem -= nCountPerColumn;
6611 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6615 else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
6617 /* Special case for autoarrange - move 'til the beginning of a row */
6618 if (is_autoarrange(infoPtr))
6620 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6621 while (nItem % nCountPerRow > 0)
6624 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6629 lvFindInfo.flags = LVFI_NEARESTXY;
6630 lvFindInfo.vkDirection = VK_LEFT;
6631 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
6632 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
6634 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6639 else if (uFlags & LVNI_TORIGHT)
6641 if (infoPtr->uView == LV_VIEW_LIST)
6643 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6644 while (nItem + nCountPerColumn < infoPtr->nItemCount)
6646 nItem += nCountPerColumn;
6647 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6651 else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
6653 /* Special case for autoarrange - move 'til the end of a row */
6654 if (is_autoarrange(infoPtr))
6656 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6657 while (nItem % nCountPerRow < nCountPerRow - 1 )
6660 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6665 lvFindInfo.flags = LVFI_NEARESTXY;
6666 lvFindInfo.vkDirection = VK_RIGHT;
6667 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
6668 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
6670 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6679 /* search by index */
6680 for (i = nItem; i < infoPtr->nItemCount; i++)
6682 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
6690 /* LISTVIEW_GetNumberOfWorkAreas */
6694 * Retrieves the origin coordinates when in icon or small icon display mode.
6697 * [I] infoPtr : valid pointer to the listview structure
6698 * [O] lpptOrigin : coordinate information
6703 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
6705 INT nHorzPos = 0, nVertPos = 0;
6706 SCROLLINFO scrollInfo;
6708 scrollInfo.cbSize = sizeof(SCROLLINFO);
6709 scrollInfo.fMask = SIF_POS;
6711 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6712 nHorzPos = scrollInfo.nPos;
6713 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6714 nVertPos = scrollInfo.nPos;
6716 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6718 lpptOrigin->x = infoPtr->rcList.left;
6719 lpptOrigin->y = infoPtr->rcList.top;
6720 if (infoPtr->uView == LV_VIEW_LIST)
6721 nHorzPos *= infoPtr->nItemWidth;
6722 else if (infoPtr->uView == LV_VIEW_DETAILS)
6723 nVertPos *= infoPtr->nItemHeight;
6725 lpptOrigin->x -= nHorzPos;
6726 lpptOrigin->y -= nVertPos;
6728 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
6733 * Retrieves the width of a string.
6736 * [I] hwnd : window handle
6737 * [I] lpszText : text string to process
6738 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6741 * SUCCESS : string width (in pixels)
6744 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6749 if (is_textT(lpszText, isW))
6751 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6752 HDC hdc = GetDC(infoPtr->hwndSelf);
6753 HFONT hOldFont = SelectObject(hdc, hFont);
6756 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6758 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6759 SelectObject(hdc, hOldFont);
6760 ReleaseDC(infoPtr->hwndSelf, hdc);
6762 return stringSize.cx;
6767 * Determines which listview item is located at the specified position.
6770 * [I] infoPtr : valid pointer to the listview structure
6771 * [IO] lpht : hit test information
6772 * [I] subitem : fill out iSubItem.
6773 * [I] select : return the index only if the hit selects the item
6776 * (mm 20001022): We must not allow iSubItem to be touched, for
6777 * an app might pass only a structure with space up to iItem!
6778 * (MS Office 97 does that for instance in the file open dialog)
6781 * SUCCESS : item index
6784 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6786 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6787 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6788 POINT Origin, Position, opt;
6793 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6797 if (subitem) lpht->iSubItem = 0;
6799 if (infoPtr->rcList.left > lpht->pt.x)
6800 lpht->flags |= LVHT_TOLEFT;
6801 else if (infoPtr->rcList.right < lpht->pt.x)
6802 lpht->flags |= LVHT_TORIGHT;
6804 if (infoPtr->rcList.top > lpht->pt.y)
6805 lpht->flags |= LVHT_ABOVE;
6806 else if (infoPtr->rcList.bottom < lpht->pt.y)
6807 lpht->flags |= LVHT_BELOW;
6809 TRACE("lpht->flags=0x%x\n", lpht->flags);
6810 if (lpht->flags) return -1;
6812 lpht->flags |= LVHT_NOWHERE;
6814 LISTVIEW_GetOrigin(infoPtr, &Origin);
6816 /* first deal with the large items */
6817 rcSearch.left = lpht->pt.x;
6818 rcSearch.top = lpht->pt.y;
6819 rcSearch.right = rcSearch.left + 1;
6820 rcSearch.bottom = rcSearch.top + 1;
6822 iterator_frameditems(&i, infoPtr, &rcSearch);
6823 iterator_next(&i); /* go to first item in the sequence */
6825 iterator_destroy(&i);
6827 TRACE("lpht->iItem=%d\n", iItem);
6828 if (iItem == -1) return -1;
6830 if (infoPtr->uView == LV_VIEW_DETAILS && subitem)
6832 RECT bounds, *pRect;
6835 /* for top/bottom only */
6836 bounds.left = LVIR_BOUNDS;
6837 LISTVIEW_GetItemRect(infoPtr, iItem, &bounds);
6838 opt.x = lpht->pt.x - Origin.x;
6841 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6843 pRect = &LISTVIEW_GetColumnInfo(infoPtr, j)->rcHeader;
6844 bounds.left = pRect->left;
6845 bounds.right = pRect->right;
6847 if (PtInRect(&bounds, opt))
6853 TRACE("lpht->iSubItem=%d\n", lpht->iSubItem);
6856 lvItem.mask = LVIF_STATE | LVIF_TEXT;
6857 if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
6858 lvItem.stateMask = LVIS_STATEIMAGEMASK;
6859 if (infoPtr->uView == LV_VIEW_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6860 lvItem.iItem = iItem;
6861 lvItem.iSubItem = subitem ? lpht->iSubItem : 0;
6862 lvItem.pszText = szDispText;
6863 lvItem.cchTextMax = DISP_TEXT_SIZE;
6864 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6865 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6867 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
6868 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6869 opt.x = lpht->pt.x - Position.x - Origin.x;
6870 opt.y = lpht->pt.y - Position.y - Origin.y;
6872 if (infoPtr->uView == LV_VIEW_DETAILS)
6875 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
6876 opt.x = lpht->pt.x - Origin.x;
6880 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6881 UnionRect(&rcBounds, &rcBounds, &rcState);
6883 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6884 if (!PtInRect(&rcBounds, opt)) return -1;
6886 if (PtInRect(&rcIcon, opt))
6887 lpht->flags |= LVHT_ONITEMICON;
6888 else if (PtInRect(&rcLabel, opt))
6889 lpht->flags |= LVHT_ONITEMLABEL;
6890 else if (infoPtr->himlState && PtInRect(&rcState, opt))
6891 lpht->flags |= LVHT_ONITEMSTATEICON;
6892 /* special case for LVS_EX_FULLROWSELECT */
6893 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT &&
6894 !(lpht->flags & LVHT_ONITEM))
6896 lpht->flags = LVHT_ONITEM | LVHT_ABOVE;
6898 if (lpht->flags & LVHT_ONITEM)
6899 lpht->flags &= ~LVHT_NOWHERE;
6900 TRACE("lpht->flags=0x%x\n", lpht->flags);
6902 if (select && !(infoPtr->uView == LV_VIEW_DETAILS &&
6903 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6904 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6906 if (infoPtr->uView == LV_VIEW_DETAILS)
6908 /* get main item bounds */
6909 lvItem.iSubItem = 0;
6910 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
6911 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6912 UnionRect(&rcBounds, &rcBounds, &rcState);
6914 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6916 return lpht->iItem = iItem;
6921 * Inserts a new item in the listview control.
6924 * [I] infoPtr : valid pointer to the listview structure
6925 * [I] lpLVItem : item information
6926 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6929 * SUCCESS : new item index
6932 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6939 BOOL is_sorted, has_changed;
6941 HWND hwndSelf = infoPtr->hwndSelf;
6943 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6945 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6947 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6948 if (!lpLVItem || lpLVItem->iSubItem) return -1;
6950 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6952 if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
6954 /* insert item in listview control data structure */
6955 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6956 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6958 /* link with id struct */
6959 if (!(lpID = Alloc(sizeof(ITEM_ID)))) goto fail;
6961 lpID->item = hdpaSubItems;
6962 lpID->id = get_next_itemid(infoPtr);
6963 if ( DPA_InsertPtr(infoPtr->hdpaItemIds, infoPtr->nItemCount, lpID) == -1) goto fail;
6965 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6966 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6968 if (lpLVItem->iItem < 0 && !is_sorted) return -1;
6970 /* calculate new item index */
6977 while (i < infoPtr->nItemCount)
6979 hItem = DPA_GetPtr( infoPtr->hdpaItems, i);
6980 item_s = (ITEM_INFO*)DPA_GetPtr(hItem, 0);
6982 cmpv = textcmpWT(item_s->hdr.pszText, lpLVItem->pszText, TRUE);
6983 if (infoPtr->dwStyle & LVS_SORTDESCENDING) cmpv *= -1;
6985 if (cmpv >= 0) break;
6991 nItem = min(lpLVItem->iItem, infoPtr->nItemCount);
6993 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6994 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6995 if (nItem == -1) goto fail;
6996 infoPtr->nItemCount++;
6998 /* shift indices first so they don't get tangled */
6999 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
7001 /* set the item attributes */
7002 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
7004 /* full size structure expected - _WIN32IE >= 0x560 */
7007 else if (lpLVItem->mask & LVIF_INDENT)
7009 /* indent member expected - _WIN32IE >= 0x300 */
7010 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
7014 /* minimal structure expected */
7015 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
7018 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7020 item.mask |= LVIF_STATE;
7021 item.stateMask |= LVIS_STATEIMAGEMASK;
7022 item.state &= ~LVIS_STATEIMAGEMASK;
7023 item.state |= INDEXTOSTATEIMAGEMASK(1);
7025 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
7027 /* make room for the position, if we are in the right mode */
7028 if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
7030 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
7032 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
7034 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
7039 /* send LVN_INSERTITEM notification */
7040 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7042 nmlv.lParam = lpItem->lParam;
7043 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
7044 if (!IsWindow(hwndSelf))
7047 /* align items (set position of each item) */
7048 if (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON)
7052 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
7053 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
7055 LISTVIEW_NextIconPosTop(infoPtr, &pt);
7057 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
7060 /* now is the invalidation fun */
7061 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
7065 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
7066 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
7067 infoPtr->nItemCount--;
7069 DPA_DeletePtr(hdpaSubItems, 0);
7070 DPA_Destroy (hdpaSubItems);
7077 * Checks item visibility.
7080 * [I] infoPtr : valid pointer to the listview structure
7081 * [I] nFirst : item index to check for
7084 * Item visible : TRUE
7085 * Item invisible or failure : FALSE
7087 static BOOL LISTVIEW_IsItemVisible(const LISTVIEW_INFO *infoPtr, INT nItem)
7089 POINT Origin, Position;
7094 TRACE("nItem=%d\n", nItem);
7096 if (nItem < 0 || nItem >= DPA_GetPtrCount(infoPtr->hdpaItems)) return FALSE;
7098 LISTVIEW_GetOrigin(infoPtr, &Origin);
7099 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
7100 rcItem.left = Position.x + Origin.x;
7101 rcItem.top = Position.y + Origin.y;
7102 rcItem.right = rcItem.left + infoPtr->nItemWidth;
7103 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
7105 hdc = GetDC(infoPtr->hwndSelf);
7106 if (!hdc) return FALSE;
7107 ret = RectVisible(hdc, &rcItem);
7108 ReleaseDC(infoPtr->hwndSelf, hdc);
7115 * Redraws a range of items.
7118 * [I] infoPtr : valid pointer to the listview structure
7119 * [I] nFirst : first item
7120 * [I] nLast : last item
7126 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
7130 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
7131 max(nFirst, nLast) >= infoPtr->nItemCount)
7134 for (i = nFirst; i <= nLast; i++)
7135 LISTVIEW_InvalidateItem(infoPtr, i);
7142 * Scroll the content of a listview.
7145 * [I] infoPtr : valid pointer to the listview structure
7146 * [I] dx : horizontal scroll amount in pixels
7147 * [I] dy : vertical scroll amount in pixels
7154 * If the control is in report view (LV_VIEW_DETAILS) the control can
7155 * be scrolled only in line increments. "dy" will be rounded to the
7156 * nearest number of pixels that are a whole line. Ex: if line height
7157 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
7158 * is passed, then the scroll will be 0. (per MSDN 7/2002)
7160 * For: (per experimentation with native control and CSpy ListView)
7161 * LV_VIEW_ICON dy=1 = 1 pixel (vertical only)
7163 * LV_VIEW_SMALLICON dy=1 = 1 pixel (vertical only)
7165 * LV_VIEW_LIST dx=1 = 1 column (horizontal only)
7166 * but will only scroll 1 column per message
7167 * no matter what the value.
7168 * dy must be 0 or FALSE returned.
7169 * LV_VIEW_DETAILS dx=1 = 1 pixel
7173 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7175 switch(infoPtr->uView) {
7176 case LV_VIEW_DETAILS:
7177 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
7178 dy /= infoPtr->nItemHeight;
7181 if (dy != 0) return FALSE;
7188 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
7189 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
7196 * Sets the background color.
7199 * [I] infoPtr : valid pointer to the listview structure
7200 * [I] clrBk : background color
7206 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
7208 TRACE("(clrBk=%x)\n", clrBk);
7210 if(infoPtr->clrBk != clrBk) {
7211 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7212 infoPtr->clrBk = clrBk;
7213 if (clrBk == CLR_NONE)
7214 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
7216 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
7217 LISTVIEW_InvalidateList(infoPtr);
7223 /* LISTVIEW_SetBkImage */
7225 /*** Helper for {Insert,Set}ColumnT *only* */
7226 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
7227 const LVCOLUMNW *lpColumn, BOOL isW)
7229 if (lpColumn->mask & LVCF_FMT)
7231 /* format member is valid */
7232 lphdi->mask |= HDI_FORMAT;
7234 /* set text alignment (leftmost column must be left-aligned) */
7235 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
7236 lphdi->fmt |= HDF_LEFT;
7237 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
7238 lphdi->fmt |= HDF_RIGHT;
7239 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
7240 lphdi->fmt |= HDF_CENTER;
7242 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
7243 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
7245 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
7247 lphdi->fmt |= HDF_IMAGE;
7248 lphdi->iImage = I_IMAGECALLBACK;
7252 if (lpColumn->mask & LVCF_WIDTH)
7254 lphdi->mask |= HDI_WIDTH;
7255 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
7257 /* make it fill the remainder of the controls width */
7261 for(item_index = 0; item_index < (nColumn - 1); item_index++)
7263 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
7264 lphdi->cxy += rcHeader.right - rcHeader.left;
7267 /* retrieve the layout of the header */
7268 GetClientRect(infoPtr->hwndSelf, &rcHeader);
7269 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
7271 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
7274 lphdi->cxy = lpColumn->cx;
7277 if (lpColumn->mask & LVCF_TEXT)
7279 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
7280 lphdi->fmt |= HDF_STRING;
7281 lphdi->pszText = lpColumn->pszText;
7282 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
7285 if (lpColumn->mask & LVCF_IMAGE)
7287 lphdi->mask |= HDI_IMAGE;
7288 lphdi->iImage = lpColumn->iImage;
7291 if (lpColumn->mask & LVCF_ORDER)
7293 lphdi->mask |= HDI_ORDER;
7294 lphdi->iOrder = lpColumn->iOrder;
7301 * Inserts a new column.
7304 * [I] infoPtr : valid pointer to the listview structure
7305 * [I] nColumn : column index
7306 * [I] lpColumn : column information
7307 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
7310 * SUCCESS : new column index
7313 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
7314 const LVCOLUMNW *lpColumn, BOOL isW)
7316 COLUMN_INFO *lpColumnInfo;
7320 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
7322 if (!lpColumn || nColumn < 0) return -1;
7323 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
7325 ZeroMemory(&hdi, sizeof(HDITEMW));
7326 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
7329 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
7330 * (can be seen in SPY) otherwise column never gets added.
7332 if (!(lpColumn->mask & LVCF_WIDTH)) {
7333 hdi.mask |= HDI_WIDTH;
7338 * when the iSubItem is available Windows copies it to the header lParam. It seems
7339 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
7341 if (lpColumn->mask & LVCF_SUBITEM)
7343 hdi.mask |= HDI_LPARAM;
7344 hdi.lParam = lpColumn->iSubItem;
7347 /* create header if not present */
7348 LISTVIEW_CreateHeader(infoPtr);
7349 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle) &&
7350 (infoPtr->uView == LV_VIEW_DETAILS) && (WS_VISIBLE & infoPtr->dwStyle))
7352 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7355 /* insert item in header control */
7356 nNewColumn = SendMessageW(infoPtr->hwndHeader,
7357 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
7358 (WPARAM)nColumn, (LPARAM)&hdi);
7359 if (nNewColumn == -1) return -1;
7360 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
7362 /* create our own column info */
7363 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
7364 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
7366 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
7367 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, nNewColumn, (LPARAM)&lpColumnInfo->rcHeader))
7370 /* now we have to actually adjust the data */
7371 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
7373 SUBITEM_INFO *lpSubItem;
7377 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
7379 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
7380 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
7382 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
7383 if (lpSubItem->iSubItem >= nNewColumn)
7384 lpSubItem->iSubItem++;
7389 /* make space for the new column */
7390 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7391 LISTVIEW_UpdateItemSize(infoPtr);
7396 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
7399 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
7407 * Sets the attributes of a header item.
7410 * [I] infoPtr : valid pointer to the listview structure
7411 * [I] nColumn : column index
7412 * [I] lpColumn : column attributes
7413 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
7419 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
7420 const LVCOLUMNW *lpColumn, BOOL isW)
7422 HDITEMW hdi, hdiget;
7425 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
7427 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7429 ZeroMemory(&hdi, sizeof(HDITEMW));
7430 if (lpColumn->mask & LVCF_FMT)
7432 hdi.mask |= HDI_FORMAT;
7433 hdiget.mask = HDI_FORMAT;
7434 if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdiget))
7435 hdi.fmt = hdiget.fmt & HDF_STRING;
7437 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
7439 /* set header item attributes */
7440 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
7441 if (!bResult) return FALSE;
7443 if (lpColumn->mask & LVCF_FMT)
7445 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
7446 int oldFmt = lpColumnInfo->fmt;
7448 lpColumnInfo->fmt = lpColumn->fmt;
7449 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
7451 if (infoPtr->uView == LV_VIEW_DETAILS) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
7460 * Sets the column order array
7463 * [I] infoPtr : valid pointer to the listview structure
7464 * [I] iCount : number of elements in column order array
7465 * [I] lpiArray : pointer to column order array
7471 static BOOL LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
7473 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7484 * Sets the width of a column
7487 * [I] infoPtr : valid pointer to the listview structure
7488 * [I] nColumn : column index
7489 * [I] cx : column width
7495 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
7497 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
7501 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
7503 /* set column width only if in report or list mode */
7504 if (infoPtr->uView != LV_VIEW_DETAILS && infoPtr->uView != LV_VIEW_LIST) return FALSE;
7506 /* take care of invalid cx values */
7507 if(infoPtr->uView == LV_VIEW_DETAILS && cx < -2) cx = LVSCW_AUTOSIZE;
7508 else if (infoPtr->uView == LV_VIEW_LIST && cx < 1) return FALSE;
7510 /* resize all columns if in LV_VIEW_LIST mode */
7511 if(infoPtr->uView == LV_VIEW_LIST)
7513 infoPtr->nItemWidth = cx;
7514 LISTVIEW_InvalidateList(infoPtr);
7518 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7520 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
7525 lvItem.mask = LVIF_TEXT;
7527 lvItem.iSubItem = nColumn;
7528 lvItem.pszText = szDispText;
7529 lvItem.cchTextMax = DISP_TEXT_SIZE;
7530 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7532 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
7533 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
7534 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
7536 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
7537 max_cx += infoPtr->iconSize.cx;
7538 max_cx += TRAILING_LABEL_PADDING;
7541 /* autosize based on listview items width */
7542 if(cx == LVSCW_AUTOSIZE)
7544 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7546 /* if iCol is the last column make it fill the remainder of the controls width */
7547 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
7552 LISTVIEW_GetOrigin(infoPtr, &Origin);
7553 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
7555 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
7559 /* Despite what the MS docs say, if this is not the last
7560 column, then MS resizes the column to the width of the
7561 largest text string in the column, including headers
7562 and items. This is different from LVSCW_AUTOSIZE in that
7563 LVSCW_AUTOSIZE ignores the header string length. */
7566 /* retrieve header text */
7567 hdi.mask = HDI_TEXT;
7568 hdi.cchTextMax = DISP_TEXT_SIZE;
7569 hdi.pszText = szDispText;
7570 if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdi))
7572 HDC hdc = GetDC(infoPtr->hwndSelf);
7573 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
7576 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
7577 cx = size.cx + TRAILING_HEADER_PADDING;
7578 /* FIXME: Take into account the header image, if one is present */
7579 SelectObject(hdc, old_font);
7580 ReleaseDC(infoPtr->hwndSelf, hdc);
7582 cx = max (cx, max_cx);
7586 if (cx < 0) return FALSE;
7588 /* call header to update the column change */
7589 hdi.mask = HDI_WIDTH;
7591 TRACE("hdi.cxy=%d\n", hdi.cxy);
7592 return SendMessageW(infoPtr->hwndHeader, HDM_SETITEMW, nColumn, (LPARAM)&hdi);
7596 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
7599 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
7602 HBITMAP hbm_im, hbm_mask, hbm_orig;
7604 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
7605 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
7608 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
7609 ILC_COLOR | ILC_MASK, 2, 2);
7610 hdc_wnd = GetDC(infoPtr->hwndSelf);
7611 hdc = CreateCompatibleDC(hdc_wnd);
7612 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
7613 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
7614 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
7616 rc.left = rc.top = 0;
7617 rc.right = GetSystemMetrics(SM_CXSMICON);
7618 rc.bottom = GetSystemMetrics(SM_CYSMICON);
7620 hbm_orig = SelectObject(hdc, hbm_mask);
7621 FillRect(hdc, &rc, hbr_white);
7622 InflateRect(&rc, -2, -2);
7623 FillRect(hdc, &rc, hbr_black);
7625 SelectObject(hdc, hbm_im);
7626 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
7627 SelectObject(hdc, hbm_orig);
7628 ImageList_Add(himl, hbm_im, hbm_mask);
7630 SelectObject(hdc, hbm_im);
7631 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
7632 SelectObject(hdc, hbm_orig);
7633 ImageList_Add(himl, hbm_im, hbm_mask);
7635 DeleteObject(hbm_mask);
7636 DeleteObject(hbm_im);
7644 * Sets the extended listview style.
7647 * [I] infoPtr : valid pointer to the listview structure
7649 * [I] dwStyle : style
7652 * SUCCESS : previous style
7655 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwExStyle)
7657 DWORD dwOldExStyle = infoPtr->dwLvExStyle;
7661 infoPtr->dwLvExStyle = (dwOldExStyle & ~dwMask) | (dwExStyle & dwMask);
7663 infoPtr->dwLvExStyle = dwExStyle;
7665 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_CHECKBOXES)
7667 HIMAGELIST himl = 0;
7668 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7671 item.mask = LVIF_STATE;
7672 item.stateMask = LVIS_STATEIMAGEMASK;
7673 item.state = INDEXTOSTATEIMAGEMASK(1);
7674 LISTVIEW_SetItemState(infoPtr, -1, &item);
7676 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
7678 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
7681 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_HEADERDRAGDROP)
7685 /* if not already created */
7686 LISTVIEW_CreateHeader(infoPtr);
7688 dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
7689 if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
7690 dwStyle |= HDS_DRAGDROP;
7692 dwStyle &= ~HDS_DRAGDROP;
7693 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
7696 /* GRIDLINES adds decoration at top so changes sizes */
7697 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_GRIDLINES)
7699 LISTVIEW_UpdateSize(infoPtr);
7703 LISTVIEW_InvalidateList(infoPtr);
7704 return dwOldExStyle;
7709 * Sets the new hot cursor used during hot tracking and hover selection.
7712 * [I] infoPtr : valid pointer to the listview structure
7713 * [I] hCursor : the new hot cursor handle
7716 * Returns the previous hot cursor
7718 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
7720 HCURSOR oldCursor = infoPtr->hHotCursor;
7722 infoPtr->hHotCursor = hCursor;
7730 * Sets the hot item index.
7733 * [I] infoPtr : valid pointer to the listview structure
7734 * [I] iIndex : index
7737 * SUCCESS : previous hot item index
7738 * FAILURE : -1 (no hot item)
7740 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
7742 INT iOldIndex = infoPtr->nHotItem;
7744 infoPtr->nHotItem = iIndex;
7752 * Sets the amount of time the cursor must hover over an item before it is selected.
7755 * [I] infoPtr : valid pointer to the listview structure
7756 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7759 * Returns the previous hover time
7761 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
7763 DWORD oldHoverTime = infoPtr->dwHoverTime;
7765 infoPtr->dwHoverTime = dwHoverTime;
7767 return oldHoverTime;
7772 * Sets spacing for icons of LVS_ICON style.
7775 * [I] infoPtr : valid pointer to the listview structure
7776 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7777 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7780 * MAKELONG(oldcx, oldcy)
7782 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
7784 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7786 TRACE("requested=(%d,%d)\n", cx, cy);
7788 /* this is supported only for LVS_ICON style */
7789 if (infoPtr->uView != LV_VIEW_ICON) return oldspacing;
7791 /* set to defaults, if instructed to */
7792 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
7793 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
7795 /* if 0 then compute width
7796 * FIXME: Should scan each item and determine max width of
7797 * icon or label, then make that the width */
7799 cx = infoPtr->iconSpacing.cx;
7801 /* if 0 then compute height */
7803 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
7804 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
7807 infoPtr->iconSpacing.cx = cx;
7808 infoPtr->iconSpacing.cy = cy;
7810 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7811 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
7812 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7813 infoPtr->ntmHeight);
7815 /* these depend on the iconSpacing */
7816 LISTVIEW_UpdateItemSize(infoPtr);
7821 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
7825 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
7832 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
7833 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7842 * [I] infoPtr : valid pointer to the listview structure
7843 * [I] nType : image list type
7844 * [I] himl : image list handle
7847 * SUCCESS : old image list
7850 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7852 INT oldHeight = infoPtr->nItemHeight;
7853 HIMAGELIST himlOld = 0;
7855 TRACE("(nType=%d, himl=%p\n", nType, himl);
7860 himlOld = infoPtr->himlNormal;
7861 infoPtr->himlNormal = himl;
7862 if (infoPtr->uView == LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7863 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7867 himlOld = infoPtr->himlSmall;
7868 infoPtr->himlSmall = himl;
7869 if (infoPtr->uView != LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7873 himlOld = infoPtr->himlState;
7874 infoPtr->himlState = himl;
7875 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7876 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7880 ERR("Unknown icon type=%d\n", nType);
7884 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7885 if (infoPtr->nItemHeight != oldHeight)
7886 LISTVIEW_UpdateScroll(infoPtr);
7893 * Preallocates memory (does *not* set the actual count of items !)
7896 * [I] infoPtr : valid pointer to the listview structure
7897 * [I] nItems : item count (projected number of items to allocate)
7898 * [I] dwFlags : update flags
7904 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7906 TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
7908 if (infoPtr->dwStyle & LVS_OWNERDATA)
7910 INT nOldCount = infoPtr->nItemCount;
7912 if (nItems < nOldCount)
7914 RANGE range = { nItems, nOldCount };
7915 ranges_del(infoPtr->selectionRanges, range);
7916 if (infoPtr->nFocusedItem >= nItems)
7918 LISTVIEW_SetItemFocus(infoPtr, -1);
7919 SetRectEmpty(&infoPtr->rcFocus);
7923 infoPtr->nItemCount = nItems;
7924 LISTVIEW_UpdateScroll(infoPtr);
7926 /* the flags are valid only in ownerdata report and list modes */
7927 if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON) dwFlags = 0;
7929 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7930 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7932 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7933 LISTVIEW_InvalidateList(infoPtr);
7940 LISTVIEW_GetOrigin(infoPtr, &Origin);
7941 nFrom = min(nOldCount, nItems);
7942 nTo = max(nOldCount, nItems);
7944 if (infoPtr->uView == LV_VIEW_DETAILS)
7947 rcErase.top = nFrom * infoPtr->nItemHeight;
7948 rcErase.right = infoPtr->nItemWidth;
7949 rcErase.bottom = nTo * infoPtr->nItemHeight;
7950 OffsetRect(&rcErase, Origin.x, Origin.y);
7951 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7952 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7954 else /* LV_VIEW_LIST */
7956 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7958 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7959 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7960 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7961 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7962 OffsetRect(&rcErase, Origin.x, Origin.y);
7963 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7964 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7966 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7968 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7969 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7970 OffsetRect(&rcErase, Origin.x, Origin.y);
7971 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7972 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7978 /* According to MSDN for non-LVS_OWNERDATA this is just
7979 * a performance issue. The control allocates its internal
7980 * data structures for the number of items specified. It
7981 * cuts down on the number of memory allocations. Therefore
7982 * we will just issue a WARN here
7984 WARN("for non-ownerdata performance option not implemented.\n");
7992 * Sets the position of an item.
7995 * [I] infoPtr : valid pointer to the listview structure
7996 * [I] nItem : item index
7997 * [I] pt : coordinate
8003 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
8007 TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
8009 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
8010 !(infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)) return FALSE;
8012 LISTVIEW_GetOrigin(infoPtr, &Origin);
8014 /* This point value seems to be an undocumented feature.
8015 * The best guess is that it means either at the origin,
8016 * or at true beginning of the list. I will assume the origin. */
8017 if ((pt.x == -1) && (pt.y == -1))
8020 if (infoPtr->uView == LV_VIEW_ICON)
8022 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
8023 pt.y -= ICON_TOP_PADDING;
8028 infoPtr->bAutoarrange = FALSE;
8030 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
8035 * Sets the state of one or many items.
8038 * [I] infoPtr : valid pointer to the listview structure
8039 * [I] nItem : item index
8040 * [I] lpLVItem : item or subitem info
8046 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
8048 BOOL bResult = TRUE;
8051 lvItem.iItem = nItem;
8052 lvItem.iSubItem = 0;
8053 lvItem.mask = LVIF_STATE;
8054 lvItem.state = lpLVItem->state;
8055 lvItem.stateMask = lpLVItem->stateMask;
8056 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
8060 /* select all isn't allowed in LVS_SINGLESEL */
8061 if ((lvItem.state & lvItem.stateMask & LVIS_SELECTED) && (infoPtr->dwStyle & LVS_SINGLESEL))
8064 /* apply to all items */
8065 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
8066 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
8069 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
8072 * Update selection mark
8074 * Investigation on windows 2k showed that selection mark was updated
8075 * whenever a new selection was made, but if the selected item was
8076 * unselected it was not updated.
8078 * we are probably still not 100% accurate, but this at least sets the
8079 * proper selection mark when it is needed
8082 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
8083 (infoPtr->nSelectionMark == -1))
8086 for (i = 0; i < infoPtr->nItemCount; i++)
8088 if (infoPtr->uCallbackMask & LVIS_SELECTED)
8090 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
8092 infoPtr->nSelectionMark = i;
8096 else if (ranges_contain(infoPtr->selectionRanges, i))
8098 infoPtr->nSelectionMark = i;
8109 * Sets the text of an item or subitem.
8112 * [I] hwnd : window handle
8113 * [I] nItem : item index
8114 * [I] lpLVItem : item or subitem info
8115 * [I] isW : TRUE if input is Unicode
8121 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
8125 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
8127 lvItem.iItem = nItem;
8128 lvItem.iSubItem = lpLVItem->iSubItem;
8129 lvItem.mask = LVIF_TEXT;
8130 lvItem.pszText = lpLVItem->pszText;
8131 lvItem.cchTextMax = lpLVItem->cchTextMax;
8133 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
8135 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
8140 * Set item index that marks the start of a multiple selection.
8143 * [I] infoPtr : valid pointer to the listview structure
8144 * [I] nIndex : index
8147 * Index number or -1 if there is no selection mark.
8149 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
8151 INT nOldIndex = infoPtr->nSelectionMark;
8153 TRACE("(nIndex=%d)\n", nIndex);
8155 infoPtr->nSelectionMark = nIndex;
8162 * Sets the text background color.
8165 * [I] infoPtr : valid pointer to the listview structure
8166 * [I] clrTextBk : text background color
8172 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
8174 TRACE("(clrTextBk=%x)\n", clrTextBk);
8176 if (infoPtr->clrTextBk != clrTextBk)
8178 infoPtr->clrTextBk = clrTextBk;
8179 LISTVIEW_InvalidateList(infoPtr);
8187 * Sets the text foreground color.
8190 * [I] infoPtr : valid pointer to the listview structure
8191 * [I] clrText : text color
8197 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
8199 TRACE("(clrText=%x)\n", clrText);
8201 if (infoPtr->clrText != clrText)
8203 infoPtr->clrText = clrText;
8204 LISTVIEW_InvalidateList(infoPtr);
8212 * Sets new ToolTip window to ListView control.
8215 * [I] infoPtr : valid pointer to the listview structure
8216 * [I] hwndNewToolTip : handle to new ToolTip
8221 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
8223 HWND hwndOldToolTip = infoPtr->hwndToolTip;
8224 infoPtr->hwndToolTip = hwndNewToolTip;
8225 return hwndOldToolTip;
8230 * sets the Unicode character format flag for the control
8232 * [I] infoPtr :valid pointer to the listview structure
8233 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
8236 * Old Unicode Format
8238 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL fUnicode)
8240 SHORT rc = infoPtr->notifyFormat;
8241 infoPtr->notifyFormat = (fUnicode) ? NFR_UNICODE : NFR_ANSI;
8242 return rc == NFR_UNICODE;
8247 * sets the control view mode
8249 * [I] infoPtr :valid pointer to the listview structure
8250 * [I] nView :new view mode value
8256 static INT LISTVIEW_SetView(LISTVIEW_INFO *infoPtr, DWORD nView)
8258 SIZE oldIconSize = infoPtr->iconSize;
8261 if (infoPtr->uView == nView) return 1;
8263 if ((INT)nView < 0 || nView > LV_VIEW_MAX) return -1;
8264 if (nView == LV_VIEW_TILE)
8266 FIXME("View LV_VIEW_TILE unimplemented\n");
8270 infoPtr->uView = nView;
8272 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8273 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8275 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8276 SetRectEmpty(&infoPtr->rcFocus);
8278 himl = (nView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8279 set_icon_size(&infoPtr->iconSize, himl, nView != LV_VIEW_ICON);
8284 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8286 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
8287 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8288 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8290 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8292 case LV_VIEW_SMALLICON:
8293 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8295 case LV_VIEW_DETAILS:
8300 LISTVIEW_CreateHeader( infoPtr );
8302 hl.prc = &infoPtr->rcList;
8304 SendMessageW(infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl);
8305 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
8306 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
8313 LISTVIEW_UpdateItemSize(infoPtr);
8314 LISTVIEW_UpdateSize(infoPtr);
8315 LISTVIEW_UpdateScroll(infoPtr);
8316 LISTVIEW_InvalidateList(infoPtr);
8318 TRACE("nView=%d\n", nView);
8323 /* LISTVIEW_SetWorkAreas */
8327 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMS
8330 * [I] first : pointer to first ITEM_INFO to compare
8331 * [I] second : pointer to second ITEM_INFO to compare
8332 * [I] lParam : HWND of control
8335 * if first comes before second : negative
8336 * if first comes after second : positive
8337 * if first and second are equivalent : zero
8339 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
8341 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
8342 ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
8343 ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
8345 /* Forward the call to the client defined callback */
8346 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
8351 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMSEX
8354 * [I] first : pointer to first ITEM_INFO to compare
8355 * [I] second : pointer to second ITEM_INFO to compare
8356 * [I] lParam : HWND of control
8359 * if first comes before second : negative
8360 * if first comes after second : positive
8361 * if first and second are equivalent : zero
8363 static INT WINAPI LISTVIEW_CallBackCompareEx(LPVOID first, LPVOID second, LPARAM lParam)
8365 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
8366 INT first_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, first );
8367 INT second_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, second );
8369 /* Forward the call to the client defined callback */
8370 return (infoPtr->pfnCompare)( first_idx, second_idx, infoPtr->lParamSort );
8375 * Sorts the listview items.
8378 * [I] infoPtr : valid pointer to the listview structure
8379 * [I] pfnCompare : application-defined value
8380 * [I] lParamSort : pointer to comparison callback
8381 * [I] IsEx : TRUE when LVM_SORTITEMSEX used
8387 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare,
8388 LPARAM lParamSort, BOOL IsEx)
8392 LPVOID selectionMarkItem = NULL;
8393 LPVOID focusedItem = NULL;
8396 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
8398 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
8400 if (!pfnCompare) return FALSE;
8401 if (!infoPtr->hdpaItems) return FALSE;
8403 /* if there are 0 or 1 items, there is no need to sort */
8404 if (infoPtr->nItemCount < 2) return TRUE;
8406 /* clear selection */
8407 ranges_clear(infoPtr->selectionRanges);
8409 /* save selection mark and focused item */
8410 if (infoPtr->nSelectionMark >= 0)
8411 selectionMarkItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark);
8412 if (infoPtr->nFocusedItem >= 0)
8413 focusedItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
8415 infoPtr->pfnCompare = pfnCompare;
8416 infoPtr->lParamSort = lParamSort;
8418 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompareEx, (LPARAM)infoPtr);
8420 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
8422 /* restore selection ranges */
8423 for (i=0; i < infoPtr->nItemCount; i++)
8425 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
8426 lpItem = DPA_GetPtr(hdpaSubItems, 0);
8428 if (lpItem->state & LVIS_SELECTED)
8429 ranges_additem(infoPtr->selectionRanges, i);
8431 /* restore selection mark and focused item */
8432 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
8433 infoPtr->nFocusedItem = DPA_GetPtrIndex(infoPtr->hdpaItems, focusedItem);
8435 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
8437 /* refresh the display */
8438 if (infoPtr->uView != LV_VIEW_ICON && infoPtr->uView != LV_VIEW_SMALLICON)
8439 LISTVIEW_InvalidateList(infoPtr);
8446 * Update theme handle after a theme change.
8449 * [I] infoPtr : valid pointer to the listview structure
8453 * FAILURE : something else
8455 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr)
8457 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8458 CloseThemeData(theme);
8459 OpenThemeData(infoPtr->hwndSelf, themeClass);
8465 * Updates an items or rearranges the listview control.
8468 * [I] infoPtr : valid pointer to the listview structure
8469 * [I] nItem : item index
8475 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
8477 TRACE("(nItem=%d)\n", nItem);
8479 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
8481 /* rearrange with default alignment style */
8482 if (is_autoarrange(infoPtr))
8483 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8485 LISTVIEW_InvalidateItem(infoPtr, nItem);
8492 * Draw the track line at the place defined in the infoPtr structure.
8493 * The line is drawn with a XOR pen so drawing the line for the second time
8494 * in the same place erases the line.
8497 * [I] infoPtr : valid pointer to the listview structure
8503 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
8509 if (infoPtr->xTrackLine == -1)
8512 if (!(hdc = GetDC(infoPtr->hwndSelf)))
8514 hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
8515 oldROP = SetROP2(hdc, R2_XORPEN);
8516 MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
8517 LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
8518 SetROP2(hdc, oldROP);
8519 SelectObject(hdc, hOldPen);
8520 ReleaseDC(infoPtr->hwndSelf, hdc);
8526 * Called when an edit control should be displayed. This function is called after
8527 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
8530 * [I] hwnd : Handle to the listview
8531 * [I] uMsg : WM_TIMER (ignored)
8532 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
8533 * [I] dwTimer : The elapsed time (ignored)
8538 static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
8540 DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
8541 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8543 KillTimer(hwnd, idEvent);
8544 editItem->fEnabled = FALSE;
8545 /* check if the item is still selected */
8546 if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
8547 LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
8552 * Creates the listview control - the WM_NCCREATE phase.
8555 * [I] hwnd : window handle
8556 * [I] lpcs : the create parameters
8562 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
8564 LISTVIEW_INFO *infoPtr;
8567 TRACE("(lpcs=%p)\n", lpcs);
8569 /* initialize info pointer */
8570 infoPtr = Alloc(sizeof(LISTVIEW_INFO));
8571 if (!infoPtr) return FALSE;
8573 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
8575 infoPtr->hwndSelf = hwnd;
8576 infoPtr->dwStyle = lpcs->style; /* Note: may be changed in WM_CREATE */
8577 map_style_view(infoPtr);
8578 /* determine the type of structures to use */
8579 infoPtr->hwndNotify = lpcs->hwndParent;
8580 /* infoPtr->notifyFormat will be filled in WM_CREATE */
8582 /* initialize color information */
8583 infoPtr->clrBk = CLR_NONE;
8584 infoPtr->clrText = CLR_DEFAULT;
8585 infoPtr->clrTextBk = CLR_DEFAULT;
8586 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
8588 /* set default values */
8589 infoPtr->nFocusedItem = -1;
8590 infoPtr->nSelectionMark = -1;
8591 infoPtr->nHotItem = -1;
8592 infoPtr->bRedraw = TRUE;
8593 infoPtr->bNoItemMetrics = TRUE;
8594 infoPtr->bDoChangeNotify = TRUE;
8595 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
8596 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
8597 infoPtr->nEditLabelItem = -1;
8598 infoPtr->nLButtonDownItem = -1;
8599 infoPtr->dwHoverTime = -1; /* default system hover time */
8600 infoPtr->nMeasureItemHeight = 0;
8601 infoPtr->xTrackLine = -1; /* no track line */
8602 infoPtr->itemEdit.fEnabled = FALSE;
8603 infoPtr->iVersion = COMCTL32_VERSION;
8605 /* get default font (icon title) */
8606 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
8607 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
8608 infoPtr->hFont = infoPtr->hDefaultFont;
8609 LISTVIEW_SaveTextMetrics(infoPtr);
8611 /* allocate memory for the data structure */
8612 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
8613 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
8614 if (!(infoPtr->hdpaItemIds = DPA_Create(10))) goto fail;
8615 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
8616 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
8617 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
8621 DestroyWindow(infoPtr->hwndHeader);
8622 ranges_destroy(infoPtr->selectionRanges);
8623 DPA_Destroy(infoPtr->hdpaItems);
8624 DPA_Destroy(infoPtr->hdpaItemIds);
8625 DPA_Destroy(infoPtr->hdpaPosX);
8626 DPA_Destroy(infoPtr->hdpaPosY);
8627 DPA_Destroy(infoPtr->hdpaColumns);
8634 * Creates the listview control - the WM_CREATE phase. Most of the data is
8635 * already set up in LISTVIEW_NCCreate
8638 * [I] hwnd : window handle
8639 * [I] lpcs : the create parameters
8645 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
8647 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8649 TRACE("(lpcs=%p)\n", lpcs);
8651 infoPtr->dwStyle = lpcs->style;
8652 map_style_view(infoPtr);
8654 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
8655 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
8656 /* on error defaulting to ANSI notifications */
8657 if (infoPtr->notifyFormat == 0) infoPtr->notifyFormat = NFR_ANSI;
8659 if ((infoPtr->uView == LV_VIEW_DETAILS) && (lpcs->style & WS_VISIBLE))
8661 if (LISTVIEW_CreateHeader(infoPtr) < 0) return -1;
8664 infoPtr->hwndHeader = 0;
8666 /* init item size to avoid division by 0 */
8667 LISTVIEW_UpdateItemSize (infoPtr);
8669 if (infoPtr->uView == LV_VIEW_DETAILS)
8671 if (!(LVS_NOCOLUMNHEADER & lpcs->style) && (WS_VISIBLE & lpcs->style))
8673 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8675 LISTVIEW_UpdateScroll(infoPtr);
8676 /* send WM_MEASUREITEM notification */
8677 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED) notify_measureitem(infoPtr);
8680 OpenThemeData(hwnd, themeClass);
8682 /* initialize the icon sizes */
8683 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, infoPtr->uView != LV_VIEW_ICON);
8684 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
8690 * Destroys the listview control.
8693 * [I] infoPtr : valid pointer to the listview structure
8699 static LRESULT LISTVIEW_Destroy(const LISTVIEW_INFO *infoPtr)
8701 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8702 CloseThemeData(theme);
8708 * Enables the listview control.
8711 * [I] infoPtr : valid pointer to the listview structure
8712 * [I] bEnable : specifies whether to enable or disable the window
8718 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr, BOOL bEnable)
8720 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
8721 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8727 * Erases the background of the listview control.
8730 * [I] infoPtr : valid pointer to the listview structure
8731 * [I] hdc : device context handle
8737 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
8741 TRACE("(hdc=%p)\n", hdc);
8743 if (!GetClipBox(hdc, &rc)) return FALSE;
8745 if (infoPtr->clrBk == CLR_NONE)
8746 return SendMessageW(infoPtr->hwndNotify, WM_ERASEBKGND, (WPARAM)hdc, 0);
8748 /* for double buffered controls we need to do this during refresh */
8749 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
8751 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
8757 * Helper function for LISTVIEW_[HV]Scroll *only*.
8758 * Performs vertical/horizontal scrolling by a give amount.
8761 * [I] infoPtr : valid pointer to the listview structure
8762 * [I] dx : amount of horizontal scroll
8763 * [I] dy : amount of vertical scroll
8765 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
8767 /* now we can scroll the list */
8768 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
8769 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
8770 /* if we have focus, adjust rect */
8771 OffsetRect(&infoPtr->rcFocus, dx, dy);
8772 UpdateWindow(infoPtr->hwndSelf);
8777 * Performs vertical scrolling.
8780 * [I] infoPtr : valid pointer to the listview structure
8781 * [I] nScrollCode : scroll code
8782 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8783 * [I] hScrollWnd : scrollbar control window handle
8789 * SB_LINEUP/SB_LINEDOWN:
8790 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8791 * for LVS_REPORT is 1 line
8792 * for LVS_LIST cannot occur
8795 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8796 INT nScrollDiff, HWND hScrollWnd)
8798 INT nOldScrollPos, nNewScrollPos;
8799 SCROLLINFO scrollInfo;
8802 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8803 debugscrollcode(nScrollCode), nScrollDiff);
8805 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8807 scrollInfo.cbSize = sizeof(SCROLLINFO);
8808 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8810 is_an_icon = ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON));
8812 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
8814 nOldScrollPos = scrollInfo.nPos;
8815 switch (nScrollCode)
8821 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
8825 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
8829 nScrollDiff = -scrollInfo.nPage;
8833 nScrollDiff = scrollInfo.nPage;
8836 case SB_THUMBPOSITION:
8838 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8845 /* quit right away if pos isn't changing */
8846 if (nScrollDiff == 0) return 0;
8848 /* calculate new position, and handle overflows */
8849 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8850 if (nScrollDiff > 0) {
8851 if (nNewScrollPos < nOldScrollPos ||
8852 nNewScrollPos > scrollInfo.nMax)
8853 nNewScrollPos = scrollInfo.nMax;
8855 if (nNewScrollPos > nOldScrollPos ||
8856 nNewScrollPos < scrollInfo.nMin)
8857 nNewScrollPos = scrollInfo.nMin;
8860 /* set the new position, and reread in case it changed */
8861 scrollInfo.fMask = SIF_POS;
8862 scrollInfo.nPos = nNewScrollPos;
8863 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
8865 /* carry on only if it really changed */
8866 if (nNewScrollPos == nOldScrollPos) return 0;
8868 /* now adjust to client coordinates */
8869 nScrollDiff = nOldScrollPos - nNewScrollPos;
8870 if (infoPtr->uView == LV_VIEW_DETAILS) nScrollDiff *= infoPtr->nItemHeight;
8872 /* and scroll the window */
8873 scroll_list(infoPtr, 0, nScrollDiff);
8880 * Performs horizontal scrolling.
8883 * [I] infoPtr : valid pointer to the listview structure
8884 * [I] nScrollCode : scroll code
8885 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8886 * [I] hScrollWnd : scrollbar control window handle
8892 * SB_LINELEFT/SB_LINERIGHT:
8893 * for LVS_ICON, LVS_SMALLICON 1 pixel
8894 * for LVS_REPORT is 1 pixel
8895 * for LVS_LIST is 1 column --> which is a 1 because the
8896 * scroll is based on columns not pixels
8899 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8900 INT nScrollDiff, HWND hScrollWnd)
8902 INT nOldScrollPos, nNewScrollPos;
8903 SCROLLINFO scrollInfo;
8905 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8906 debugscrollcode(nScrollCode), nScrollDiff);
8908 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8910 scrollInfo.cbSize = sizeof(SCROLLINFO);
8911 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8913 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
8915 nOldScrollPos = scrollInfo.nPos;
8917 switch (nScrollCode)
8931 nScrollDiff = -scrollInfo.nPage;
8935 nScrollDiff = scrollInfo.nPage;
8938 case SB_THUMBPOSITION:
8940 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8947 /* quit right away if pos isn't changing */
8948 if (nScrollDiff == 0) return 0;
8950 /* calculate new position, and handle overflows */
8951 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8952 if (nScrollDiff > 0) {
8953 if (nNewScrollPos < nOldScrollPos ||
8954 nNewScrollPos > scrollInfo.nMax)
8955 nNewScrollPos = scrollInfo.nMax;
8957 if (nNewScrollPos > nOldScrollPos ||
8958 nNewScrollPos < scrollInfo.nMin)
8959 nNewScrollPos = scrollInfo.nMin;
8962 /* set the new position, and reread in case it changed */
8963 scrollInfo.fMask = SIF_POS;
8964 scrollInfo.nPos = nNewScrollPos;
8965 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
8967 /* carry on only if it really changed */
8968 if (nNewScrollPos == nOldScrollPos) return 0;
8970 if (infoPtr->uView == LV_VIEW_DETAILS)
8971 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
8973 /* now adjust to client coordinates */
8974 nScrollDiff = nOldScrollPos - nNewScrollPos;
8975 if (infoPtr->uView == LV_VIEW_LIST) nScrollDiff *= infoPtr->nItemWidth;
8977 /* and scroll the window */
8978 scroll_list(infoPtr, nScrollDiff, 0);
8983 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
8985 INT gcWheelDelta = 0;
8986 INT pulScrollLines = 3;
8987 SCROLLINFO scrollInfo;
8989 TRACE("(wheelDelta=%d)\n", wheelDelta);
8991 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8992 gcWheelDelta -= wheelDelta;
8994 scrollInfo.cbSize = sizeof(SCROLLINFO);
8995 scrollInfo.fMask = SIF_POS;
8997 switch(infoPtr->uView)
9000 case LV_VIEW_SMALLICON:
9002 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
9003 * should be fixed in the future.
9005 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
9006 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
9009 case LV_VIEW_DETAILS:
9010 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
9012 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
9013 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
9014 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
9019 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
9030 * [I] infoPtr : valid pointer to the listview structure
9031 * [I] nVirtualKey : virtual key
9032 * [I] lKeyData : key data
9037 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
9039 HWND hwndSelf = infoPtr->hwndSelf;
9041 NMLVKEYDOWN nmKeyDown;
9043 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
9045 /* send LVN_KEYDOWN notification */
9046 nmKeyDown.wVKey = nVirtualKey;
9047 nmKeyDown.flags = 0;
9048 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
9049 if (!IsWindow(hwndSelf))
9052 switch (nVirtualKey)
9055 nItem = infoPtr->nFocusedItem;
9056 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
9057 toggle_checkbox_state(infoPtr, infoPtr->nFocusedItem);
9061 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
9063 if (!notify(infoPtr, NM_RETURN)) return 0;
9064 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
9069 if (infoPtr->nItemCount > 0)
9074 if (infoPtr->nItemCount > 0)
9075 nItem = infoPtr->nItemCount - 1;
9079 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TOLEFT);
9083 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_ABOVE);
9087 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TORIGHT);
9091 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_BELOW);
9095 if (infoPtr->uView == LV_VIEW_DETAILS)
9097 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
9098 if (infoPtr->nFocusedItem == topidx)
9099 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
9104 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
9105 * LISTVIEW_GetCountPerRow(infoPtr);
9106 if(nItem < 0) nItem = 0;
9110 if (infoPtr->uView == LV_VIEW_DETAILS)
9112 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
9113 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
9114 if (infoPtr->nFocusedItem == topidx + cnt - 1)
9115 nItem = infoPtr->nFocusedItem + cnt - 1;
9117 nItem = topidx + cnt - 1;
9120 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
9121 * LISTVIEW_GetCountPerRow(infoPtr);
9122 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
9126 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
9127 LISTVIEW_KeySelection(infoPtr, nItem, nVirtualKey == VK_SPACE);
9137 * [I] infoPtr : valid pointer to the listview structure
9142 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
9146 /* if we did not have the focus, there's nothing to do */
9147 if (!infoPtr->bFocus) return 0;
9149 /* send NM_KILLFOCUS notification */
9150 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
9152 /* if we have a focus rectangle, get rid of it */
9153 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
9155 /* set window focus flag */
9156 infoPtr->bFocus = FALSE;
9158 /* invalidate the selected items before resetting focus flag */
9159 LISTVIEW_InvalidateSelectedItems(infoPtr);
9166 * Processes double click messages (left mouse button).
9169 * [I] infoPtr : valid pointer to the listview structure
9170 * [I] wKey : key flag
9171 * [I] x,y : mouse coordinate
9176 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9178 LVHITTESTINFO htInfo;
9180 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
9182 /* Cancel the item edition if any */
9183 if (infoPtr->itemEdit.fEnabled)
9185 KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
9186 infoPtr->itemEdit.fEnabled = FALSE;
9189 /* send NM_RELEASEDCAPTURE notification */
9190 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9195 /* send NM_DBLCLK notification */
9196 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
9197 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
9199 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
9200 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
9207 * Processes mouse down messages (left mouse button).
9210 * infoPtr [I ] valid pointer to the listview structure
9211 * wKey [I ] key flag
9212 * x,y [I ] mouse coordinate
9217 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9219 LVHITTESTINFO lvHitTestInfo;
9220 static BOOL bGroupSelect = TRUE;
9221 POINT pt = { x, y };
9224 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
9226 /* send NM_RELEASEDCAPTURE notification */
9227 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9229 /* set left button down flag and record the click position */
9230 infoPtr->bLButtonDown = TRUE;
9231 infoPtr->ptClickPos = pt;
9232 infoPtr->bDragging = FALSE;
9234 lvHitTestInfo.pt.x = x;
9235 lvHitTestInfo.pt.y = y;
9237 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
9238 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
9239 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9241 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
9243 toggle_checkbox_state(infoPtr, nItem);
9247 if (infoPtr->dwStyle & LVS_SINGLESEL)
9249 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9250 infoPtr->nEditLabelItem = nItem;
9252 LISTVIEW_SetSelection(infoPtr, nItem);
9256 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
9260 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
9261 LISTVIEW_SetItemFocus(infoPtr, nItem);
9262 infoPtr->nSelectionMark = nItem;
9268 item.state = LVIS_SELECTED | LVIS_FOCUSED;
9269 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
9271 LISTVIEW_SetItemState(infoPtr,nItem,&item);
9272 infoPtr->nSelectionMark = nItem;
9275 else if (wKey & MK_CONTROL)
9279 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
9281 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
9282 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
9283 LISTVIEW_SetItemState(infoPtr, nItem, &item);
9284 infoPtr->nSelectionMark = nItem;
9286 else if (wKey & MK_SHIFT)
9288 LISTVIEW_SetGroupSelection(infoPtr, nItem);
9292 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9294 infoPtr->nEditLabelItem = nItem;
9295 infoPtr->nLButtonDownItem = nItem;
9297 LISTVIEW_SetItemFocus(infoPtr, nItem);
9300 /* set selection (clears other pre-existing selections) */
9301 LISTVIEW_SetSelection(infoPtr, nItem);
9305 if (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE)
9306 if(lvHitTestInfo.iItem != -1) notify_itemactivate(infoPtr,&lvHitTestInfo);
9310 /* remove all selections */
9311 if (!(wKey & MK_CONTROL) && !(wKey & MK_SHIFT))
9312 LISTVIEW_DeselectAll(infoPtr);
9321 * Processes mouse up messages (left mouse button).
9324 * infoPtr [I ] valid pointer to the listview structure
9325 * wKey [I ] key flag
9326 * x,y [I ] mouse coordinate
9331 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9333 LVHITTESTINFO lvHitTestInfo;
9335 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
9337 if (!infoPtr->bLButtonDown) return 0;
9339 lvHitTestInfo.pt.x = x;
9340 lvHitTestInfo.pt.y = y;
9342 /* send NM_CLICK notification */
9343 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9344 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
9346 /* set left button flag */
9347 infoPtr->bLButtonDown = FALSE;
9349 /* set a single selection, reset others */
9350 if(lvHitTestInfo.iItem == infoPtr->nLButtonDownItem && lvHitTestInfo.iItem != -1)
9351 LISTVIEW_SetSelection(infoPtr, infoPtr->nLButtonDownItem);
9352 infoPtr->nLButtonDownItem = -1;
9354 if (infoPtr->bDragging)
9356 infoPtr->bDragging = FALSE;
9360 /* if we clicked on a selected item, edit the label */
9361 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
9363 /* we want to make sure the user doesn't want to do a double click. So we will
9364 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
9366 infoPtr->itemEdit.fEnabled = TRUE;
9367 infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
9368 SetTimer(infoPtr->hwndSelf,
9369 (UINT_PTR)&infoPtr->itemEdit,
9370 GetDoubleClickTime(),
9371 LISTVIEW_DelayedEditItem);
9374 if (!infoPtr->bFocus)
9375 SetFocus(infoPtr->hwndSelf);
9382 * Destroys the listview control (called after WM_DESTROY).
9385 * [I] infoPtr : valid pointer to the listview structure
9390 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
9394 /* delete all items */
9395 LISTVIEW_DeleteAllItems(infoPtr, TRUE);
9397 /* destroy data structure */
9398 DPA_Destroy(infoPtr->hdpaItems);
9399 DPA_Destroy(infoPtr->hdpaItemIds);
9400 DPA_Destroy(infoPtr->hdpaPosX);
9401 DPA_Destroy(infoPtr->hdpaPosY);
9402 DPA_Destroy(infoPtr->hdpaColumns);
9403 ranges_destroy(infoPtr->selectionRanges);
9405 /* destroy image lists */
9406 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
9408 if (infoPtr->himlNormal)
9409 ImageList_Destroy(infoPtr->himlNormal);
9410 if (infoPtr->himlSmall)
9411 ImageList_Destroy(infoPtr->himlSmall);
9412 if (infoPtr->himlState)
9413 ImageList_Destroy(infoPtr->himlState);
9416 /* destroy font, bkgnd brush */
9418 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
9419 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
9421 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
9423 /* free listview info pointer*/
9431 * Handles notifications from header.
9434 * [I] infoPtr : valid pointer to the listview structure
9435 * [I] nCtrlId : control identifier
9436 * [I] lpnmh : notification information
9441 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
9443 HWND hwndSelf = infoPtr->hwndSelf;
9445 TRACE("(lpnmh=%p)\n", lpnmh);
9447 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
9449 switch (lpnmh->hdr.code)
9454 COLUMN_INFO *lpColumnInfo;
9458 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
9461 /* remove the old line (if any) */
9462 LISTVIEW_DrawTrackLine(infoPtr);
9464 /* compute & draw the new line */
9465 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
9466 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
9467 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
9468 infoPtr->xTrackLine = x + ptOrigin.x;
9469 LISTVIEW_DrawTrackLine(infoPtr);
9475 /* remove the track line (if any) */
9476 LISTVIEW_DrawTrackLine(infoPtr);
9477 infoPtr->xTrackLine = -1;
9481 notify_forward_header(infoPtr, lpnmh);
9482 return (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP) == 0;
9485 FIXME("Changing column order not implemented\n");
9486 notify_forward_header(infoPtr, lpnmh);
9489 case HDN_ITEMCHANGINGW:
9490 case HDN_ITEMCHANGINGA:
9491 return notify_forward_header(infoPtr, lpnmh);
9493 case HDN_ITEMCHANGEDW:
9494 case HDN_ITEMCHANGEDA:
9496 COLUMN_INFO *lpColumnInfo;
9499 notify_forward_header(infoPtr, lpnmh);
9500 if (!IsWindow(hwndSelf))
9503 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
9507 hdi.mask = HDI_WIDTH;
9508 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, lpnmh->iItem, (LPARAM)&hdi)) return 0;
9512 cxy = lpnmh->pitem->cxy;
9514 /* determine how much we change since the last know position */
9515 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
9516 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
9519 lpColumnInfo->rcHeader.right += dx;
9520 if (lpnmh->iItem + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
9521 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
9524 /* only needs to update the scrolls */
9525 infoPtr->nItemWidth += dx;
9526 LISTVIEW_UpdateScroll(infoPtr);
9528 LISTVIEW_UpdateItemSize(infoPtr);
9529 if (infoPtr->uView == LV_VIEW_DETAILS && is_redrawing(infoPtr))
9532 RECT rcCol = lpColumnInfo->rcHeader;
9534 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
9535 OffsetRect(&rcCol, ptOrigin.x, 0);
9537 rcCol.top = infoPtr->rcList.top;
9538 rcCol.bottom = infoPtr->rcList.bottom;
9540 /* resizing left-aligned columns leaves most of the left side untouched */
9541 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
9543 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
9546 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
9549 /* when shrinking the last column clear the now unused field */
9550 if (lpnmh->iItem == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
9556 /* deal with right from rightmost column area */
9557 right.left = rcCol.right;
9558 right.top = rcCol.top;
9559 right.bottom = rcCol.bottom;
9560 right.right = infoPtr->rcList.right;
9562 LISTVIEW_InvalidateRect(infoPtr, &right);
9565 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
9571 case HDN_ITEMCLICKW:
9572 case HDN_ITEMCLICKA:
9574 /* Handle sorting by Header Column */
9577 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
9579 nmlv.iSubItem = lpnmh->iItem;
9580 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
9581 notify_forward_header(infoPtr, lpnmh);
9585 case HDN_DIVIDERDBLCLICKW:
9586 case HDN_DIVIDERDBLCLICKA:
9587 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
9588 notify_forward_header(infoPtr, lpnmh);
9597 * Paint non-client area of control.
9600 * [I] infoPtr : valid pointer to the listview structureof the sender
9601 * [I] region : update region
9604 * TRUE - frame was painted
9605 * FALSE - call default window proc
9607 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
9609 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
9613 int cxEdge = GetSystemMetrics (SM_CXEDGE),
9614 cyEdge = GetSystemMetrics (SM_CYEDGE);
9616 if (!theme) return FALSE;
9618 GetWindowRect(infoPtr->hwndSelf, &r);
9620 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
9621 r.right - cxEdge, r.bottom - cyEdge);
9622 if (region != (HRGN)1)
9623 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
9624 OffsetRect(&r, -r.left, -r.top);
9626 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
9627 OffsetRect(&r, -r.left, -r.top);
9629 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
9630 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
9631 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
9632 ReleaseDC(infoPtr->hwndSelf, dc);
9634 /* Call default proc to get the scrollbars etc. painted */
9635 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
9642 * Determines the type of structure to use.
9645 * [I] infoPtr : valid pointer to the listview structureof the sender
9646 * [I] hwndFrom : listview window handle
9647 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
9652 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
9654 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
9656 if (nCommand == NF_REQUERY)
9657 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
9659 return infoPtr->notifyFormat;
9664 * Paints/Repaints the listview control.
9667 * [I] infoPtr : valid pointer to the listview structure
9668 * [I] hdc : device context handle
9673 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
9675 TRACE("(hdc=%p)\n", hdc);
9677 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
9679 infoPtr->bNoItemMetrics = FALSE;
9680 LISTVIEW_UpdateItemSize(infoPtr);
9681 if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)
9682 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9683 LISTVIEW_UpdateScroll(infoPtr);
9686 if (infoPtr->hwndHeader) UpdateWindow(infoPtr->hwndHeader);
9689 LISTVIEW_Refresh(infoPtr, hdc, NULL);
9694 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
9696 LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
9697 EndPaint(infoPtr->hwndSelf, &ps);
9706 * Paints/Repaints the listview control.
9709 * [I] infoPtr : valid pointer to the listview structure
9710 * [I] hdc : device context handle
9711 * [I] options : drawing options
9716 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
9718 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
9720 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
9723 if (options & PRF_ERASEBKGND)
9724 LISTVIEW_EraseBkgnd(infoPtr, hdc);
9726 if (options & PRF_CLIENT)
9727 LISTVIEW_Paint(infoPtr, hdc);
9735 * Processes double click messages (right mouse button).
9738 * [I] infoPtr : valid pointer to the listview structure
9739 * [I] wKey : key flag
9740 * [I] x,y : mouse coordinate
9745 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9747 LVHITTESTINFO lvHitTestInfo;
9749 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9751 /* send NM_RELEASEDCAPTURE notification */
9752 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9754 /* send NM_RDBLCLK notification */
9755 lvHitTestInfo.pt.x = x;
9756 lvHitTestInfo.pt.y = y;
9757 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9758 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
9765 * Processes mouse down messages (right mouse button).
9768 * [I] infoPtr : valid pointer to the listview structure
9769 * [I] wKey : key flag
9770 * [I] x,y : mouse coordinate
9775 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9777 LVHITTESTINFO lvHitTestInfo;
9780 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9782 /* send NM_RELEASEDCAPTURE notification */
9783 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9785 /* make sure the listview control window has the focus */
9786 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
9788 /* set right button down flag */
9789 infoPtr->bRButtonDown = TRUE;
9791 /* determine the index of the selected item */
9792 lvHitTestInfo.pt.x = x;
9793 lvHitTestInfo.pt.y = y;
9794 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
9796 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9798 LISTVIEW_SetItemFocus(infoPtr, nItem);
9799 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9800 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9801 LISTVIEW_SetSelection(infoPtr, nItem);
9805 LISTVIEW_DeselectAll(infoPtr);
9813 * Processes mouse up messages (right mouse button).
9816 * [I] infoPtr : valid pointer to the listview structure
9817 * [I] wKey : key flag
9818 * [I] x,y : mouse coordinate
9823 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9825 LVHITTESTINFO lvHitTestInfo;
9828 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9830 if (!infoPtr->bRButtonDown) return 0;
9832 /* set button flag */
9833 infoPtr->bRButtonDown = FALSE;
9835 /* Send NM_RCLICK notification */
9836 lvHitTestInfo.pt.x = x;
9837 lvHitTestInfo.pt.y = y;
9838 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9839 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
9841 /* Change to screen coordinate for WM_CONTEXTMENU */
9842 pt = lvHitTestInfo.pt;
9843 ClientToScreen(infoPtr->hwndSelf, &pt);
9845 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9846 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
9847 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
9858 * [I] infoPtr : valid pointer to the listview structure
9859 * [I] hwnd : window handle of window containing the cursor
9860 * [I] nHittest : hit-test code
9861 * [I] wMouseMsg : ideintifier of the mouse message
9864 * TRUE if cursor is set
9867 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
9869 LVHITTESTINFO lvHitTestInfo;
9871 if(!(LISTVIEW_isHotTracking(infoPtr))) return FALSE;
9873 if(!infoPtr->hHotCursor) return FALSE;
9875 GetCursorPos(&lvHitTestInfo.pt);
9876 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
9878 SetCursor(infoPtr->hHotCursor);
9888 * [I] infoPtr : valid pointer to the listview structure
9889 * [I] hwndLoseFocus : handle of previously focused window
9894 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
9896 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
9898 /* if we have the focus already, there's nothing to do */
9899 if (infoPtr->bFocus) return 0;
9901 /* send NM_SETFOCUS notification */
9902 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
9904 /* set window focus flag */
9905 infoPtr->bFocus = TRUE;
9907 /* put the focus rect back on */
9908 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
9910 /* redraw all visible selected items */
9911 LISTVIEW_InvalidateSelectedItems(infoPtr);
9921 * [I] infoPtr : valid pointer to the listview structure
9922 * [I] fRedraw : font handle
9923 * [I] fRedraw : redraw flag
9928 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
9930 HFONT oldFont = infoPtr->hFont;
9932 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
9934 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9935 if (infoPtr->hFont == oldFont) return 0;
9937 LISTVIEW_SaveTextMetrics(infoPtr);
9939 if (infoPtr->uView == LV_VIEW_DETAILS)
9941 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
9942 LISTVIEW_UpdateSize(infoPtr);
9943 LISTVIEW_UpdateScroll(infoPtr);
9946 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
9953 * Message handling for WM_SETREDRAW.
9954 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9957 * [I] infoPtr : valid pointer to the listview structure
9958 * [I] bRedraw: state of redraw flag
9961 * DefWinProc return value
9963 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
9965 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
9967 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9968 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
9970 infoPtr->bRedraw = bRedraw;
9972 if(!bRedraw) return 0;
9974 if (is_autoarrange(infoPtr))
9975 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9976 LISTVIEW_UpdateScroll(infoPtr);
9978 /* despite what the WM_SETREDRAW docs says, apps expect us
9979 * to invalidate the listview here... stupid! */
9980 LISTVIEW_InvalidateList(infoPtr);
9987 * Resizes the listview control. This function processes WM_SIZE
9988 * messages. At this time, the width and height are not used.
9991 * [I] infoPtr : valid pointer to the listview structure
9992 * [I] Width : new width
9993 * [I] Height : new height
9998 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
10000 RECT rcOld = infoPtr->rcList;
10002 TRACE("(width=%d, height=%d)\n", Width, Height);
10004 LISTVIEW_UpdateSize(infoPtr);
10005 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
10007 /* do not bother with display related stuff if we're not redrawing */
10008 if (!is_redrawing(infoPtr)) return 0;
10010 if (is_autoarrange(infoPtr))
10011 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
10013 LISTVIEW_UpdateScroll(infoPtr);
10015 /* refresh all only for lists whose height changed significantly */
10016 if ((infoPtr->uView == LV_VIEW_LIST) &&
10017 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
10018 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
10019 LISTVIEW_InvalidateList(infoPtr);
10026 * Sets the size information.
10029 * [I] infoPtr : valid pointer to the listview structure
10034 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
10036 TRACE("uView=%d, rcList(old)=%s\n", infoPtr->uView, wine_dbgstr_rect(&infoPtr->rcList));
10038 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
10040 if (infoPtr->uView == LV_VIEW_LIST)
10042 /* Apparently the "LIST" style is supposed to have the same
10043 * number of items in a column even if there is no scroll bar.
10044 * Since if a scroll bar already exists then the bottom is already
10045 * reduced, only reduce if the scroll bar does not currently exist.
10046 * The "2" is there to mimic the native control. I think it may be
10047 * related to either padding or edges. (GLA 7/2002)
10049 if (!(infoPtr->dwStyle & WS_HSCROLL))
10050 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
10051 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
10053 else if (infoPtr->uView == LV_VIEW_DETAILS)
10058 hl.prc = &infoPtr->rcList;
10060 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
10061 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp.flags, wp.x, wp.y, wp.cx, wp.cy);
10062 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy,
10063 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
10064 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
10065 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy);
10067 infoPtr->rcList.top = max(wp.cy, 0);
10068 infoPtr->rcList.top += (infoPtr->dwLvExStyle & LVS_EX_GRIDLINES) ? 2 : 0;
10071 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
10076 * Processes WM_STYLECHANGED messages.
10079 * [I] infoPtr : valid pointer to the listview structure
10080 * [I] wStyleType : window style type (normal or extended)
10081 * [I] lpss : window style information
10086 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
10087 const STYLESTRUCT *lpss)
10089 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
10090 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
10093 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
10094 wStyleType, lpss->styleOld, lpss->styleNew);
10096 if (wStyleType != GWL_STYLE) return 0;
10098 infoPtr->dwStyle = lpss->styleNew;
10099 map_style_view(infoPtr);
10101 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
10102 ((lpss->styleNew & WS_HSCROLL) == 0))
10103 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
10105 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
10106 ((lpss->styleNew & WS_VSCROLL) == 0))
10107 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
10109 if (uNewView != uOldView)
10111 SIZE oldIconSize = infoPtr->iconSize;
10114 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
10115 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
10117 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
10118 SetRectEmpty(&infoPtr->rcFocus);
10120 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
10121 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
10123 if (uNewView == LVS_ICON)
10125 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
10127 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
10128 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
10129 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
10132 else if (uNewView == LVS_REPORT)
10137 LISTVIEW_CreateHeader( infoPtr );
10139 hl.prc = &infoPtr->rcList;
10141 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
10142 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
10143 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
10144 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
10147 LISTVIEW_UpdateItemSize(infoPtr);
10150 if (uNewView == LVS_REPORT)
10152 if ((lpss->styleOld ^ lpss->styleNew) & LVS_NOCOLUMNHEADER)
10154 if (lpss->styleNew & LVS_NOCOLUMNHEADER)
10156 /* Turn off the header control */
10157 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
10158 TRACE("Hide header control, was 0x%08x\n", style);
10159 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style | HDS_HIDDEN);
10161 /* Turn on the header control */
10162 if ((style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE)) & HDS_HIDDEN)
10164 TRACE("Show header control, was 0x%08x\n", style);
10165 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, (style & ~HDS_HIDDEN) | WS_VISIBLE);
10171 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
10172 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
10173 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
10175 /* update the size of the client area */
10176 LISTVIEW_UpdateSize(infoPtr);
10178 /* add scrollbars if needed */
10179 LISTVIEW_UpdateScroll(infoPtr);
10181 /* invalidate client area + erase background */
10182 LISTVIEW_InvalidateList(infoPtr);
10189 * Processes WM_STYLECHANGING messages.
10192 * [I] infoPtr : valid pointer to the listview structure
10193 * [I] wStyleType : window style type (normal or extended)
10194 * [I0] lpss : window style information
10199 static INT LISTVIEW_StyleChanging(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
10202 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
10203 wStyleType, lpss->styleOld, lpss->styleNew);
10205 /* don't forward LVS_OWNERDATA only if not already set to */
10206 if ((lpss->styleNew ^ lpss->styleOld) & LVS_OWNERDATA)
10208 if (lpss->styleOld & LVS_OWNERDATA)
10209 lpss->styleNew |= LVS_OWNERDATA;
10211 lpss->styleNew &= ~LVS_OWNERDATA;
10219 * Processes WM_SHOWWINDOW messages.
10222 * [I] infoPtr : valid pointer to the listview structure
10223 * [I] bShown : window is being shown (FALSE when hidden)
10224 * [I] iStatus : window show status
10229 static LRESULT LISTVIEW_ShowWindow(LISTVIEW_INFO *infoPtr, BOOL bShown, INT iStatus)
10231 /* header delayed creation */
10232 if ((infoPtr->uView == LV_VIEW_DETAILS) && bShown)
10234 LISTVIEW_CreateHeader(infoPtr);
10236 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle))
10237 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
10245 * Processes CCM_GETVERSION messages.
10248 * [I] infoPtr : valid pointer to the listview structure
10253 static inline LRESULT LISTVIEW_GetVersion(const LISTVIEW_INFO *infoPtr)
10255 return infoPtr->iVersion;
10260 * Processes CCM_SETVERSION messages.
10263 * [I] infoPtr : valid pointer to the listview structure
10264 * [I] iVersion : version to be set
10267 * -1 when requested version is greater than DLL version;
10268 * previous version otherwise
10270 static LRESULT LISTVIEW_SetVersion(LISTVIEW_INFO *infoPtr, DWORD iVersion)
10272 INT iOldVersion = infoPtr->iVersion;
10274 if (iVersion > COMCTL32_VERSION)
10277 infoPtr->iVersion = iVersion;
10279 TRACE("new version %d\n", iVersion);
10281 return iOldVersion;
10286 * Window procedure of the listview control.
10289 static LRESULT WINAPI
10290 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10292 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
10294 TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg, wParam, lParam);
10296 if (!infoPtr && (uMsg != WM_NCCREATE))
10297 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10301 case LVM_APPROXIMATEVIEWRECT:
10302 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
10303 LOWORD(lParam), HIWORD(lParam));
10305 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
10307 case LVM_CANCELEDITLABEL:
10308 return LISTVIEW_CancelEditLabel(infoPtr);
10310 case LVM_CREATEDRAGIMAGE:
10311 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
10313 case LVM_DELETEALLITEMS:
10314 return LISTVIEW_DeleteAllItems(infoPtr, FALSE);
10316 case LVM_DELETECOLUMN:
10317 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
10319 case LVM_DELETEITEM:
10320 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
10322 case LVM_EDITLABELW:
10323 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
10325 case LVM_EDITLABELA:
10326 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
10328 /* case LVM_ENABLEGROUPVIEW: */
10330 case LVM_ENSUREVISIBLE:
10331 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
10333 case LVM_FINDITEMW:
10334 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
10336 case LVM_FINDITEMA:
10337 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
10339 case LVM_GETBKCOLOR:
10340 return infoPtr->clrBk;
10342 /* case LVM_GETBKIMAGE: */
10344 case LVM_GETCALLBACKMASK:
10345 return infoPtr->uCallbackMask;
10347 case LVM_GETCOLUMNA:
10348 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10350 case LVM_GETCOLUMNW:
10351 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10353 case LVM_GETCOLUMNORDERARRAY:
10354 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
10356 case LVM_GETCOLUMNWIDTH:
10357 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
10359 case LVM_GETCOUNTPERPAGE:
10360 return LISTVIEW_GetCountPerPage(infoPtr);
10362 case LVM_GETEDITCONTROL:
10363 return (LRESULT)infoPtr->hwndEdit;
10365 case LVM_GETEXTENDEDLISTVIEWSTYLE:
10366 return infoPtr->dwLvExStyle;
10368 /* case LVM_GETGROUPINFO: */
10370 /* case LVM_GETGROUPMETRICS: */
10372 case LVM_GETHEADER:
10373 return (LRESULT)infoPtr->hwndHeader;
10375 case LVM_GETHOTCURSOR:
10376 return (LRESULT)infoPtr->hHotCursor;
10378 case LVM_GETHOTITEM:
10379 return infoPtr->nHotItem;
10381 case LVM_GETHOVERTIME:
10382 return infoPtr->dwHoverTime;
10384 case LVM_GETIMAGELIST:
10385 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
10387 /* case LVM_GETINSERTMARK: */
10389 /* case LVM_GETINSERTMARKCOLOR: */
10391 /* case LVM_GETINSERTMARKRECT: */
10393 case LVM_GETISEARCHSTRINGA:
10394 case LVM_GETISEARCHSTRINGW:
10395 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
10399 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
10402 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
10404 case LVM_GETITEMCOUNT:
10405 return infoPtr->nItemCount;
10407 case LVM_GETITEMPOSITION:
10408 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
10410 case LVM_GETITEMRECT:
10411 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
10413 case LVM_GETITEMSPACING:
10414 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
10416 case LVM_GETITEMSTATE:
10417 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
10419 case LVM_GETITEMTEXTA:
10420 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
10422 case LVM_GETITEMTEXTW:
10423 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
10425 case LVM_GETNEXTITEM:
10426 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
10428 case LVM_GETNUMBEROFWORKAREAS:
10429 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
10432 case LVM_GETORIGIN:
10433 if (!lParam) return FALSE;
10434 if (infoPtr->uView == LV_VIEW_DETAILS ||
10435 infoPtr->uView == LV_VIEW_LIST) return FALSE;
10436 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
10439 /* case LVM_GETOUTLINECOLOR: */
10441 /* case LVM_GETSELECTEDCOLUMN: */
10443 case LVM_GETSELECTEDCOUNT:
10444 return LISTVIEW_GetSelectedCount(infoPtr);
10446 case LVM_GETSELECTIONMARK:
10447 return infoPtr->nSelectionMark;
10449 case LVM_GETSTRINGWIDTHA:
10450 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
10452 case LVM_GETSTRINGWIDTHW:
10453 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
10455 case LVM_GETSUBITEMRECT:
10456 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
10458 case LVM_GETTEXTBKCOLOR:
10459 return infoPtr->clrTextBk;
10461 case LVM_GETTEXTCOLOR:
10462 return infoPtr->clrText;
10464 /* case LVM_GETTILEINFO: */
10466 /* case LVM_GETTILEVIEWINFO: */
10468 case LVM_GETTOOLTIPS:
10469 if( !infoPtr->hwndToolTip )
10470 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
10471 return (LRESULT)infoPtr->hwndToolTip;
10473 case LVM_GETTOPINDEX:
10474 return LISTVIEW_GetTopIndex(infoPtr);
10476 case LVM_GETUNICODEFORMAT:
10477 return (infoPtr->notifyFormat == NFR_UNICODE);
10480 return infoPtr->uView;
10482 case LVM_GETVIEWRECT:
10483 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
10485 case LVM_GETWORKAREAS:
10486 FIXME("LVM_GETWORKAREAS: unimplemented\n");
10489 /* case LVM_HASGROUP: */
10492 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, TRUE);
10494 case LVM_INSERTCOLUMNA:
10495 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10497 case LVM_INSERTCOLUMNW:
10498 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10500 /* case LVM_INSERTGROUP: */
10502 /* case LVM_INSERTGROUPSORTED: */
10504 case LVM_INSERTITEMA:
10505 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
10507 case LVM_INSERTITEMW:
10508 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
10510 /* case LVM_INSERTMARKHITTEST: */
10512 /* case LVM_ISGROUPVIEWENABLED: */
10514 case LVM_ISITEMVISIBLE:
10515 return LISTVIEW_IsItemVisible(infoPtr, (INT)wParam);
10517 case LVM_MAPIDTOINDEX:
10518 return LISTVIEW_MapIdToIndex(infoPtr, (UINT)wParam);
10520 case LVM_MAPINDEXTOID:
10521 return LISTVIEW_MapIndexToId(infoPtr, (INT)wParam);
10523 /* case LVM_MOVEGROUP: */
10525 /* case LVM_MOVEITEMTOGROUP: */
10527 case LVM_REDRAWITEMS:
10528 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
10530 /* case LVM_REMOVEALLGROUPS: */
10532 /* case LVM_REMOVEGROUP: */
10535 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
10537 case LVM_SETBKCOLOR:
10538 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
10540 /* case LVM_SETBKIMAGE: */
10542 case LVM_SETCALLBACKMASK:
10543 infoPtr->uCallbackMask = (UINT)wParam;
10546 case LVM_SETCOLUMNA:
10547 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10549 case LVM_SETCOLUMNW:
10550 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10552 case LVM_SETCOLUMNORDERARRAY:
10553 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
10555 case LVM_SETCOLUMNWIDTH:
10556 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
10558 case LVM_SETEXTENDEDLISTVIEWSTYLE:
10559 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
10561 /* case LVM_SETGROUPINFO: */
10563 /* case LVM_SETGROUPMETRICS: */
10565 case LVM_SETHOTCURSOR:
10566 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
10568 case LVM_SETHOTITEM:
10569 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
10571 case LVM_SETHOVERTIME:
10572 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
10574 case LVM_SETICONSPACING:
10575 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10577 case LVM_SETIMAGELIST:
10578 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
10580 /* case LVM_SETINFOTIP: */
10582 /* case LVM_SETINSERTMARK: */
10584 /* case LVM_SETINSERTMARKCOLOR: */
10589 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
10590 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, (uMsg == LVM_SETITEMW));
10593 case LVM_SETITEMCOUNT:
10594 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
10596 case LVM_SETITEMPOSITION:
10599 pt.x = (short)LOWORD(lParam);
10600 pt.y = (short)HIWORD(lParam);
10601 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
10604 case LVM_SETITEMPOSITION32:
10605 if (lParam == 0) return FALSE;
10606 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
10608 case LVM_SETITEMSTATE:
10609 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
10611 case LVM_SETITEMTEXTA:
10612 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
10614 case LVM_SETITEMTEXTW:
10615 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
10617 /* case LVM_SETOUTLINECOLOR: */
10619 /* case LVM_SETSELECTEDCOLUMN: */
10621 case LVM_SETSELECTIONMARK:
10622 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
10624 case LVM_SETTEXTBKCOLOR:
10625 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
10627 case LVM_SETTEXTCOLOR:
10628 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
10630 /* case LVM_SETTILEINFO: */
10632 /* case LVM_SETTILEVIEWINFO: */
10634 /* case LVM_SETTILEWIDTH: */
10636 case LVM_SETTOOLTIPS:
10637 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
10639 case LVM_SETUNICODEFORMAT:
10640 return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
10643 return LISTVIEW_SetView(infoPtr, wParam);
10645 /* case LVM_SETWORKAREAS: */
10647 /* case LVM_SORTGROUPS: */
10649 case LVM_SORTITEMS:
10650 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam, FALSE);
10652 case LVM_SORTITEMSEX:
10653 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam, TRUE);
10655 case LVM_SUBITEMHITTEST:
10656 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
10659 return LISTVIEW_Update(infoPtr, (INT)wParam);
10661 case CCM_GETVERSION:
10662 return LISTVIEW_GetVersion(infoPtr);
10664 case CCM_SETVERSION:
10665 return LISTVIEW_SetVersion(infoPtr, wParam);
10668 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
10671 return LISTVIEW_Command(infoPtr, wParam, lParam);
10674 return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
10677 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
10680 return LISTVIEW_Destroy(infoPtr);
10683 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
10685 case WM_ERASEBKGND:
10686 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
10688 case WM_GETDLGCODE:
10689 return DLGC_WANTCHARS | DLGC_WANTARROWS;
10692 return (LRESULT)infoPtr->hFont;
10695 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10698 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
10701 return LISTVIEW_KillFocus(infoPtr);
10703 case WM_LBUTTONDBLCLK:
10704 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10706 case WM_LBUTTONDOWN:
10707 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10710 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10713 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10715 case WM_MOUSEHOVER:
10716 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10719 return LISTVIEW_NCDestroy(infoPtr);
10722 if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
10727 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
10728 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
10731 case WM_NOTIFYFORMAT:
10732 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
10734 case WM_PRINTCLIENT:
10735 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
10738 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
10740 case WM_RBUTTONDBLCLK:
10741 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10743 case WM_RBUTTONDOWN:
10744 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10747 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10750 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
10755 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
10758 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
10761 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
10763 case WM_SHOWWINDOW:
10764 LISTVIEW_ShowWindow(infoPtr, (BOOL)wParam, (INT)lParam);
10765 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10768 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10770 case WM_STYLECHANGED:
10771 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10773 case WM_STYLECHANGING:
10774 return LISTVIEW_StyleChanging(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10776 case WM_SYSCOLORCHANGE:
10777 COMCTL32_RefreshSysColors();
10780 /* case WM_TIMER: */
10781 case WM_THEMECHANGED:
10782 return LISTVIEW_ThemeChanged(infoPtr);
10785 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10787 case WM_MOUSEWHEEL:
10788 if (wParam & (MK_SHIFT | MK_CONTROL))
10789 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10790 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
10792 case WM_WINDOWPOSCHANGED:
10793 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
10795 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
10796 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
10798 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
10800 if (notify_measureitem(infoPtr)) LISTVIEW_InvalidateList(infoPtr);
10803 LISTVIEW_UpdateSize(infoPtr);
10804 LISTVIEW_UpdateScroll(infoPtr);
10806 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10808 /* case WM_WININICHANGE: */
10811 if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
10812 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
10815 /* call default window procedure */
10816 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10823 * Registers the window class.
10831 void LISTVIEW_Register(void)
10833 WNDCLASSW wndClass;
10835 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
10836 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
10837 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
10838 wndClass.cbClsExtra = 0;
10839 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
10840 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
10841 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
10842 wndClass.lpszClassName = WC_LISTVIEWW;
10843 RegisterClassW(&wndClass);
10848 * Unregisters the window class.
10856 void LISTVIEW_Unregister(void)
10858 UnregisterClassW(WC_LISTVIEWW, NULL);
10863 * Handle any WM_COMMAND messages
10866 * [I] infoPtr : valid pointer to the listview structure
10867 * [I] wParam : the first message parameter
10868 * [I] lParam : the second message parameter
10873 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
10875 switch (HIWORD(wParam))
10880 * Adjust the edit window size
10882 WCHAR buffer[1024];
10883 HDC hdc = GetDC(infoPtr->hwndEdit);
10884 HFONT hFont, hOldFont = 0;
10888 if (!infoPtr->hwndEdit || !hdc) return 0;
10889 GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
10890 GetWindowRect(infoPtr->hwndEdit, &rect);
10892 /* Select font to get the right dimension of the string */
10893 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
10896 hOldFont = SelectObject(hdc, hFont);
10899 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
10901 TEXTMETRICW textMetric;
10903 /* Add Extra spacing for the next character */
10904 GetTextMetricsW(hdc, &textMetric);
10905 sz.cx += (textMetric.tmMaxCharWidth * 2);
10913 rect.bottom - rect.top,
10914 SWP_DRAWFRAME|SWP_NOMOVE);
10917 SelectObject(hdc, hOldFont);
10919 ReleaseDC(infoPtr->hwndEdit, hdc);
10925 LISTVIEW_CancelEditLabel(infoPtr);
10929 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
10938 * Subclassed edit control windproc function
10941 * [I] hwnd : the edit window handle
10942 * [I] uMsg : the message that is to be processed
10943 * [I] wParam : first message parameter
10944 * [I] lParam : second message parameter
10945 * [I] isW : TRUE if input is Unicode
10950 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
10952 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
10955 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10956 hwnd, uMsg, wParam, lParam, isW);
10960 case WM_GETDLGCODE:
10961 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
10965 WNDPROC editProc = infoPtr->EditWndProc;
10966 infoPtr->EditWndProc = 0;
10967 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
10968 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
10972 if (VK_ESCAPE == (INT)wParam)
10977 else if (VK_RETURN == (INT)wParam)
10981 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
10984 /* kill the edit */
10985 if (infoPtr->hwndEdit)
10986 LISTVIEW_EndEditLabelT(infoPtr, save, isW);
10988 SendMessageW(hwnd, WM_CLOSE, 0, 0);
10994 * Subclassed edit control Unicode windproc function
10997 * [I] hwnd : the edit window handle
10998 * [I] uMsg : the message that is to be processed
10999 * [I] wParam : first message parameter
11000 * [I] lParam : second message parameter
11004 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
11006 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
11011 * Subclassed edit control ANSI windproc function
11014 * [I] hwnd : the edit window handle
11015 * [I] uMsg : the message that is to be processed
11016 * [I] wParam : first message parameter
11017 * [I] lParam : second message parameter
11021 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
11023 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
11028 * Creates a subclassed edit control
11031 * [I] infoPtr : valid pointer to the listview structure
11032 * [I] text : initial text for the edit
11033 * [I] style : the window style
11034 * [I] isW : TRUE if input is Unicode
11038 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style, BOOL isW)
11040 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
11042 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
11044 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
11046 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
11048 /* Window will be resized and positioned after LVN_BEGINLABELEDIT */
11050 hedit = CreateWindowW(editName, text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0);
11052 hedit = CreateWindowA("Edit", (LPCSTR)text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0);
11054 if (!hedit) return 0;
11056 infoPtr->EditWndProc = (WNDPROC)
11057 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
11058 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
11060 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);