4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2001 CodeWeavers Inc.
8 * Copyright 2002 Dimitrie O. Paun
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * This code was audited for completeness against the documented features
27 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
29 * Unless otherwise noted, we believe this code to be complete, as per
30 * the specification mentioned above.
31 * If you discover missing features, or bugs, please note them below.
35 * Default Message Processing
36 * -- EN_KILLFOCUS should be handled in WM_COMMAND
37 * -- WM_CREATE: create the icon and small icon image lists at this point only if
38 * the LVS_SHAREIMAGELISTS style is not specified.
39 * -- WM_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)
84 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
86 * -- LVS_TYPESTYLEMASK
89 * -- LVS_EX_BORDERSELECT
91 * -- LVS_EX_HEADERDRAGDROP
94 * -- LVS_EX_MULTIWORKAREAS
96 * -- LVS_EX_SIMPLESELECT
97 * -- LVS_EX_TWOCLICKACTIVATE
98 * -- LVS_EX_UNDERLINECOLD
99 * -- LVS_EX_UNDERLINEHOT
102 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
105 * -- LVN_MARQUEEBEGIN
111 * -- LVM_CANCELEDITLABEL
112 * -- LVM_ENABLEGROUPVIEW
113 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
114 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
115 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
116 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
117 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
118 * -- LVM_GETINSERTMARKRECT
119 * -- LVM_GETNUMBEROFWORKAREAS
120 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
121 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
122 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
123 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
124 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
125 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
126 * -- LVM_GETVIEW, LVM_SETVIEW
127 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
128 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
129 * -- LVM_INSERTGROUPSORTED
130 * -- LVM_INSERTMARKHITTEST
131 * -- LVM_ISGROUPVIEWENABLED
132 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
134 * -- LVM_MOVEITEMTOGROUP
136 * -- LVM_SETTILEWIDTH
140 * -- ListView_GetCheckSate, ListView_SetCheckState
141 * -- ListView_GetHoverTime, ListView_SetHoverTime
142 * -- ListView_GetISearchString
143 * -- ListView_GetNumberOfWorkAreas
144 * -- ListView_GetUnicodeFormat, ListView_SetUnicodeFormat
145 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
150 * Known differences in message stream from native control (not known if
151 * these differences cause problems):
152 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
153 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
154 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
155 * processing for "USEDOUBLECLICKTIME".
159 #include "wine/port.h"
174 #include "commctrl.h"
175 #include "comctl32.h"
178 #include "wine/debug.h"
179 #include "wine/unicode.h"
181 WINE_DEFAULT_DEBUG_CHANNEL(listview);
183 /* make sure you set this to 0 for production use! */
184 #define DEBUG_RANGES 1
186 typedef struct tagCOLUMN_INFO
188 RECT rcHeader; /* tracks the header's rectangle */
189 int fmt; /* same as LVCOLUMN.fmt */
192 typedef struct tagITEMHDR
196 } ITEMHDR, *LPITEMHDR;
198 typedef struct tagSUBITEM_INFO
204 typedef struct tagITEM_INFO
212 typedef struct tagRANGE
218 typedef struct tagRANGES
223 typedef struct tagITERATOR
232 typedef struct tagDELAYED_ITEM_EDIT
238 typedef struct tagLISTVIEW_INFO
245 HIMAGELIST himlNormal;
246 HIMAGELIST himlSmall;
247 HIMAGELIST himlState;
251 POINT ptClickPos; /* point where the user clicked */
252 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
255 RANGES selectionRanges;
260 RECT rcList; /* This rectangle is really the window
261 * client rectangle possibly reduced by the
262 * horizontal scroll bar and/or header - see
263 * LISTVIEW_UpdateSize. This rectangle offset
264 * by the LISTVIEW_GetOrigin value is in
265 * client coordinates */
274 INT ntmHeight; /* Some cached metrics of the font used */
275 INT ntmMaxCharWidth; /* by the listview to draw items */
277 BOOL bRedraw; /* Turns on/off repaints & invalidations */
278 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
280 BOOL bDoChangeNotify; /* send change notification messages? */
283 DWORD dwStyle; /* the cached window GWL_STYLE */
284 DWORD dwLvExStyle; /* extended listview style */
285 INT nItemCount; /* the number of items in the list */
286 HDPA hdpaItems; /* array ITEM_INFO pointers */
287 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
288 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
289 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
290 POINT currIconPos; /* this is the position next icon will be placed */
291 PFNLVCOMPARE pfnCompare;
296 INT nLButtonDownItem; /* tracks item to reset multiselection on WM_LBUTTONUP */
300 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
302 DWORD lastKeyPressTimestamp;
304 INT nSearchParamLength;
305 WCHAR szSearchParam[ MAX_PATH ];
307 INT nMeasureItemHeight;
308 INT xTrackLine; /* The x coefficient of the track line or -1 if none */
309 DELAYED_ITEM_EDIT itemEdit; /* Pointer to this structure will be the timer ID */
311 DWORD iVersion; /* CCM_[G,S]ETVERSION */
317 /* How many we debug buffer to allocate */
318 #define DEBUG_BUFFERS 20
319 /* The size of a single debug bbuffer */
320 #define DEBUG_BUFFER_SIZE 256
322 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
323 #define SB_INTERNAL -1
325 /* maximum size of a label */
326 #define DISP_TEXT_SIZE 512
328 /* padding for items in list and small icon display modes */
329 #define WIDTH_PADDING 12
331 /* padding for items in list, report and small icon display modes */
332 #define HEIGHT_PADDING 1
334 /* offset of items in report display mode */
335 #define REPORT_MARGINX 2
337 /* padding for icon in large icon display mode
338 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
339 * that HITTEST will see.
340 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
341 * ICON_TOP_PADDING - sum of the two above.
342 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
343 * LABEL_HOR_PADDING - between text and sides of box
344 * LABEL_VERT_PADDING - between bottom of text and end of box
346 * ICON_LR_PADDING - additional width above icon size.
347 * ICON_LR_HALF - half of the above value
349 #define ICON_TOP_PADDING_NOTHITABLE 2
350 #define ICON_TOP_PADDING_HITABLE 2
351 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
352 #define ICON_BOTTOM_PADDING 4
353 #define LABEL_HOR_PADDING 5
354 #define LABEL_VERT_PADDING 7
355 #define ICON_LR_PADDING 16
356 #define ICON_LR_HALF (ICON_LR_PADDING/2)
358 /* default label width for items in list and small icon display modes */
359 #define DEFAULT_LABEL_WIDTH 40
361 /* default column width for items in list display mode */
362 #define DEFAULT_COLUMN_WIDTH 128
364 /* Size of "line" scroll for V & H scrolls */
365 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
367 /* Padding between image and label */
368 #define IMAGE_PADDING 2
370 /* Padding behind the label */
371 #define TRAILING_LABEL_PADDING 12
372 #define TRAILING_HEADER_PADDING 11
374 /* Border for the icon caption */
375 #define CAPTION_BORDER 2
377 /* Standard DrawText flags */
378 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
379 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
380 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
382 /* Image index from state */
383 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
385 /* The time in milliseconds to reset the search in the list */
386 #define KEY_DELAY 450
388 /* Dump the LISTVIEW_INFO structure to the debug channel */
389 #define LISTVIEW_DUMP(iP) do { \
390 TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
391 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
392 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
393 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
394 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
395 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
396 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
397 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
398 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
399 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
402 static const WCHAR themeClass[] = {'L','i','s','t','V','i','e','w',0};
405 * forward declarations
407 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *, LPLVITEMW, BOOL);
408 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *, INT, LPRECT);
409 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *, INT, LPPOINT);
410 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *, INT, LPPOINT);
411 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *, INT, LPRECT);
412 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *, INT);
413 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *, LPPOINT);
414 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *, LPRECT);
415 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
416 static LRESULT LISTVIEW_Command(const LISTVIEW_INFO *, WPARAM, LPARAM);
417 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *, LPCWSTR, BOOL);
418 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT, BOOL);
419 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *, INT, UINT);
420 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
421 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
422 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
423 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
424 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
425 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
426 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
428 /******** Text handling functions *************************************/
430 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
431 * text string. The string may be ANSI or Unicode, in which case
432 * the boolean isW tells us the type of the string.
434 * The name of the function tell what type of strings it expects:
435 * W: Unicode, T: ANSI/Unicode - function of isW
438 static inline BOOL is_textW(LPCWSTR text)
440 return text != NULL && text != LPSTR_TEXTCALLBACKW;
443 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
445 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
446 return is_textW(text);
449 static inline int textlenT(LPCWSTR text, BOOL isW)
451 return !is_textT(text, isW) ? 0 :
452 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
455 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
458 if (isSrcW) lstrcpynW(dest, src, max);
459 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
461 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
462 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
465 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
467 LPWSTR wstr = (LPWSTR)text;
469 if (!isW && is_textT(text, isW))
471 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
472 wstr = Alloc(len * sizeof(WCHAR));
473 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
475 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
479 static inline void textfreeT(LPWSTR wstr, BOOL isW)
481 if (!isW && is_textT(wstr, isW)) Free (wstr);
485 * dest is a pointer to a Unicode string
486 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
488 static BOOL textsetptrT(LPWSTR *dest, LPCWSTR src, BOOL isW)
492 if (src == LPSTR_TEXTCALLBACKW)
494 if (is_textW(*dest)) Free(*dest);
495 *dest = LPSTR_TEXTCALLBACKW;
499 LPWSTR pszText = textdupTtoW(src, isW);
500 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
501 bResult = Str_SetPtrW(dest, pszText);
502 textfreeT(pszText, isW);
508 * compares a Unicode to a Unicode/ANSI text string
510 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
512 if (!aw) return bt ? -1 : 0;
513 if (!bt) return aw ? 1 : 0;
514 if (aw == LPSTR_TEXTCALLBACKW)
515 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
516 if (bt != LPSTR_TEXTCALLBACKW)
518 LPWSTR bw = textdupTtoW(bt, isW);
519 int r = bw ? lstrcmpW(aw, bw) : 1;
527 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
531 n = min(min(n, lstrlenW(s1)), lstrlenW(s2));
532 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
533 return res ? res - sizeof(WCHAR) : res;
536 /******** Debugging functions *****************************************/
538 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
540 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
541 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
544 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
546 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
547 n = min(textlenT(text, isW), n);
548 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
551 static char* debug_getbuf(void)
553 static int index = 0;
554 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
555 return buffers[index++ % DEBUG_BUFFERS];
558 static inline const char* debugrange(const RANGE *lprng)
560 if (!lprng) return "(null)";
561 return wine_dbg_sprintf("[%d, %d]", lprng->lower, lprng->upper);
564 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
566 char* buf = debug_getbuf(), *text = buf;
567 int len, size = DEBUG_BUFFER_SIZE;
569 if (pScrollInfo == NULL) return "(null)";
570 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
571 if (len == -1) goto end; buf += len; size -= len;
572 if (pScrollInfo->fMask & SIF_RANGE)
573 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
575 if (len == -1) goto end; buf += len; size -= len;
576 if (pScrollInfo->fMask & SIF_PAGE)
577 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
579 if (len == -1) goto end; buf += len; size -= len;
580 if (pScrollInfo->fMask & SIF_POS)
581 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
583 if (len == -1) goto end; buf += len; size -= len;
584 if (pScrollInfo->fMask & SIF_TRACKPOS)
585 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
587 if (len == -1) goto end; buf += len; size -= len;
590 buf = text + strlen(text);
592 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
596 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
598 if (!plvnm) return "(null)";
599 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
600 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
601 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
602 plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam);
605 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
607 char* buf = debug_getbuf(), *text = buf;
608 int len, size = DEBUG_BUFFER_SIZE;
610 if (lpLVItem == NULL) return "(null)";
611 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
612 if (len == -1) goto end; buf += len; size -= len;
613 if (lpLVItem->mask & LVIF_STATE)
614 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
616 if (len == -1) goto end; buf += len; size -= len;
617 if (lpLVItem->mask & LVIF_TEXT)
618 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
620 if (len == -1) goto end; buf += len; size -= len;
621 if (lpLVItem->mask & LVIF_IMAGE)
622 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
624 if (len == -1) goto end; buf += len; size -= len;
625 if (lpLVItem->mask & LVIF_PARAM)
626 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
628 if (len == -1) goto end; buf += len; size -= len;
629 if (lpLVItem->mask & LVIF_INDENT)
630 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
632 if (len == -1) goto end; buf += len; size -= len;
635 buf = text + strlen(text);
637 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
641 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
643 char* buf = debug_getbuf(), *text = buf;
644 int len, size = DEBUG_BUFFER_SIZE;
646 if (lpColumn == NULL) return "(null)";
647 len = snprintf(buf, size, "{");
648 if (len == -1) goto end; buf += len; size -= len;
649 if (lpColumn->mask & LVCF_SUBITEM)
650 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
652 if (len == -1) goto end; buf += len; size -= len;
653 if (lpColumn->mask & LVCF_FMT)
654 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
656 if (len == -1) goto end; buf += len; size -= len;
657 if (lpColumn->mask & LVCF_WIDTH)
658 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
660 if (len == -1) goto end; buf += len; size -= len;
661 if (lpColumn->mask & LVCF_TEXT)
662 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
664 if (len == -1) goto end; buf += len; size -= len;
665 if (lpColumn->mask & LVCF_IMAGE)
666 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
668 if (len == -1) goto end; buf += len; size -= len;
669 if (lpColumn->mask & LVCF_ORDER)
670 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
672 if (len == -1) goto end; buf += len; size -= len;
675 buf = text + strlen(text);
677 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
681 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
683 if (!lpht) return "(null)";
685 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
686 wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
689 /* Return the corresponding text for a given scroll value */
690 static inline LPCSTR debugscrollcode(int nScrollCode)
694 case SB_LINELEFT: return "SB_LINELEFT";
695 case SB_LINERIGHT: return "SB_LINERIGHT";
696 case SB_PAGELEFT: return "SB_PAGELEFT";
697 case SB_PAGERIGHT: return "SB_PAGERIGHT";
698 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
699 case SB_THUMBTRACK: return "SB_THUMBTRACK";
700 case SB_ENDSCROLL: return "SB_ENDSCROLL";
701 case SB_INTERNAL: return "SB_INTERNAL";
702 default: return "unknown";
707 /******** Notification functions ************************************/
709 static LRESULT notify_forward_header(const LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
711 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
712 (WPARAM)lpnmh->hdr.idFrom, (LPARAM)lpnmh);
715 static LRESULT notify_hdr(const LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
719 TRACE("(code=%d)\n", code);
721 pnmh->hwndFrom = infoPtr->hwndSelf;
722 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
724 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, pnmh->idFrom, (LPARAM)pnmh);
726 TRACE(" <= %ld\n", result);
731 static inline BOOL notify(const LISTVIEW_INFO *infoPtr, INT code)
734 HWND hwnd = infoPtr->hwndSelf;
735 notify_hdr(infoPtr, code, &nmh);
736 return IsWindow(hwnd);
739 static inline void notify_itemactivate(const LISTVIEW_INFO *infoPtr, const LVHITTESTINFO *htInfo)
750 item.mask = LVIF_PARAM|LVIF_STATE;
751 item.iItem = htInfo->iItem;
753 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
754 nmia.lParam = item.lParam;
755 nmia.uOldState = item.state;
756 nmia.uNewState = item.state | LVIS_ACTIVATING;
757 nmia.uChanged = LVIF_STATE;
760 nmia.iItem = htInfo->iItem;
761 nmia.iSubItem = htInfo->iSubItem;
762 nmia.ptAction = htInfo->pt;
764 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
765 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
766 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
768 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
771 static inline LRESULT notify_listview(const LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
773 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
774 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
777 static BOOL notify_click(const LISTVIEW_INFO *infoPtr, INT code, const LVHITTESTINFO *lvht)
781 HWND hwnd = infoPtr->hwndSelf;
783 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
784 ZeroMemory(&nmlv, sizeof(nmlv));
785 nmlv.iItem = lvht->iItem;
786 nmlv.iSubItem = lvht->iSubItem;
787 nmlv.ptAction = lvht->pt;
788 item.mask = LVIF_PARAM;
789 item.iItem = lvht->iItem;
791 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
792 notify_listview(infoPtr, code, &nmlv);
793 return IsWindow(hwnd);
796 static BOOL notify_deleteitem(const LISTVIEW_INFO *infoPtr, INT nItem)
800 HWND hwnd = infoPtr->hwndSelf;
802 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
804 item.mask = LVIF_PARAM;
807 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
808 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
809 return IsWindow(hwnd);
812 static int get_ansi_notification(UINT unicodeNotificationCode)
814 switch (unicodeNotificationCode)
816 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
817 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
818 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
819 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
820 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
821 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
823 ERR("unknown notification %x\n", unicodeNotificationCode);
829 Send notification. depends on dispinfoW having same
830 structure as dispinfoA.
831 infoPtr : listview struct
832 notificationCode : *Unicode* notification code
833 pdi : dispinfo structure (can be unicode or ansi)
834 isW : TRUE if dispinfo is Unicode
836 static BOOL notify_dispinfoT(const LISTVIEW_INFO *infoPtr, UINT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
838 BOOL bResult = FALSE;
839 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
840 INT cchTempBufMax = 0, savCchTextMax = 0;
842 LPWSTR pszTempBuf = NULL, savPszText = NULL;
844 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
846 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
847 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
850 if (convertToAnsi || convertToUnicode)
852 if (notificationCode != LVN_GETDISPINFOW)
854 cchTempBufMax = convertToUnicode ?
855 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
856 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
860 cchTempBufMax = pdi->item.cchTextMax;
861 *pdi->item.pszText = 0; /* make sure we don't process garbage */
864 pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
865 if (!pszTempBuf) return FALSE;
867 if (convertToUnicode)
868 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
869 pszTempBuf, cchTempBufMax);
871 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
872 cchTempBufMax, NULL, NULL);
874 savCchTextMax = pdi->item.cchTextMax;
875 savPszText = pdi->item.pszText;
876 pdi->item.pszText = pszTempBuf;
877 pdi->item.cchTextMax = cchTempBufMax;
880 if (infoPtr->notifyFormat == NFR_ANSI)
881 realNotifCode = get_ansi_notification(notificationCode);
883 realNotifCode = notificationCode;
884 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
885 bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
887 if (convertToUnicode || convertToAnsi)
889 if (convertToUnicode) /* note : pointer can be changed by app ! */
890 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
891 savCchTextMax, NULL, NULL);
893 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
894 savPszText, savCchTextMax);
895 pdi->item.pszText = savPszText; /* restores our buffer */
896 pdi->item.cchTextMax = savCchTextMax;
902 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, const LISTVIEW_INFO *infoPtr, HDC hdc,
903 const RECT *rcBounds, const LVITEMW *lplvItem)
905 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
906 lpnmlvcd->nmcd.hdc = hdc;
907 lpnmlvcd->nmcd.rc = *rcBounds;
908 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
909 lpnmlvcd->clrText = infoPtr->clrText;
910 if (!lplvItem) return;
911 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
912 lpnmlvcd->iSubItem = lplvItem->iSubItem;
913 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
914 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
915 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
916 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
919 static inline DWORD notify_customdraw (const LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
921 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
924 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
925 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
926 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
927 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
928 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
929 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
933 static void prepaint_setup (const LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd, BOOL SubItem)
935 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
936 lpnmlvcd->clrTextBk = comctl32_color.clrWindow;
937 if (lpnmlvcd->clrText == CLR_DEFAULT)
938 lpnmlvcd->clrText = comctl32_color.clrWindowText;
940 /* apparently, for selected items, we have to override the returned values */
943 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
947 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
948 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
950 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
952 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
953 lpnmlvcd->clrText = comctl32_color.clrBtnText;
958 /* Set the text attributes */
959 if (lpnmlvcd->clrTextBk != CLR_NONE)
961 SetBkMode(hdc, OPAQUE);
962 SetBkColor(hdc,lpnmlvcd->clrTextBk);
965 SetBkMode(hdc, TRANSPARENT);
966 SetTextColor(hdc, lpnmlvcd->clrText);
969 static inline DWORD notify_postpaint (const LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
971 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
974 /******** Item iterator functions **********************************/
976 static RANGES ranges_create(int count);
977 static void ranges_destroy(RANGES ranges);
978 static BOOL ranges_add(RANGES ranges, RANGE range);
979 static BOOL ranges_del(RANGES ranges, RANGE range);
980 static void ranges_dump(RANGES ranges);
982 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
984 RANGE range = { nItem, nItem + 1 };
986 return ranges_add(ranges, range);
989 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
991 RANGE range = { nItem, nItem + 1 };
993 return ranges_del(ranges, range);
997 * ITERATOR DOCUMENTATION
999 * The iterator functions allow for easy, and convenient iteration
1000 * over items of interest in the list. Typically, you create a
1001 * iterator, use it, and destroy it, as such:
1004 * iterator_xxxitems(&i, ...);
1005 * while (iterator_{prev,next}(&i)
1007 * //code which uses i.nItem
1009 * iterator_destroy(&i);
1011 * where xxx is either: framed, or visible.
1012 * Note that it is important that the code destroys the iterator
1013 * after it's done with it, as the creation of the iterator may
1014 * allocate memory, which thus needs to be freed.
1016 * You can iterate both forwards, and backwards through the list,
1017 * by using iterator_next or iterator_prev respectively.
1019 * Lower numbered items are draw on top of higher number items in
1020 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1021 * items may overlap). So, to test items, you should use
1023 * which lists the items top to bottom (in Z-order).
1024 * For drawing items, you should use
1026 * which lists the items bottom to top (in Z-order).
1027 * If you keep iterating over the items after the end-of-items
1028 * marker (-1) is returned, the iterator will start from the
1029 * beginning. Typically, you don't need to test for -1,
1030 * because iterator_{next,prev} will return TRUE if more items
1031 * are to be iterated over, or FALSE otherwise.
1033 * Note: the iterator is defined to be bidirectional. That is,
1034 * any number of prev followed by any number of next, or
1035 * five versa, should leave the iterator at the same item:
1036 * prev * n, next * n = next * n, prev * n
1038 * The iterator has a notion of an out-of-order, special item,
1039 * which sits at the start of the list. This is used in
1040 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1041 * which needs to be first, as it may overlap other items.
1043 * The code is a bit messy because we have:
1044 * - a special item to deal with
1045 * - simple range, or composite range
1047 * If you find bugs, or want to add features, please make sure you
1048 * always check/modify *both* iterator_prev, and iterator_next.
1052 * This function iterates through the items in increasing order,
1053 * but prefixed by the special item, then -1. That is:
1054 * special, 1, 2, 3, ..., n, -1.
1055 * Each item is listed only once.
1057 static inline BOOL iterator_next(ITERATOR* i)
1061 i->nItem = i->nSpecial;
1062 if (i->nItem != -1) return TRUE;
1064 if (i->nItem == i->nSpecial)
1066 if (i->ranges) i->index = 0;
1072 if (i->nItem == i->nSpecial) i->nItem++;
1073 if (i->nItem < i->range.upper) return TRUE;
1078 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1079 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1082 else if (i->nItem >= i->range.upper) goto end;
1084 i->nItem = i->range.lower;
1085 if (i->nItem >= 0) goto testitem;
1092 * This function iterates through the items in decreasing order,
1093 * followed by the special item, then -1. That is:
1094 * n, n-1, ..., 3, 2, 1, special, -1.
1095 * Each item is listed only once.
1097 static inline BOOL iterator_prev(ITERATOR* i)
1104 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1107 if (i->nItem == i->nSpecial)
1115 if (i->nItem == i->nSpecial) i->nItem--;
1116 if (i->nItem >= i->range.lower) return TRUE;
1122 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1125 else if (!start && i->nItem < i->range.lower) goto end;
1127 i->nItem = i->range.upper;
1128 if (i->nItem > 0) goto testitem;
1130 return (i->nItem = i->nSpecial) != -1;
1133 static RANGE iterator_range(const ITERATOR *i)
1137 if (!i->ranges) return i->range;
1139 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1141 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1142 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1144 else range.lower = range.upper = 0;
1150 * Releases resources associated with this ierator.
1152 static inline void iterator_destroy(const ITERATOR *i)
1154 ranges_destroy(i->ranges);
1158 * Create an empty iterator.
1160 static inline BOOL iterator_empty(ITERATOR* i)
1162 ZeroMemory(i, sizeof(*i));
1163 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1168 * Create an iterator over a range.
1170 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1178 * Create an iterator over a bunch of ranges.
1179 * Please note that the iterator will take ownership of the ranges,
1180 * and will free them upon destruction.
1182 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1190 * Creates an iterator over the items which intersect lprc.
1192 static BOOL iterator_frameditems(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *lprc)
1194 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1195 RECT frame = *lprc, rcItem, rcTemp;
1198 /* in case we fail, we want to return an empty iterator */
1199 if (!iterator_empty(i)) return FALSE;
1201 LISTVIEW_GetOrigin(infoPtr, &Origin);
1203 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc));
1204 OffsetRect(&frame, -Origin.x, -Origin.y);
1206 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1210 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1212 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1213 if (IntersectRect(&rcTemp, &rcItem, lprc))
1214 i->nSpecial = infoPtr->nFocusedItem;
1216 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1217 /* to do better here, we need to have PosX, and PosY sorted */
1218 TRACE("building icon ranges:\n");
1219 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1221 rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1222 rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1223 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1224 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1225 if (IntersectRect(&rcTemp, &rcItem, &frame))
1226 ranges_additem(i->ranges, nItem);
1230 else if (uView == LVS_REPORT)
1234 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1235 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1237 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1238 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1239 if (range.upper <= range.lower) return TRUE;
1240 if (!iterator_rangeitems(i, range)) return FALSE;
1241 TRACE(" report=%s\n", debugrange(&i->range));
1245 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1246 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1247 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1248 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1249 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1250 INT lower = nFirstCol * nPerCol + nFirstRow;
1254 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1255 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1257 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1259 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1260 TRACE("building list ranges:\n");
1261 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1263 item_range.lower = nCol * nPerCol + nFirstRow;
1264 if(item_range.lower >= infoPtr->nItemCount) break;
1265 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1266 TRACE(" list=%s\n", debugrange(&item_range));
1267 ranges_add(i->ranges, item_range);
1275 * Creates an iterator over the items which intersect the visible region of hdc.
1277 static BOOL iterator_visibleitems(ITERATOR *i, const LISTVIEW_INFO *infoPtr, HDC hdc)
1279 POINT Origin, Position;
1280 RECT rcItem, rcClip;
1283 rgntype = GetClipBox(hdc, &rcClip);
1284 if (rgntype == NULLREGION) return iterator_empty(i);
1285 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1286 if (rgntype == SIMPLEREGION) return TRUE;
1288 /* first deal with the special item */
1289 if (i->nSpecial != -1)
1291 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1292 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1295 /* if we can't deal with the region, we'll just go with the simple range */
1296 LISTVIEW_GetOrigin(infoPtr, &Origin);
1297 TRACE("building visible range:\n");
1298 if (!i->ranges && i->range.lower < i->range.upper)
1300 if (!(i->ranges = ranges_create(50))) return TRUE;
1301 if (!ranges_add(i->ranges, i->range))
1303 ranges_destroy(i->ranges);
1309 /* now delete the invisible items from the list */
1310 while(iterator_next(i))
1312 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1313 rcItem.left = Position.x + Origin.x;
1314 rcItem.top = Position.y + Origin.y;
1315 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1316 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1317 if (!RectVisible(hdc, &rcItem))
1318 ranges_delitem(i->ranges, i->nItem);
1320 /* the iterator should restart on the next iterator_next */
1326 /******** Misc helper functions ************************************/
1328 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1329 WPARAM wParam, LPARAM lParam, BOOL isW)
1331 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1332 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1335 static inline BOOL is_autoarrange(const LISTVIEW_INFO *infoPtr)
1337 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1339 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1340 (uView == LVS_ICON || uView == LVS_SMALLICON);
1343 static void toggle_checkbox_state(LISTVIEW_INFO *infoPtr, INT nItem)
1345 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
1346 if(state == 1 || state == 2)
1350 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
1351 lvitem.stateMask = LVIS_STATEIMAGEMASK;
1352 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
1356 /******** Internal API functions ************************************/
1358 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(const LISTVIEW_INFO *infoPtr, INT nSubItem)
1360 static COLUMN_INFO mainItem;
1362 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1363 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1364 return DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1367 static INT LISTVIEW_CreateHeader(LISTVIEW_INFO *infoPtr)
1369 DWORD dFlags = WS_CHILD | HDS_HORZ | HDS_FULLDRAG | HDS_DRAGDROP;
1372 if (infoPtr->hwndHeader) return 0;
1374 TRACE("Creating header for list %p\n", infoPtr->hwndSelf);
1376 /* setup creation flags */
1377 dFlags |= (LVS_NOSORTHEADER & infoPtr->dwStyle) ? 0 : HDS_BUTTONS;
1378 dFlags |= (LVS_NOCOLUMNHEADER & infoPtr->dwStyle) ? HDS_HIDDEN : 0;
1380 hInst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
1383 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL, dFlags,
1384 0, 0, 0, 0, infoPtr->hwndSelf, NULL, hInst, NULL);
1385 if (!infoPtr->hwndHeader) return -1;
1387 /* set header unicode format */
1388 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, TRUE, 0);
1390 /* set header font */
1391 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
1393 LISTVIEW_UpdateSize(infoPtr);
1398 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO *infoPtr, INT nSubItem, LPRECT lprc)
1400 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1403 static inline BOOL LISTVIEW_GetItemW(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1405 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1408 /* used to handle collapse main item column case */
1409 static inline BOOL LISTVIEW_DrawFocusRect(const LISTVIEW_INFO *infoPtr, HDC hdc)
1411 return (infoPtr->rcFocus.left < infoPtr->rcFocus.right) ?
1412 DrawFocusRect(hdc, &infoPtr->rcFocus) : FALSE;
1415 /* Listview invalidation functions: use _only_ these functions to invalidate */
1417 static inline BOOL is_redrawing(const LISTVIEW_INFO *infoPtr)
1419 return infoPtr->bRedraw;
1422 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO *infoPtr, const RECT* rect)
1424 if(!is_redrawing(infoPtr)) return;
1425 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect));
1426 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1429 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO *infoPtr, INT nItem)
1433 if(!is_redrawing(infoPtr)) return;
1434 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1435 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1438 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1440 POINT Origin, Position;
1443 if(!is_redrawing(infoPtr)) return;
1444 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1445 LISTVIEW_GetOrigin(infoPtr, &Origin);
1446 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1447 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1449 rcBox.bottom = infoPtr->nItemHeight;
1450 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1451 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1454 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO *infoPtr)
1456 LISTVIEW_InvalidateRect(infoPtr, NULL);
1459 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO *infoPtr, INT nColumn)
1463 if(!is_redrawing(infoPtr)) return;
1464 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1465 rcCol.top = infoPtr->rcList.top;
1466 rcCol.bottom = infoPtr->rcList.bottom;
1467 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1472 * Retrieves the number of items that can fit vertically in the client area.
1475 * [I] infoPtr : valid pointer to the listview structure
1478 * Number of items per row.
1480 static inline INT LISTVIEW_GetCountPerRow(const LISTVIEW_INFO *infoPtr)
1482 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1484 return max(nListWidth/infoPtr->nItemWidth, 1);
1489 * Retrieves the number of items that can fit horizontally in the client
1493 * [I] infoPtr : valid pointer to the listview structure
1496 * Number of items per column.
1498 static inline INT LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO *infoPtr)
1500 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1502 return max(nListHeight / infoPtr->nItemHeight, 1);
1506 /*************************************************************************
1507 * LISTVIEW_ProcessLetterKeys
1509 * Processes keyboard messages generated by pressing the letter keys
1511 * What this does is perform a case insensitive search from the
1512 * current position with the following quirks:
1513 * - If two chars or more are pressed in quick succession we search
1514 * for the corresponding string (e.g. 'abc').
1515 * - If there is a delay we wipe away the current search string and
1516 * restart with just that char.
1517 * - If the user keeps pressing the same character, whether slowly or
1518 * fast, so that the search string is entirely composed of this
1519 * character ('aaaaa' for instance), then we search for first item
1520 * that starting with that character.
1521 * - If the user types the above character in quick succession, then
1522 * we must also search for the corresponding string ('aaaaa'), and
1523 * go to that string if there is a match.
1526 * [I] hwnd : handle to the window
1527 * [I] charCode : the character code, the actual character
1528 * [I] keyData : key data
1536 * - The current implementation has a list of characters it will
1537 * accept and it ignores everything else. In particular it will
1538 * ignore accentuated characters which seems to match what
1539 * Windows does. But I'm not sure it makes sense to follow
1541 * - We don't sound a beep when the search fails.
1545 * TREEVIEW_ProcessLetterKeys
1547 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1552 WCHAR buffer[MAX_PATH];
1553 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1555 /* simple parameter checking */
1556 if (!charCode || !keyData) return 0;
1558 /* only allow the valid WM_CHARs through */
1559 if (!isalnumW(charCode) &&
1560 charCode != '.' && charCode != '`' && charCode != '!' &&
1561 charCode != '@' && charCode != '#' && charCode != '$' &&
1562 charCode != '%' && charCode != '^' && charCode != '&' &&
1563 charCode != '*' && charCode != '(' && charCode != ')' &&
1564 charCode != '-' && charCode != '_' && charCode != '+' &&
1565 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1566 charCode != '}' && charCode != '[' && charCode != '{' &&
1567 charCode != '/' && charCode != '?' && charCode != '>' &&
1568 charCode != '<' && charCode != ',' && charCode != '~')
1571 /* if there's one item or less, there is no where to go */
1572 if (infoPtr->nItemCount <= 1) return 0;
1574 /* update the search parameters */
1575 infoPtr->lastKeyPressTimestamp = GetTickCount();
1576 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1577 if (infoPtr->nSearchParamLength < MAX_PATH-1)
1578 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1579 if (infoPtr->charCode != charCode)
1580 infoPtr->charCode = charCode = 0;
1582 infoPtr->charCode=charCode;
1583 infoPtr->szSearchParam[0]=charCode;
1584 infoPtr->nSearchParamLength=1;
1585 /* Redundant with the 1 char string */
1589 /* and search from the current position */
1591 if (infoPtr->nFocusedItem >= 0) {
1592 endidx=infoPtr->nFocusedItem;
1594 /* if looking for single character match,
1595 * then we must always move forward
1597 if (infoPtr->nSearchParamLength == 1)
1600 endidx=infoPtr->nItemCount;
1604 /* Let application handle this for virtual listview */
1605 if (infoPtr->dwStyle & LVS_OWNERDATA)
1610 ZeroMemory(&lvfi, sizeof(lvfi));
1611 lvfi.flags = (LVFI_WRAP | LVFI_PARTIAL);
1612 infoPtr->szSearchParam[infoPtr->nSearchParamLength] = '\0';
1613 lvfi.psz = infoPtr->szSearchParam;
1617 nItem = notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
1620 LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1626 if (idx == infoPtr->nItemCount) {
1627 if (endidx == infoPtr->nItemCount || endidx == 0)
1633 item.mask = LVIF_TEXT;
1636 item.pszText = buffer;
1637 item.cchTextMax = MAX_PATH;
1638 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1640 /* check for a match */
1641 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1644 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1645 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1646 /* This would work but we must keep looking for a longer match */
1650 } while (idx != endidx);
1653 LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1658 /*************************************************************************
1659 * LISTVIEW_UpdateHeaderSize [Internal]
1661 * Function to resize the header control
1664 * [I] hwnd : handle to a window
1665 * [I] nNewScrollPos : scroll pos to set
1670 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1675 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1677 if (!infoPtr->hwndHeader) return;
1679 GetWindowRect(infoPtr->hwndHeader, &winRect);
1680 point[0].x = winRect.left;
1681 point[0].y = winRect.top;
1682 point[1].x = winRect.right;
1683 point[1].y = winRect.bottom;
1685 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1686 point[0].x = -nNewScrollPos;
1687 point[1].x += nNewScrollPos;
1689 SetWindowPos(infoPtr->hwndHeader,0,
1690 point[0].x,point[0].y,point[1].x,point[1].y,
1691 (infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW |
1692 SWP_NOZORDER | SWP_NOACTIVATE);
1697 * Update the scrollbars. This functions should be called whenever
1698 * the content, size or view changes.
1701 * [I] infoPtr : valid pointer to the listview structure
1706 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *infoPtr)
1708 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1709 SCROLLINFO horzInfo, vertInfo;
1712 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1714 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1715 horzInfo.cbSize = sizeof(SCROLLINFO);
1716 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1718 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1719 if (uView == LVS_LIST)
1721 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1722 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1724 /* scroll by at least one column per page */
1725 if(horzInfo.nPage < infoPtr->nItemWidth)
1726 horzInfo.nPage = infoPtr->nItemWidth;
1728 horzInfo.nPage /= infoPtr->nItemWidth;
1730 else if (uView == LVS_REPORT)
1732 horzInfo.nMax = infoPtr->nItemWidth;
1734 else /* LVS_ICON, or LVS_SMALLICON */
1738 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1741 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1742 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1743 dx = GetScrollPos(infoPtr->hwndSelf, SB_HORZ);
1744 dx -= SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1745 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1747 /* Setting the horizontal scroll can change the listview size
1748 * (and potentially everything else) so we need to recompute
1749 * everything again for the vertical scroll
1752 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1753 vertInfo.cbSize = sizeof(SCROLLINFO);
1754 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1756 if (uView == LVS_REPORT)
1758 vertInfo.nMax = infoPtr->nItemCount;
1760 /* scroll by at least one page */
1761 if(vertInfo.nPage < infoPtr->nItemHeight)
1762 vertInfo.nPage = infoPtr->nItemHeight;
1764 if (infoPtr->nItemHeight > 0)
1765 vertInfo.nPage /= infoPtr->nItemHeight;
1767 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1771 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1774 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1775 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1776 dy = GetScrollPos(infoPtr->hwndSelf, SB_VERT);
1777 dy -= SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1778 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1780 /* Change of the range may have changed the scroll pos. If so move the content */
1781 if (dx != 0 || dy != 0)
1784 listRect = infoPtr->rcList;
1785 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &listRect, &listRect, 0, 0,
1786 SW_ERASE | SW_INVALIDATE);
1789 /* Update the Header Control */
1790 if (uView == LVS_REPORT)
1792 horzInfo.fMask = SIF_POS;
1793 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1794 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1801 * Shows/hides the focus rectangle.
1804 * [I] infoPtr : valid pointer to the listview structure
1805 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1810 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO *infoPtr, BOOL fShow)
1812 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1815 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1817 if (infoPtr->nFocusedItem < 0) return;
1819 /* we need some gymnastics in ICON mode to handle large items */
1820 if (uView == LVS_ICON)
1824 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1825 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1827 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1832 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1834 /* for some reason, owner draw should work only in report mode */
1835 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1840 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1841 HFONT hOldFont = SelectObject(hdc, hFont);
1843 item.iItem = infoPtr->nFocusedItem;
1845 item.mask = LVIF_PARAM;
1846 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1848 ZeroMemory(&dis, sizeof(dis));
1849 dis.CtlType = ODT_LISTVIEW;
1850 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1851 dis.itemID = item.iItem;
1852 dis.itemAction = ODA_FOCUS;
1853 if (fShow) dis.itemState |= ODS_FOCUS;
1854 dis.hwndItem = infoPtr->hwndSelf;
1856 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1857 dis.itemData = item.lParam;
1859 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1861 SelectObject(hdc, hOldFont);
1865 LISTVIEW_DrawFocusRect(infoPtr, hdc);
1868 ReleaseDC(infoPtr->hwndSelf, hdc);
1872 * Invalidates all visible selected items.
1874 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO *infoPtr)
1878 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1879 while(iterator_next(&i))
1881 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1882 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1884 iterator_destroy(&i);
1889 * DESCRIPTION: [INTERNAL]
1890 * Computes an item's (left,top) corner, relative to rcView.
1891 * That is, the position has NOT been made relative to the Origin.
1892 * This is deliberate, to avoid computing the Origin over, and
1893 * over again, when this function is called in a loop. Instead,
1894 * one can factor the computation of the Origin before the loop,
1895 * and offset the value returned by this function, on every iteration.
1898 * [I] infoPtr : valid pointer to the listview structure
1899 * [I] nItem : item number
1900 * [O] lpptOrig : item top, left corner
1905 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1907 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1909 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1911 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1913 lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1914 lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1916 else if (uView == LVS_LIST)
1918 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1919 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1920 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1922 else /* LVS_REPORT */
1924 lpptPosition->x = 0;
1925 lpptPosition->y = nItem * infoPtr->nItemHeight;
1930 * DESCRIPTION: [INTERNAL]
1931 * Compute the rectangles of an item. This is to localize all
1932 * the computations in one place. If you are not interested in some
1933 * of these values, simply pass in a NULL -- the function is smart
1934 * enough to compute only what's necessary. The function computes
1935 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1936 * one, the BOX rectangle. This rectangle is very cheap to compute,
1937 * and is guaranteed to contain all the other rectangles. Computing
1938 * the ICON rect is also cheap, but all the others are potentially
1939 * expensive. This gives an easy and effective optimization when
1940 * searching (like point inclusion, or rectangle intersection):
1941 * first test against the BOX, and if TRUE, test against the desired
1943 * If the function does not have all the necessary information
1944 * to computed the requested rectangles, will crash with a
1945 * failed assertion. This is done so we catch all programming
1946 * errors, given that the function is called only from our code.
1948 * We have the following 'special' meanings for a few fields:
1949 * * If LVIS_FOCUSED is set, we assume the item has the focus
1950 * This is important in ICON mode, where it might get a larger
1951 * then usual rectangle
1953 * Please note that subitem support works only in REPORT mode.
1956 * [I] infoPtr : valid pointer to the listview structure
1957 * [I] lpLVItem : item to compute the measures for
1958 * [O] lprcBox : ptr to Box rectangle
1959 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1960 * [0] lprcSelectBox : ptr to select box rectangle
1961 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
1962 * [O] lprcIcon : ptr to Icon rectangle
1963 * Same as LVM_GETITEMRECT with LVIR_ICON
1964 * [O] lprcStateIcon: ptr to State Icon rectangle
1965 * [O] lprcLabel : ptr to Label rectangle
1966 * Same as LVM_GETITEMRECT with LVIR_LABEL
1971 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1972 LPRECT lprcBox, LPRECT lprcSelectBox,
1973 LPRECT lprcIcon, LPRECT lprcStateIcon, LPRECT lprcLabel)
1975 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1976 BOOL doSelectBox = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1977 RECT Box, SelectBox, Icon, Label;
1978 COLUMN_INFO *lpColumnInfo = NULL;
1979 SIZE labelSize = { 0, 0 };
1981 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1983 /* Be smart and try to figure out the minimum we have to do */
1984 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1985 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1987 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1988 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1990 if (lprcSelectBox) doSelectBox = TRUE;
1991 if (lprcLabel) doLabel = TRUE;
1992 if (doLabel || lprcIcon || lprcStateIcon) doIcon = TRUE;
1999 /************************************************************/
2000 /* compute the box rectangle (it should be cheap to do) */
2001 /************************************************************/
2002 if (lpLVItem->iSubItem || uView == LVS_REPORT)
2003 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
2005 if (lpLVItem->iSubItem)
2007 Box = lpColumnInfo->rcHeader;
2012 Box.right = infoPtr->nItemWidth;
2015 Box.bottom = infoPtr->nItemHeight;
2017 /******************************************************************/
2018 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
2019 /******************************************************************/
2022 LONG state_width = 0;
2024 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
2025 state_width = infoPtr->iconStateSize.cx;
2027 if (uView == LVS_ICON)
2029 Icon.left = Box.left + state_width;
2030 if (infoPtr->himlNormal)
2031 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx - state_width) / 2;
2032 Icon.top = Box.top + ICON_TOP_PADDING;
2033 Icon.right = Icon.left;
2034 Icon.bottom = Icon.top;
2035 if (infoPtr->himlNormal)
2037 Icon.right += infoPtr->iconSize.cx;
2038 Icon.bottom += infoPtr->iconSize.cy;
2041 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2043 Icon.left = Box.left + state_width;
2045 if (uView == LVS_REPORT)
2046 Icon.left += REPORT_MARGINX;
2049 Icon.right = Icon.left;
2050 if (infoPtr->himlSmall &&
2051 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
2052 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
2053 Icon.right += infoPtr->iconSize.cx;
2054 Icon.bottom = Icon.top + infoPtr->iconSize.cy;
2056 if(lprcIcon) *lprcIcon = Icon;
2057 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon));
2059 /* TODO: is this correct? */
2062 lprcStateIcon->left = Icon.left - state_width;
2063 lprcStateIcon->right = Icon.left;
2064 lprcStateIcon->top = Icon.top;
2065 lprcStateIcon->bottom = lprcStateIcon->top + infoPtr->iconSize.cy;
2066 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon));
2069 else Icon.right = 0;
2071 /************************************************************/
2072 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2073 /************************************************************/
2076 /* calculate how far to the right can the label stretch */
2077 Label.right = Box.right;
2078 if (uView == LVS_REPORT)
2080 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
2083 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
2085 labelSize.cx = infoPtr->nItemWidth;
2086 labelSize.cy = infoPtr->nItemHeight;
2090 /* we need the text in non owner draw mode */
2091 assert(lpLVItem->mask & LVIF_TEXT);
2092 if (is_textT(lpLVItem->pszText, TRUE))
2094 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2095 HDC hdc = GetDC(infoPtr->hwndSelf);
2096 HFONT hOldFont = SelectObject(hdc, hFont);
2100 /* compute rough rectangle where the label will go */
2101 SetRectEmpty(&rcText);
2102 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2103 rcText.bottom = infoPtr->nItemHeight;
2104 if (uView == LVS_ICON)
2105 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2107 /* now figure out the flags */
2108 if (uView == LVS_ICON)
2109 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2111 uFormat = LV_SL_DT_FLAGS;
2113 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2115 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2116 labelSize.cy = rcText.bottom - rcText.top;
2118 SelectObject(hdc, hOldFont);
2119 ReleaseDC(infoPtr->hwndSelf, hdc);
2123 if (uView == LVS_ICON)
2125 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2126 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2127 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2128 Label.right = Label.left + labelSize.cx;
2129 Label.bottom = Label.top + infoPtr->nItemHeight;
2130 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2132 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2133 labelSize.cy /= infoPtr->ntmHeight;
2134 labelSize.cy = max(labelSize.cy, 1);
2135 labelSize.cy *= infoPtr->ntmHeight;
2137 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2139 else if (uView == LVS_REPORT)
2141 Label.left = Icon.right;
2142 Label.top = Box.top;
2143 Label.right = lpColumnInfo->rcHeader.right;
2144 Label.bottom = Label.top + infoPtr->nItemHeight;
2146 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2148 Label.left = Icon.right;
2149 Label.top = Box.top;
2150 Label.right = min(Label.left + labelSize.cx, Label.right);
2151 Label.bottom = Label.top + infoPtr->nItemHeight;
2154 if (lprcLabel) *lprcLabel = Label;
2155 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label));
2158 /************************************************************/
2159 /* compute STATEICON bounding box */
2160 /************************************************************/
2163 if (uView == LVS_REPORT)
2165 SelectBox.left = Icon.right; /* FIXME: should be Icon.left */
2166 SelectBox.top = Box.top;
2167 SelectBox.bottom = Box.bottom;
2168 if (lpLVItem->iSubItem == 0)
2170 /* we need the indent in report mode */
2171 assert(lpLVItem->mask & LVIF_INDENT);
2172 SelectBox.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
2174 SelectBox.right = min(SelectBox.left + labelSize.cx, Label.right);
2178 UnionRect(&SelectBox, &Icon, &Label);
2180 if (lprcSelectBox) *lprcSelectBox = SelectBox;
2181 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox));
2184 /* Fix the Box if necessary */
2187 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2188 else *lprcBox = Box;
2190 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box));
2194 * DESCRIPTION: [INTERNAL]
2197 * [I] infoPtr : valid pointer to the listview structure
2198 * [I] nItem : item number
2199 * [O] lprcBox : ptr to Box rectangle
2204 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2206 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2207 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2208 POINT Position, Origin;
2211 LISTVIEW_GetOrigin(infoPtr, &Origin);
2212 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2214 /* Be smart and try to figure out the minimum we have to do */
2216 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2217 lvItem.mask |= LVIF_TEXT;
2218 lvItem.iItem = nItem;
2219 lvItem.iSubItem = 0;
2220 lvItem.pszText = szDispText;
2221 lvItem.cchTextMax = DISP_TEXT_SIZE;
2222 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2223 if (uView == LVS_ICON)
2225 lvItem.mask |= LVIF_STATE;
2226 lvItem.stateMask = LVIS_FOCUSED;
2227 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2229 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0, 0);
2231 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2237 * Returns the current icon position, and advances it along the top.
2238 * The returned position is not offset by Origin.
2241 * [I] infoPtr : valid pointer to the listview structure
2242 * [O] lpPos : will get the current icon position
2247 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2249 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2251 *lpPos = infoPtr->currIconPos;
2253 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2254 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2256 infoPtr->currIconPos.x = 0;
2257 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2263 * Returns the current icon position, and advances it down the left edge.
2264 * The returned position is not offset by Origin.
2267 * [I] infoPtr : valid pointer to the listview structure
2268 * [O] lpPos : will get the current icon position
2273 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2275 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2277 *lpPos = infoPtr->currIconPos;
2279 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2280 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2282 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2283 infoPtr->currIconPos.y = 0;
2289 * Moves an icon to the specified position.
2290 * It takes care of invalidating the item, etc.
2293 * [I] infoPtr : valid pointer to the listview structure
2294 * [I] nItem : the item to move
2295 * [I] lpPos : the new icon position
2296 * [I] isNew : flags the item as being new
2302 static BOOL LISTVIEW_MoveIconTo(const LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2308 old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2309 old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2311 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2312 LISTVIEW_InvalidateItem(infoPtr, nItem);
2315 /* Allocating a POINTER for every item is too resource intensive,
2316 * so we'll keep the (x,y) in different arrays */
2317 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2318 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2320 LISTVIEW_InvalidateItem(infoPtr, nItem);
2327 * Arranges listview items in icon display mode.
2330 * [I] infoPtr : valid pointer to the listview structure
2331 * [I] nAlignCode : alignment code
2337 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2339 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2340 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2344 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2346 TRACE("nAlignCode=%d\n", nAlignCode);
2348 if (nAlignCode == LVA_DEFAULT)
2350 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2351 else nAlignCode = LVA_ALIGNTOP;
2356 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2357 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2358 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2359 default: return FALSE;
2362 infoPtr->bAutoarrange = TRUE;
2363 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2364 for (i = 0; i < infoPtr->nItemCount; i++)
2366 next_pos(infoPtr, &pos);
2367 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2375 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2378 * [I] infoPtr : valid pointer to the listview structure
2379 * [O] lprcView : bounding rectangle
2385 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2389 SetRectEmpty(lprcView);
2391 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2395 for (i = 0; i < infoPtr->nItemCount; i++)
2397 x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2398 y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2399 lprcView->right = max(lprcView->right, x);
2400 lprcView->bottom = max(lprcView->bottom, y);
2402 if (infoPtr->nItemCount > 0)
2404 lprcView->right += infoPtr->nItemWidth;
2405 lprcView->bottom += infoPtr->nItemHeight;
2410 y = LISTVIEW_GetCountPerColumn(infoPtr);
2411 x = infoPtr->nItemCount / y;
2412 if (infoPtr->nItemCount % y) x++;
2413 lprcView->right = x * infoPtr->nItemWidth;
2414 lprcView->bottom = y * infoPtr->nItemHeight;
2418 lprcView->right = infoPtr->nItemWidth;
2419 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2426 * Retrieves the bounding rectangle of all the items.
2429 * [I] infoPtr : valid pointer to the listview structure
2430 * [O] lprcView : bounding rectangle
2436 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2440 TRACE("(lprcView=%p)\n", lprcView);
2442 if (!lprcView) return FALSE;
2444 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2445 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2446 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2448 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView));
2455 * Retrieves the subitem pointer associated with the subitem index.
2458 * [I] hdpaSubItems : DPA handle for a specific item
2459 * [I] nSubItem : index of subitem
2462 * SUCCESS : subitem pointer
2465 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2467 SUBITEM_INFO *lpSubItem;
2470 /* we should binary search here if need be */
2471 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2473 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
2474 if (lpSubItem->iSubItem == nSubItem)
2484 * Calculates the desired item width.
2487 * [I] infoPtr : valid pointer to the listview structure
2490 * The desired item width.
2492 static INT LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO *infoPtr)
2494 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2497 TRACE("uView=%d\n", uView);
2499 if (uView == LVS_ICON)
2500 nItemWidth = infoPtr->iconSpacing.cx;
2501 else if (uView == LVS_REPORT)
2505 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2507 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2508 nItemWidth = rcHeader.right;
2511 else /* LVS_SMALLICON, or LVS_LIST */
2515 for (i = 0; i < infoPtr->nItemCount; i++)
2516 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2518 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2519 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2521 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2524 return max(nItemWidth, 1);
2529 * Calculates the desired item height.
2532 * [I] infoPtr : valid pointer to the listview structure
2535 * The desired item height.
2537 static INT LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO *infoPtr)
2539 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2542 TRACE("uView=%d\n", uView);
2544 if (uView == LVS_ICON)
2545 nItemHeight = infoPtr->iconSpacing.cy;
2548 nItemHeight = infoPtr->ntmHeight;
2549 if (uView == LVS_REPORT && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
2551 if (infoPtr->himlState)
2552 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2553 if (infoPtr->himlSmall)
2554 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2555 if (infoPtr->himlState || infoPtr->himlSmall)
2556 nItemHeight += HEIGHT_PADDING;
2557 if (infoPtr->nMeasureItemHeight > 0)
2558 nItemHeight = infoPtr->nMeasureItemHeight;
2561 return max(nItemHeight, 1);
2566 * Updates the width, and height of an item.
2569 * [I] infoPtr : valid pointer to the listview structure
2574 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2576 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2577 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2583 * Retrieves and saves important text metrics info for the current
2587 * [I] infoPtr : valid pointer to the listview structure
2590 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2592 HDC hdc = GetDC(infoPtr->hwndSelf);
2593 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2594 HFONT hOldFont = SelectObject(hdc, hFont);
2598 if (GetTextMetricsW(hdc, &tm))
2600 infoPtr->ntmHeight = tm.tmHeight;
2601 infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
2604 if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
2605 infoPtr->nEllipsisWidth = sz.cx;
2607 SelectObject(hdc, hOldFont);
2608 ReleaseDC(infoPtr->hwndSelf, hdc);
2610 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2615 * A compare function for ranges
2618 * [I] range1 : pointer to range 1;
2619 * [I] range2 : pointer to range 2;
2623 * > 0 : if range 1 > range 2
2624 * < 0 : if range 2 > range 1
2625 * = 0 : if range intersects range 2
2627 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2631 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2633 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2638 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1), debugrange(range2), cmp);
2644 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2646 #define ranges_check(ranges, desc) do { } while(0)
2649 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2654 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2656 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2657 ranges_dump(ranges);
2658 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2660 prev = DPA_GetPtr(ranges->hdpa, 0);
2661 assert (prev->lower >= 0 && prev->lower < prev->upper);
2662 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2664 curr = DPA_GetPtr(ranges->hdpa, i);
2665 assert (prev->upper <= curr->lower);
2666 assert (curr->lower < curr->upper);
2670 TRACE("--- Done checking---\n");
2673 static RANGES ranges_create(int count)
2675 RANGES ranges = Alloc(sizeof(struct tagRANGES));
2676 if (!ranges) return NULL;
2677 ranges->hdpa = DPA_Create(count);
2678 if (ranges->hdpa) return ranges;
2683 static void ranges_clear(RANGES ranges)
2687 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2688 Free(DPA_GetPtr(ranges->hdpa, i));
2689 DPA_DeleteAllPtrs(ranges->hdpa);
2693 static void ranges_destroy(RANGES ranges)
2695 if (!ranges) return;
2696 ranges_clear(ranges);
2697 DPA_Destroy(ranges->hdpa);
2701 static RANGES ranges_clone(RANGES ranges)
2706 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2708 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2710 RANGE *newrng = Alloc(sizeof(RANGE));
2711 if (!newrng) goto fail;
2712 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2713 DPA_SetPtr(clone->hdpa, i, newrng);
2718 TRACE ("clone failed\n");
2719 ranges_destroy(clone);
2723 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2727 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2728 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2733 static void ranges_dump(RANGES ranges)
2737 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2738 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2741 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2743 RANGE srchrng = { nItem, nItem + 1 };
2745 TRACE("(nItem=%d)\n", nItem);
2746 ranges_check(ranges, "before contain");
2747 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2750 static INT ranges_itemcount(RANGES ranges)
2754 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2756 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2757 count += sel->upper - sel->lower;
2763 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2765 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2768 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2769 if (index == -1) return TRUE;
2771 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2773 chkrng = DPA_GetPtr(ranges->hdpa, index);
2774 if (chkrng->lower >= nItem)
2775 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2776 if (chkrng->upper > nItem)
2777 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2782 static BOOL ranges_add(RANGES ranges, RANGE range)
2787 TRACE("(%s)\n", debugrange(&range));
2788 ranges_check(ranges, "before add");
2790 /* try find overlapping regions first */
2791 srchrgn.lower = range.lower - 1;
2792 srchrgn.upper = range.upper + 1;
2793 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2799 TRACE("Adding new range\n");
2801 /* create the brand new range to insert */
2802 newrgn = Alloc(sizeof(RANGE));
2803 if(!newrgn) goto fail;
2806 /* figure out where to insert it */
2807 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2808 TRACE("index=%d\n", index);
2809 if (index == -1) index = 0;
2811 /* and get it over with */
2812 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2820 RANGE *chkrgn, *mrgrgn;
2821 INT fromindex, mergeindex;
2823 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2824 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2826 chkrgn->lower = min(range.lower, chkrgn->lower);
2827 chkrgn->upper = max(range.upper, chkrgn->upper);
2829 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2831 /* merge now common ranges */
2833 srchrgn.lower = chkrgn->lower - 1;
2834 srchrgn.upper = chkrgn->upper + 1;
2838 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2839 if (mergeindex == -1) break;
2840 if (mergeindex == index)
2842 fromindex = index + 1;
2846 TRACE("Merge with index %i\n", mergeindex);
2848 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2849 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2850 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2852 DPA_DeletePtr(ranges->hdpa, mergeindex);
2853 if (mergeindex < index) index --;
2857 ranges_check(ranges, "after add");
2861 ranges_check(ranges, "failed add");
2865 static BOOL ranges_del(RANGES ranges, RANGE range)
2870 TRACE("(%s)\n", debugrange(&range));
2871 ranges_check(ranges, "before del");
2873 /* we don't use DPAS_SORTED here, since we need *
2874 * to find the first overlapping range */
2875 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2878 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2880 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2882 /* case 1: Same range */
2883 if ( (chkrgn->upper == range.upper) &&
2884 (chkrgn->lower == range.lower) )
2886 DPA_DeletePtr(ranges->hdpa, index);
2889 /* case 2: engulf */
2890 else if ( (chkrgn->upper <= range.upper) &&
2891 (chkrgn->lower >= range.lower) )
2893 DPA_DeletePtr(ranges->hdpa, index);
2895 /* case 3: overlap upper */
2896 else if ( (chkrgn->upper <= range.upper) &&
2897 (chkrgn->lower < range.lower) )
2899 chkrgn->upper = range.lower;
2901 /* case 4: overlap lower */
2902 else if ( (chkrgn->upper > range.upper) &&
2903 (chkrgn->lower >= range.lower) )
2905 chkrgn->lower = range.upper;
2908 /* case 5: fully internal */
2911 RANGE tmprgn = *chkrgn, *newrgn;
2913 if (!(newrgn = Alloc(sizeof(RANGE)))) goto fail;
2914 newrgn->lower = chkrgn->lower;
2915 newrgn->upper = range.lower;
2916 chkrgn->lower = range.upper;
2917 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2926 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2929 ranges_check(ranges, "after del");
2933 ranges_check(ranges, "failed del");
2939 * Removes all selection ranges
2942 * [I] infoPtr : valid pointer to the listview structure
2943 * [I] toSkip : item range to skip removing the selection
2949 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2958 lvItem.stateMask = LVIS_SELECTED;
2960 /* need to clone the DPA because callbacks can change it */
2961 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2962 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2963 while(iterator_next(&i))
2964 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2965 /* note that the iterator destructor will free the cloned range */
2966 iterator_destroy(&i);
2971 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2975 if (!(toSkip = ranges_create(1))) return FALSE;
2976 if (nItem != -1) ranges_additem(toSkip, nItem);
2977 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2978 ranges_destroy(toSkip);
2982 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2984 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2989 * Retrieves the number of items that are marked as selected.
2992 * [I] infoPtr : valid pointer to the listview structure
2995 * Number of items selected.
2997 static INT LISTVIEW_GetSelectedCount(const LISTVIEW_INFO *infoPtr)
2999 INT nSelectedCount = 0;
3001 if (infoPtr->uCallbackMask & LVIS_SELECTED)
3004 for (i = 0; i < infoPtr->nItemCount; i++)
3006 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
3011 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
3013 TRACE("nSelectedCount=%d\n", nSelectedCount);
3014 return nSelectedCount;
3019 * Manages the item focus.
3022 * [I] infoPtr : valid pointer to the listview structure
3023 * [I] nItem : item index
3026 * TRUE : focused item changed
3027 * FALSE : focused item has NOT changed
3029 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
3031 INT oldFocus = infoPtr->nFocusedItem;
3034 if (nItem == infoPtr->nFocusedItem) return FALSE;
3036 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
3037 lvItem.stateMask = LVIS_FOCUSED;
3038 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
3040 return oldFocus != infoPtr->nFocusedItem;
3043 /* Helper function for LISTVIEW_ShiftIndices *only* */
3044 static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
3046 if (nShiftItem < nItem) return nShiftItem;
3048 if (nShiftItem > nItem) return nShiftItem + direction;
3050 if (direction > 0) return nShiftItem + direction;
3052 return min(nShiftItem, infoPtr->nItemCount - 1);
3057 * Updates the various indices after an item has been inserted or deleted.
3060 * [I] infoPtr : valid pointer to the listview structure
3061 * [I] nItem : item index
3062 * [I] direction : Direction of shift, +1 or -1.
3067 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
3072 /* temporarily disable change notification while shifting items */
3073 bOldChange = infoPtr->bDoChangeNotify;
3074 infoPtr->bDoChangeNotify = FALSE;
3076 TRACE("Shifting %iu, %i steps\n", nItem, direction);
3078 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
3080 assert(abs(direction) == 1);
3082 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
3084 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
3085 if (nNewFocus != infoPtr->nFocusedItem)
3086 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
3088 /* But we are not supposed to modify nHotItem! */
3090 infoPtr->bDoChangeNotify = bOldChange;
3096 * Adds a block of selections.
3099 * [I] infoPtr : valid pointer to the listview structure
3100 * [I] nItem : item index
3103 * Whether the window is still valid.
3105 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3107 INT nFirst = min(infoPtr->nSelectionMark, nItem);
3108 INT nLast = max(infoPtr->nSelectionMark, nItem);
3109 HWND hwndSelf = infoPtr->hwndSelf;
3110 NMLVODSTATECHANGE nmlv;
3115 /* Temporarily disable change notification
3116 * If the control is LVS_OWNERDATA, we need to send
3117 * only one LVN_ODSTATECHANGED notification.
3118 * See MSDN documentation for LVN_ITEMCHANGED.
3120 bOldChange = infoPtr->bDoChangeNotify;
3121 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3123 if (nFirst == -1) nFirst = nItem;
3125 item.state = LVIS_SELECTED;
3126 item.stateMask = LVIS_SELECTED;
3128 for (i = nFirst; i <= nLast; i++)
3129 LISTVIEW_SetItemState(infoPtr,i,&item);
3131 ZeroMemory(&nmlv, sizeof(nmlv));
3132 nmlv.iFrom = nFirst;
3135 nmlv.uOldState = item.state;
3137 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3138 if (!IsWindow(hwndSelf))
3140 infoPtr->bDoChangeNotify = bOldChange;
3147 * Sets a single group selection.
3150 * [I] infoPtr : valid pointer to the listview structure
3151 * [I] nItem : item index
3156 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3158 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3164 if (!(selection = ranges_create(100))) return;
3166 item.state = LVIS_SELECTED;
3167 item.stateMask = LVIS_SELECTED;
3169 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3171 if (infoPtr->nSelectionMark == -1)
3173 infoPtr->nSelectionMark = nItem;
3174 ranges_additem(selection, nItem);
3180 sel.lower = min(infoPtr->nSelectionMark, nItem);
3181 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3182 ranges_add(selection, sel);
3187 RECT rcItem, rcSel, rcSelMark;
3190 rcItem.left = LVIR_BOUNDS;
3191 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3192 rcSelMark.left = LVIR_BOUNDS;
3193 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3194 UnionRect(&rcSel, &rcItem, &rcSelMark);
3195 iterator_frameditems(&i, infoPtr, &rcSel);
3196 while(iterator_next(&i))
3198 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3199 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3201 iterator_destroy(&i);
3204 /* disable per item notifications on LVS_OWNERDATA style
3205 FIXME: single LVN_ODSTATECHANGED should be used */
3206 bOldChange = infoPtr->bDoChangeNotify;
3207 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3209 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3212 iterator_rangesitems(&i, selection);
3213 while(iterator_next(&i))
3214 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3215 /* this will also destroy the selection */
3216 iterator_destroy(&i);
3218 infoPtr->bDoChangeNotify = bOldChange;
3220 LISTVIEW_SetItemFocus(infoPtr, nItem);
3225 * Sets a single selection.
3228 * [I] infoPtr : valid pointer to the listview structure
3229 * [I] nItem : item index
3234 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3238 TRACE("nItem=%d\n", nItem);
3240 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3242 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3243 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3244 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3246 infoPtr->nSelectionMark = nItem;
3251 * Set selection(s) with keyboard.
3254 * [I] infoPtr : valid pointer to the listview structure
3255 * [I] nItem : item index
3256 * [I] space : VK_SPACE code sent
3259 * SUCCESS : TRUE (needs to be repainted)
3260 * FAILURE : FALSE (nothing has changed)
3262 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem, BOOL space)
3264 /* FIXME: pass in the state */
3265 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3266 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3267 BOOL bResult = FALSE;
3269 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3270 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3272 if (infoPtr->dwStyle & LVS_SINGLESEL)
3275 LISTVIEW_SetSelection(infoPtr, nItem);
3282 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3287 lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3288 lvItem.stateMask = LVIS_SELECTED;
3291 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3292 if (lvItem.state & LVIS_SELECTED)
3293 infoPtr->nSelectionMark = nItem;
3295 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3300 LISTVIEW_SetSelection(infoPtr, nItem);
3303 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3306 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3310 static BOOL LISTVIEW_GetItemAtPt(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3312 LVHITTESTINFO lvHitTestInfo;
3314 ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3315 lvHitTestInfo.pt.x = pt.x;
3316 lvHitTestInfo.pt.y = pt.y;
3318 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3320 lpLVItem->mask = LVIF_PARAM;
3321 lpLVItem->iItem = lvHitTestInfo.iItem;
3322 lpLVItem->iSubItem = 0;
3324 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3327 static inline BOOL LISTVIEW_isHotTracking(const LISTVIEW_INFO *infoPtr)
3329 return ((infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) ||
3330 (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE) ||
3331 (infoPtr->dwLvExStyle & LVS_EX_TWOCLICKACTIVATE));
3336 * Called when the mouse is being actively tracked and has hovered for a specified
3340 * [I] infoPtr : valid pointer to the listview structure
3341 * [I] fwKeys : key indicator
3342 * [I] x,y : mouse position
3345 * 0 if the message was processed, non-zero if there was an error
3348 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3349 * over the item for a certain period of time.
3352 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3354 if (LISTVIEW_isHotTracking(infoPtr))
3362 if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3363 LISTVIEW_SetSelection(infoPtr, item.iItem);
3371 * Called whenever WM_MOUSEMOVE is received.
3374 * [I] infoPtr : valid pointer to the listview structure
3375 * [I] fwKeys : key indicator
3376 * [I] x,y : mouse position
3379 * 0 if the message is processed, non-zero if there was an error
3381 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3383 TRACKMOUSEEVENT trackinfo;
3385 if (!(fwKeys & MK_LBUTTON))
3386 infoPtr->bLButtonDown = FALSE;
3388 if (infoPtr->bLButtonDown)
3392 LVHITTESTINFO lvHitTestInfo;
3393 WORD wDragWidth = GetSystemMetrics(SM_CXDRAG);
3394 WORD wDragHeight= GetSystemMetrics(SM_CYDRAG);
3396 rect.left = infoPtr->ptClickPos.x - wDragWidth;
3397 rect.right = infoPtr->ptClickPos.x + wDragWidth;
3398 rect.top = infoPtr->ptClickPos.y - wDragHeight;
3399 rect.bottom = infoPtr->ptClickPos.y + wDragHeight;
3404 lvHitTestInfo.pt = tmp;
3405 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3407 /* reset item marker */
3408 if (infoPtr->nLButtonDownItem != lvHitTestInfo.iItem)
3409 infoPtr->nLButtonDownItem = -1;
3411 if (!PtInRect(&rect, tmp))
3415 /* this path covers the following:
3416 1. WM_LBUTTONDOWN over selected item (sets focus on it)
3417 2. change focus with keys
3418 3. move mouse over item from step 1 selects it and moves focus on it */
3419 if (infoPtr->nLButtonDownItem != -1 &&
3420 !LISTVIEW_GetItemState(infoPtr, infoPtr->nLButtonDownItem, LVIS_SELECTED))
3424 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3425 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3427 LISTVIEW_SetItemState(infoPtr, infoPtr->nLButtonDownItem, &lvItem);
3428 infoPtr->nLButtonDownItem = -1;
3431 lvHitTestInfo.pt = infoPtr->ptClickPos;
3432 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3434 ZeroMemory(&nmlv, sizeof(nmlv));
3435 nmlv.iItem = lvHitTestInfo.iItem;
3436 nmlv.ptAction = infoPtr->ptClickPos;
3438 if (!infoPtr->bDragging)
3440 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3441 infoPtr->bDragging = TRUE;
3448 /* see if we are supposed to be tracking mouse hovering */
3449 if (LISTVIEW_isHotTracking(infoPtr)) {
3450 /* fill in the trackinfo struct */
3451 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3452 trackinfo.dwFlags = TME_QUERY;
3453 trackinfo.hwndTrack = infoPtr->hwndSelf;
3454 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3456 /* see if we are already tracking this hwnd */
3457 _TrackMouseEvent(&trackinfo);
3459 if(!(trackinfo.dwFlags & TME_HOVER)) {
3460 trackinfo.dwFlags = TME_HOVER;
3462 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3463 _TrackMouseEvent(&trackinfo);
3472 * Tests whether the item is assignable to a list with style lStyle
3474 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3476 if ( (lpLVItem->mask & LVIF_TEXT) &&
3477 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3478 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3486 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3489 * [I] infoPtr : valid pointer to the listview structure
3490 * [I] lpLVItem : valid pointer to new item attributes
3491 * [I] isNew : the item being set is being inserted
3492 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3493 * [O] bChanged : will be set to TRUE if the item really changed
3499 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3501 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3506 /* stateMask is ignored for LVM_INSERTITEM */
3507 UINT stateMask = isNew ? ~0 : lpLVItem->stateMask;
3511 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3513 if (lpLVItem->mask == 0) return TRUE;
3515 if (infoPtr->dwStyle & LVS_OWNERDATA)
3517 /* a virtual listview only stores selection and focus */
3518 if (lpLVItem->mask & ~LVIF_STATE)
3524 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3525 lpItem = DPA_GetPtr(hdpaSubItems, 0);
3529 /* we need to get the lParam and state of the item */
3530 item.iItem = lpLVItem->iItem;
3531 item.iSubItem = lpLVItem->iSubItem;
3532 item.mask = LVIF_STATE | LVIF_PARAM;
3533 item.stateMask = (infoPtr->dwStyle & LVS_OWNERDATA) ? LVIS_FOCUSED | LVIS_SELECTED : ~0;
3537 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3539 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3540 /* determine what fields will change */
3541 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & stateMask & ~infoPtr->uCallbackMask))
3542 uChanged |= LVIF_STATE;
3544 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3545 uChanged |= LVIF_IMAGE;
3547 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3548 uChanged |= LVIF_PARAM;
3550 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3551 uChanged |= LVIF_INDENT;
3553 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3554 uChanged |= LVIF_TEXT;
3556 TRACE("uChanged=0x%x\n", uChanged);
3557 if (!uChanged) return TRUE;
3560 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3561 nmlv.iItem = lpLVItem->iItem;
3562 nmlv.uNewState = (item.state & ~stateMask) | (lpLVItem->state & stateMask);
3563 nmlv.uOldState = item.state;
3564 nmlv.uChanged = uChanged;
3565 nmlv.lParam = item.lParam;
3567 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3568 /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
3570 if(lpItem && !isNew && infoPtr->bDoChangeNotify)
3572 HWND hwndSelf = infoPtr->hwndSelf;
3574 if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3576 if (!IsWindow(hwndSelf))
3580 /* copy information */
3581 if (lpLVItem->mask & LVIF_TEXT)
3582 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3584 if (lpLVItem->mask & LVIF_IMAGE)
3585 lpItem->hdr.iImage = lpLVItem->iImage;
3587 if (lpLVItem->mask & LVIF_PARAM)
3588 lpItem->lParam = lpLVItem->lParam;
3590 if (lpLVItem->mask & LVIF_INDENT)
3591 lpItem->iIndent = lpLVItem->iIndent;
3593 if (uChanged & LVIF_STATE)
3595 if (lpItem && (stateMask & ~infoPtr->uCallbackMask))
3597 lpItem->state &= ~stateMask;
3598 lpItem->state |= (lpLVItem->state & stateMask);
3600 if (lpLVItem->state & stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3602 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3603 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3605 else if (stateMask & LVIS_SELECTED)
3607 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3609 /* if we are asked to change focus, and we manage it, do it */
3610 if (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3612 if (lpLVItem->state & LVIS_FOCUSED)
3614 if (infoPtr->nFocusedItem != -1)
3616 /* remove current focus */
3617 item.mask = LVIF_STATE;
3619 item.stateMask = LVIS_FOCUSED;
3621 /* recurse with redrawing an item */
3622 LISTVIEW_SetItemState(infoPtr, infoPtr->nFocusedItem, &item);
3625 infoPtr->nFocusedItem = lpLVItem->iItem;
3626 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3628 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3630 infoPtr->nFocusedItem = -1;
3635 /* if we're inserting the item, we're done */
3636 if (isNew) return TRUE;
3638 /* send LVN_ITEMCHANGED notification */
3639 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3640 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3647 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3650 * [I] infoPtr : valid pointer to the listview structure
3651 * [I] lpLVItem : valid pointer to new subitem attributes
3652 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3653 * [O] bChanged : will be set to TRUE if the item really changed
3659 static BOOL set_sub_item(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3662 SUBITEM_INFO *lpSubItem;
3664 /* we do not support subitems for virtual listviews */
3665 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3667 /* set subitem only if column is present */
3668 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3670 /* First do some sanity checks */
3671 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
3672 particularly useful. We currently do not actually do anything with
3673 the flag on subitems.
3675 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)) return FALSE;
3676 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))) return TRUE;
3678 /* get the subitem structure, and create it if not there */
3679 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3680 assert (hdpaSubItems);
3682 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3685 SUBITEM_INFO *tmpSubItem;
3688 lpSubItem = Alloc(sizeof(SUBITEM_INFO));
3689 if (!lpSubItem) return FALSE;
3690 /* we could binary search here, if need be...*/
3691 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3693 tmpSubItem = DPA_GetPtr(hdpaSubItems, i);
3694 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3696 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3701 lpSubItem->iSubItem = lpLVItem->iSubItem;
3702 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3706 if (lpLVItem->mask & LVIF_IMAGE)
3707 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3709 lpSubItem->hdr.iImage = lpLVItem->iImage;
3713 if (lpLVItem->mask & LVIF_TEXT)
3714 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3716 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3725 * Sets item attributes.
3728 * [I] infoPtr : valid pointer to the listview structure
3729 * [I] lpLVItem : new item attributes
3730 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3736 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem, BOOL isW)
3738 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3739 HWND hwndSelf = infoPtr->hwndSelf;
3740 LPWSTR pszText = NULL;
3741 BOOL bResult, bChanged = FALSE;
3743 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3745 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3748 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3749 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3751 pszText = lpLVItem->pszText;
3752 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
3755 /* actually set the fields */
3756 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3758 if (lpLVItem->iSubItem)
3759 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3761 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3762 if (!IsWindow(hwndSelf))
3765 /* redraw item, if necessary */
3766 if (bChanged && !infoPtr->bIsDrawing)
3768 /* this little optimization eliminates some nasty flicker */
3769 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3770 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
3771 lpLVItem->iSubItem > 0 && lpLVItem->iSubItem <= DPA_GetPtrCount(infoPtr->hdpaColumns) )
3772 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3774 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3779 textfreeT(lpLVItem->pszText, isW);
3780 lpLVItem->pszText = pszText;
3788 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3791 * [I] infoPtr : valid pointer to the listview structure
3796 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *infoPtr)
3798 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3800 SCROLLINFO scrollInfo;
3802 scrollInfo.cbSize = sizeof(SCROLLINFO);
3803 scrollInfo.fMask = SIF_POS;
3805 if (uView == LVS_LIST)
3807 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3808 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3810 else if (uView == LVS_REPORT)
3812 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3813 nItem = scrollInfo.nPos;
3817 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3818 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3821 TRACE("nItem=%d\n", nItem);
3829 * Erases the background of the given rectangle
3832 * [I] infoPtr : valid pointer to the listview structure
3833 * [I] hdc : device context handle
3834 * [I] lprcBox : clipping rectangle
3840 static inline BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3842 if (!infoPtr->hBkBrush) return FALSE;
3844 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
3846 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3854 * [I] infoPtr : valid pointer to the listview structure
3855 * [I] hdc : device context handle
3856 * [I] nItem : item index
3857 * [I] nSubItem : subitem index
3858 * [I] pos : item position in client coordinates
3859 * [I] cdmode : custom draw mode
3865 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3867 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3868 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3869 static WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3870 DWORD cdsubitemmode = CDRF_DODEFAULT;
3872 RECT rcSelect, rcBox, rcIcon, rcLabel, rcStateIcon;
3873 NMLVCUSTOMDRAW nmlvcd;
3878 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
3880 /* get information needed for drawing the item */
3881 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
3882 if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
3883 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3884 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3885 lvItem.iItem = nItem;
3886 lvItem.iSubItem = nSubItem;
3889 lvItem.cchTextMax = DISP_TEXT_SIZE;
3890 lvItem.pszText = szDispText;
3891 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3892 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3893 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3894 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
3895 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3897 /* now check if we need to update the focus rectangle */
3898 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3900 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3901 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
3902 OffsetRect(&rcBox, pos.x, pos.y);
3903 OffsetRect(&rcSelect, pos.x, pos.y);
3904 OffsetRect(&rcIcon, pos.x, pos.y);
3905 OffsetRect(&rcStateIcon, pos.x, pos.y);
3906 OffsetRect(&rcLabel, pos.x, pos.y);
3907 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
3908 wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect),
3909 wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
3911 /* fill in the custom draw structure */
3912 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3914 hOldFont = GetCurrentObject(hdc, OBJ_FONT);
3915 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3916 if (cdmode & CDRF_SKIPDEFAULT) goto postpaint;
3917 if (cdmode & CDRF_NOTIFYITEMDRAW)
3918 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3919 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3920 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3921 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3922 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3924 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3925 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3927 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3928 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
3929 else if ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) == FALSE)
3930 prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE);
3932 /* in full row select, subitems, will just use main item's colors */
3933 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3934 nmlvcd.clrTextBk = CLR_NONE;
3937 if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && (nSubItem == 0))
3939 UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
3942 TRACE("uStateImage=%d\n", uStateImage);
3943 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
3944 rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
3949 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3950 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3952 TRACE("iImage=%d\n", lvItem.iImage);
3953 ImageList_DrawEx(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3954 rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk, CLR_DEFAULT,
3955 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3958 /* Don't bother painting item being edited */
3959 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3961 /* FIXME: temporary hack */
3962 rcSelect.left = rcLabel.left;
3964 /* draw the selection background, if we're drawing the main item */
3967 /* in icon mode, the label rect is really what we want to draw the
3969 if (uView == LVS_ICON)
3972 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3973 rcSelect.right = rcBox.right;
3975 if (nmlvcd.clrTextBk != CLR_NONE)
3976 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3977 /* store new focus rectangle */
3978 if (infoPtr->nFocusedItem == nItem) infoPtr->rcFocus = rcSelect;
3981 /* figure out the text drawing flags */
3982 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3983 if (uView == LVS_ICON)
3984 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3987 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3989 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3990 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3991 default: uFormat |= DT_LEFT;
3994 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3996 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3997 else rcLabel.left += LABEL_HOR_PADDING;
3999 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
4001 /* for GRIDLINES reduce the bottom so the text formats correctly */
4002 if (uView == LVS_REPORT && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
4005 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
4008 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
4009 notify_postpaint(infoPtr, &nmlvcd);
4010 if (cdsubitemmode & CDRF_NEWFONT)
4011 SelectObject(hdc, hOldFont);
4017 * Draws listview items when in owner draw mode.
4020 * [I] infoPtr : valid pointer to the listview structure
4021 * [I] hdc : device context handle
4026 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4028 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
4029 DWORD cditemmode = CDRF_DODEFAULT;
4030 NMLVCUSTOMDRAW nmlvcd;
4031 POINT Origin, Position;
4037 ZeroMemory(&dis, sizeof(dis));
4039 /* Get scroll info once before loop */
4040 LISTVIEW_GetOrigin(infoPtr, &Origin);
4042 /* iterate through the invalidated rows */
4043 while(iterator_next(i))
4045 item.iItem = i->nItem;
4047 item.mask = LVIF_PARAM | LVIF_STATE;
4048 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4049 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
4051 dis.CtlType = ODT_LISTVIEW;
4053 dis.itemID = item.iItem;
4054 dis.itemAction = ODA_DRAWENTIRE;
4056 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
4057 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
4058 dis.hwndItem = infoPtr->hwndSelf;
4060 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
4061 dis.rcItem.left = Position.x + Origin.x;
4062 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
4063 dis.rcItem.top = Position.y + Origin.y;
4064 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
4065 dis.itemData = item.lParam;
4067 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
4070 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
4071 * structure for the rest. of the paint cycle
4073 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
4074 if (cdmode & CDRF_NOTIFYITEMDRAW)
4075 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4077 if (!(cditemmode & CDRF_SKIPDEFAULT))
4079 prepaint_setup (infoPtr, hdc, &nmlvcd, FALSE);
4080 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
4083 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
4084 notify_postpaint(infoPtr, &nmlvcd);
4090 * Draws listview items when in report display mode.
4093 * [I] infoPtr : valid pointer to the listview structure
4094 * [I] hdc : device context handle
4095 * [I] cdmode : custom draw mode
4100 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4103 RECT rcClip, rcItem;
4104 POINT Origin, Position;
4110 /* figure out what to draw */
4111 rgntype = GetClipBox(hdc, &rcClip);
4112 if (rgntype == NULLREGION) return;
4114 /* Get scroll info once before loop */
4115 LISTVIEW_GetOrigin(infoPtr, &Origin);
4117 /* narrow down the columns we need to paint */
4118 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
4120 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
4121 if (rcItem.right + Origin.x >= rcClip.left) break;
4123 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
4125 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
4126 if (rcItem.left + Origin.x < rcClip.right) break;
4128 iterator_rangeitems(&j, colRange);
4130 /* in full row select, we _have_ to draw the main item */
4131 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
4134 /* iterate through the invalidated rows */
4135 while(iterator_next(i))
4137 /* iterate through the invalidated columns */
4138 while(iterator_next(&j))
4140 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4141 Position.x += Origin.x;
4142 Position.y += Origin.y;
4144 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
4146 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4148 rcItem.bottom = infoPtr->nItemHeight;
4149 OffsetRect(&rcItem, Position.x, Position.y);
4150 if (!RectVisible(hdc, &rcItem)) continue;
4153 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
4156 iterator_destroy(&j);
4161 * Draws the gridlines if necessary when in report display mode.
4164 * [I] infoPtr : valid pointer to the listview structure
4165 * [I] hdc : device context handle
4170 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO *infoPtr, HDC hdc)
4175 RECT rcClip, rcItem = {0};
4183 /* figure out what to draw */
4184 rgntype = GetClipBox(hdc, &rcClip);
4185 if (rgntype == NULLREGION) return;
4187 /* Get scroll info once before loop */
4188 LISTVIEW_GetOrigin(infoPtr, &Origin);
4190 /* narrow down the columns we need to paint */
4191 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
4193 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
4194 if (rcItem.right + Origin.x >= rcClip.left) break;
4196 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
4198 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
4199 if (rcItem.left + Origin.x < rcClip.right) break;
4201 /* is right most vertical line visible? */
4202 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
4204 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcItem);
4205 rmost = (rcItem.right + Origin.x < rcClip.right);
4208 if ((hPen = CreatePen( PS_SOLID, 1, comctl32_color.clr3dFace )))
4210 hOldPen = SelectObject ( hdc, hPen );
4212 /* draw the vertical lines for the columns */
4213 iterator_rangeitems(&j, colRange);
4214 while(iterator_next(&j))
4216 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4217 if (rcItem.left == 0) continue; /* skip first column */
4218 rcItem.left += Origin.x;
4219 rcItem.right += Origin.x;
4220 rcItem.top = infoPtr->rcList.top;
4221 rcItem.bottom = infoPtr->rcList.bottom;
4222 TRACE("vert col=%d, rcItem=%s\n", j.nItem, wine_dbgstr_rect(&rcItem));
4223 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4224 LineTo (hdc, rcItem.left, rcItem.bottom);
4226 iterator_destroy(&j);
4227 /* draw rightmost grid line if visible */
4230 MoveToEx (hdc, rcItem.right, rcItem.top, NULL);
4231 LineTo (hdc, rcItem.right, rcItem.bottom);
4234 /* draw the horizontial lines for the rows */
4235 itemheight = LISTVIEW_CalculateItemHeight(infoPtr);
4236 rcItem.left = infoPtr->rcList.left;
4237 rcItem.right = infoPtr->rcList.right;
4238 rcItem.bottom = rcItem.top = Origin.y - 1;
4239 MoveToEx(hdc, rcItem.left, rcItem.top, NULL);
4240 LineTo(hdc, rcItem.right, rcItem.top);
4241 for(y=itemheight-1+Origin.y; y<=infoPtr->rcList.bottom; y+=itemheight)
4243 rcItem.bottom = rcItem.top = y;
4244 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem));
4245 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4246 LineTo (hdc, rcItem.right, rcItem.top);
4249 SelectObject( hdc, hOldPen );
4250 DeleteObject( hPen );
4256 * Draws listview items when in list display mode.
4259 * [I] infoPtr : valid pointer to the listview structure
4260 * [I] hdc : device context handle
4261 * [I] cdmode : custom draw mode
4266 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4268 POINT Origin, Position;
4270 /* Get scroll info once before loop */
4271 LISTVIEW_GetOrigin(infoPtr, &Origin);
4273 while(iterator_prev(i))
4275 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4276 Position.x += Origin.x;
4277 Position.y += Origin.y;
4279 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
4286 * Draws listview items.
4289 * [I] infoPtr : valid pointer to the listview structure
4290 * [I] hdc : device context handle
4291 * [I] prcErase : rect to be erased before refresh (may be NULL)
4296 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcErase)
4298 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4299 COLORREF oldTextColor = 0, oldBkColor = 0, oldClrTextBk, oldClrText;
4300 NMLVCUSTOMDRAW nmlvcd;
4307 HBITMAP hbmp = NULL;
4310 LISTVIEW_DUMP(infoPtr);
4312 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4313 TRACE("double buffering\n");
4315 hdc = CreateCompatibleDC(hdcOrig);
4317 ERR("Failed to create DC for backbuffer\n");
4320 hbmp = CreateCompatibleBitmap(hdcOrig, infoPtr->rcList.right,
4321 infoPtr->rcList.bottom);
4323 ERR("Failed to create bitmap for backbuffer\n");
4328 SelectObject(hdc, hbmp);
4329 SelectObject(hdc, infoPtr->hFont);
4331 /* Save dc values we're gonna trash while drawing
4332 * FIXME: Should be done in LISTVIEW_DrawItem() */
4333 hOldFont = SelectObject(hdc, infoPtr->hFont);
4334 oldBkMode = GetBkMode(hdc);
4335 oldBkColor = GetBkColor(hdc);
4336 oldTextColor = GetTextColor(hdc);
4339 infoPtr->bIsDrawing = TRUE;
4342 LISTVIEW_FillBkgnd(infoPtr, hdc, prcErase);
4343 } else if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4344 /* If no erasing was done (usually because RedrawWindow was called
4345 * with RDW_INVALIDATE only) we need to copy the old contents into
4346 * the backbuffer before continuing. */
4347 BitBlt(hdc, infoPtr->rcList.left, infoPtr->rcList.top,
4348 infoPtr->rcList.right - infoPtr->rcList.left,
4349 infoPtr->rcList.bottom - infoPtr->rcList.top,
4350 hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4353 /* FIXME: Shouldn't need to do this */
4354 oldClrTextBk = infoPtr->clrTextBk;
4355 oldClrText = infoPtr->clrText;
4357 infoPtr->cditemmode = CDRF_DODEFAULT;
4359 GetClientRect(infoPtr->hwndSelf, &rcClient);
4360 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4361 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4362 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4363 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4365 /* Use these colors to draw the items */
4366 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4367 infoPtr->clrText = nmlvcd.clrText;
4369 /* nothing to draw */
4370 if(infoPtr->nItemCount == 0) goto enddraw;
4372 /* figure out what we need to draw */
4373 iterator_visibleitems(&i, infoPtr, hdc);
4374 range = iterator_range(&i);
4376 /* send cache hint notification */
4377 if (infoPtr->dwStyle & LVS_OWNERDATA)
4381 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4382 nmlv.iFrom = range.lower;
4383 nmlv.iTo = range.upper - 1;
4384 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4387 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4388 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4391 if (uView == LVS_REPORT)
4392 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4393 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4394 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4396 /* if we have a focus rect and it's visible, draw it */
4397 if (infoPtr->bFocus && range.lower <= infoPtr->nFocusedItem &&
4398 (range.upper - 1) >= infoPtr->nFocusedItem)
4399 LISTVIEW_DrawFocusRect(infoPtr, hdc);
4401 iterator_destroy(&i);
4404 /* For LVS_EX_GRIDLINES go and draw lines */
4405 /* This includes the case where there were *no* items */
4406 if ((uView == LVS_REPORT) && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
4407 LISTVIEW_RefreshReportGrid(infoPtr, hdc);
4409 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4410 notify_postpaint(infoPtr, &nmlvcd);
4412 infoPtr->clrTextBk = oldClrTextBk;
4413 infoPtr->clrText = oldClrText;
4416 BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top,
4417 infoPtr->rcList.right - infoPtr->rcList.left,
4418 infoPtr->rcList.bottom - infoPtr->rcList.top,
4419 hdc, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4424 SelectObject(hdc, hOldFont);
4425 SetBkMode(hdc, oldBkMode);
4426 SetBkColor(hdc, oldBkColor);
4427 SetTextColor(hdc, oldTextColor);
4430 infoPtr->bIsDrawing = FALSE;
4436 * Calculates the approximate width and height of a given number of items.
4439 * [I] infoPtr : valid pointer to the listview structure
4440 * [I] nItemCount : number of items
4441 * [I] wWidth : width
4442 * [I] wHeight : height
4445 * Returns a DWORD. The width in the low word and the height in high word.
4447 static DWORD LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO *infoPtr, INT nItemCount,
4448 WORD wWidth, WORD wHeight)
4450 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4451 INT nItemCountPerColumn = 1;
4452 INT nColumnCount = 0;
4453 DWORD dwViewRect = 0;
4455 if (nItemCount == -1)
4456 nItemCount = infoPtr->nItemCount;
4458 if (uView == LVS_LIST)
4460 if (wHeight == 0xFFFF)
4462 /* use current height */
4463 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4466 if (wHeight < infoPtr->nItemHeight)
4467 wHeight = infoPtr->nItemHeight;
4471 if (infoPtr->nItemHeight > 0)
4473 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4474 if (nItemCountPerColumn == 0)
4475 nItemCountPerColumn = 1;
4477 if (nItemCount % nItemCountPerColumn != 0)
4478 nColumnCount = nItemCount / nItemCountPerColumn;
4480 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4484 /* Microsoft padding magic */
4485 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4486 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4488 dwViewRect = MAKELONG(wWidth, wHeight);
4490 else if (uView == LVS_REPORT)
4494 if (infoPtr->nItemCount > 0)
4496 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4497 wWidth = rcBox.right - rcBox.left;
4498 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4502 /* use current height and width */
4503 if (wHeight == 0xffff)
4504 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4505 if (wWidth == 0xffff)
4506 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4509 dwViewRect = MAKELONG(wWidth, wHeight);
4511 else if (uView == LVS_SMALLICON)
4512 FIXME("uView == LVS_SMALLICON: not implemented\n");
4513 else if (uView == LVS_ICON)
4514 FIXME("uView == LVS_ICON: not implemented\n");
4522 * Create a drag image list for the specified item.
4525 * [I] infoPtr : valid pointer to the listview structure
4526 * [I] iItem : index of item
4527 * [O] lppt : Upper-left corner of the image
4530 * Returns a handle to the image list if successful, NULL otherwise.
4532 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4538 HBITMAP hbmp, hOldbmp;
4539 HIMAGELIST dragList = 0;
4540 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
4542 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4545 rcItem.left = LVIR_BOUNDS;
4546 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4549 lppt->x = rcItem.left;
4550 lppt->y = rcItem.top;
4552 size.cx = rcItem.right - rcItem.left;
4553 size.cy = rcItem.bottom - rcItem.top;
4555 hdcOrig = GetDC(infoPtr->hwndSelf);
4556 hdc = CreateCompatibleDC(hdcOrig);
4557 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4558 hOldbmp = SelectObject(hdc, hbmp);
4560 rcItem.left = rcItem.top = 0;
4561 rcItem.right = size.cx;
4562 rcItem.bottom = size.cy;
4563 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4566 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4568 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4569 SelectObject(hdc, hOldbmp);
4570 ImageList_Add(dragList, hbmp, 0);
4573 SelectObject(hdc, hOldbmp);
4577 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4579 TRACE("ret=%p\n", dragList);
4587 * Removes all listview items and subitems.
4590 * [I] infoPtr : valid pointer to the listview structure
4596 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr, BOOL destroy)
4599 HDPA hdpaSubItems = NULL;
4606 /* we do it directly, to avoid notifications */
4607 ranges_clear(infoPtr->selectionRanges);
4608 infoPtr->nSelectionMark = -1;
4609 infoPtr->nFocusedItem = -1;
4610 SetRectEmpty(&infoPtr->rcFocus);
4611 /* But we are supposed to leave nHotItem as is! */
4614 /* send LVN_DELETEALLITEMS notification */
4615 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4617 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4619 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4621 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4623 /* send LVN_DELETEITEM notification, if not suppressed
4624 and if it is not a virtual listview */
4625 if (!bSuppress) notify_deleteitem(infoPtr, i);
4626 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
4627 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4629 hdrItem = DPA_GetPtr(hdpaSubItems, j);
4630 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4633 DPA_Destroy(hdpaSubItems);
4634 DPA_DeletePtr(infoPtr->hdpaItems, i);
4636 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4637 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4638 infoPtr->nItemCount --;
4643 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4644 LISTVIEW_UpdateScroll(infoPtr);
4646 LISTVIEW_InvalidateList(infoPtr);
4653 * Scrolls, and updates the columns, when a column is changing width.
4656 * [I] infoPtr : valid pointer to the listview structure
4657 * [I] nColumn : column to scroll
4658 * [I] dx : amount of scroll, in pixels
4663 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4665 COLUMN_INFO *lpColumnInfo;
4670 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4671 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4672 rcCol = lpColumnInfo->rcHeader;
4673 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4674 rcCol.left = rcCol.right;
4676 /* adjust the other columns */
4677 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4679 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4680 lpColumnInfo->rcHeader.left += dx;
4681 lpColumnInfo->rcHeader.right += dx;
4684 /* do not update screen if not in report mode */
4685 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4687 /* Need to reset the item width when inserting a new column */
4688 infoPtr->nItemWidth += dx;
4690 LISTVIEW_UpdateScroll(infoPtr);
4691 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4693 /* scroll to cover the deleted column, and invalidate for redraw */
4694 rcOld = infoPtr->rcList;
4695 rcOld.left = ptOrigin.x + rcCol.left + dx;
4696 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4701 * Removes a column from the listview control.
4704 * [I] infoPtr : valid pointer to the listview structure
4705 * [I] nColumn : column index
4711 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4715 TRACE("nColumn=%d\n", nColumn);
4717 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4718 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4720 /* While the MSDN specifically says that column zero should not be deleted,
4721 what actually happens is that the column itself is deleted but no items or subitems
4725 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4727 if (!SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nColumn, 0))
4730 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4731 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4733 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4735 SUBITEM_INFO *lpSubItem, *lpDelItem;
4737 INT nItem, nSubItem, i;
4739 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4741 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
4744 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4746 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
4747 if (lpSubItem->iSubItem == nColumn)
4750 lpDelItem = lpSubItem;
4752 else if (lpSubItem->iSubItem > nColumn)
4754 lpSubItem->iSubItem--;
4758 /* if we found our subitem, zapp it */
4762 if (is_textW(lpDelItem->hdr.pszText))
4763 Free(lpDelItem->hdr.pszText);
4768 /* free dpa memory */
4769 DPA_DeletePtr(hdpaSubItems, nSubItem);
4774 /* update the other column info */
4775 LISTVIEW_UpdateItemSize(infoPtr);
4776 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4777 LISTVIEW_InvalidateList(infoPtr);
4779 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4786 * Invalidates the listview after an item's insertion or deletion.
4789 * [I] infoPtr : valid pointer to the listview structure
4790 * [I] nItem : item index
4791 * [I] dir : -1 if deleting, 1 if inserting
4796 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4798 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4799 INT nPerCol, nItemCol, nItemRow;
4803 /* if we don't refresh, what's the point of scrolling? */
4804 if (!is_redrawing(infoPtr)) return;
4806 assert (abs(dir) == 1);
4808 /* arrange icons if autoarrange is on */
4809 if (is_autoarrange(infoPtr))
4811 BOOL arrange = TRUE;
4812 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4813 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4814 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4817 /* scrollbars need updating */
4818 LISTVIEW_UpdateScroll(infoPtr);
4820 /* figure out the item's position */
4821 if (uView == LVS_REPORT)
4822 nPerCol = infoPtr->nItemCount + 1;
4823 else if (uView == LVS_LIST)
4824 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4825 else /* LVS_ICON, or LVS_SMALLICON */
4828 nItemCol = nItem / nPerCol;
4829 nItemRow = nItem % nPerCol;
4830 LISTVIEW_GetOrigin(infoPtr, &Origin);
4832 /* move the items below up a slot */
4833 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4834 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4835 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4836 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4837 OffsetRect(&rcScroll, Origin.x, Origin.y);
4838 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
4839 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4841 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
4842 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4843 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4846 /* report has only that column, so we're done */
4847 if (uView == LVS_REPORT) return;
4849 /* now for LISTs, we have to deal with the columns to the right */
4850 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4852 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4853 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4854 OffsetRect(&rcScroll, Origin.x, Origin.y);
4855 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4856 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4857 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4862 * Removes an item from the listview control.
4865 * [I] infoPtr : valid pointer to the listview structure
4866 * [I] nItem : item index
4872 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4875 const UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4876 const BOOL is_icon = (uView == LVS_SMALLICON || uView == LVS_ICON);
4878 TRACE("(nItem=%d)\n", nItem);
4880 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4882 /* remove selection, and focus */
4884 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4885 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4887 /* send LVN_DELETEITEM notification. */
4888 if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
4890 /* we need to do this here, because we'll be deleting stuff */
4892 LISTVIEW_InvalidateItem(infoPtr, nItem);
4894 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4900 hdpaSubItems = DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4901 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4903 hdrItem = DPA_GetPtr(hdpaSubItems, i);
4904 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4907 DPA_Destroy(hdpaSubItems);
4912 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4913 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4916 infoPtr->nItemCount--;
4917 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4919 /* now is the invalidation fun */
4921 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4928 * Callback implementation for editlabel control
4931 * [I] infoPtr : valid pointer to the listview structure
4932 * [I] pszText : modified text
4933 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4939 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4941 HWND hwndSelf = infoPtr->hwndSelf;
4942 NMLVDISPINFOW dispInfo;
4943 INT editedItem = infoPtr->nEditLabelItem;
4946 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4948 infoPtr->nEditLabelItem = -1;
4950 ZeroMemory(&dispInfo, sizeof(dispInfo));
4951 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4952 dispInfo.item.iItem = editedItem;
4953 dispInfo.item.iSubItem = 0;
4954 dispInfo.item.stateMask = ~0;
4955 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4958 bSame = (lstrcmpW(dispInfo.item.pszText, pszText) == 0);
4961 LPWSTR tmp = textdupTtoW(pszText, FALSE);
4962 bSame = (lstrcmpW(dispInfo.item.pszText, tmp) == 0);
4963 textfreeT(tmp, FALSE);
4965 if (bSame) return TRUE;
4967 /* add the text from the edit in */
4968 dispInfo.item.mask |= LVIF_TEXT;
4969 dispInfo.item.pszText = pszText;
4970 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4972 /* Do we need to update the Item Text */
4973 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4974 if (!IsWindow(hwndSelf))
4976 if (!pszText) return TRUE;
4978 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4980 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, editedItem);
4981 ITEM_INFO* lpItem = DPA_GetPtr(hdpaSubItems, 0);
4982 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4984 LISTVIEW_InvalidateItem(infoPtr, editedItem);
4989 ZeroMemory(&dispInfo, sizeof(dispInfo));
4990 dispInfo.item.mask = LVIF_TEXT;
4991 dispInfo.item.iItem = editedItem;
4992 dispInfo.item.iSubItem = 0;
4993 dispInfo.item.pszText = pszText;
4994 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4995 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
5000 * Begin in place editing of specified list view item
5003 * [I] infoPtr : valid pointer to the listview structure
5004 * [I] nItem : item index
5005 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
5011 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
5013 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
5014 NMLVDISPINFOW dispInfo;
5016 HWND hwndSelf = infoPtr->hwndSelf;
5018 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
5020 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
5021 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5023 infoPtr->nEditLabelItem = nItem;
5025 /* Is the EditBox still there, if so remove it */
5026 if(infoPtr->hwndEdit != 0)
5028 SetFocus(infoPtr->hwndSelf);
5029 infoPtr->hwndEdit = 0;
5032 LISTVIEW_SetSelection(infoPtr, nItem);
5033 LISTVIEW_SetItemFocus(infoPtr, nItem);
5034 LISTVIEW_InvalidateItem(infoPtr, nItem);
5036 rect.left = LVIR_LABEL;
5037 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
5039 ZeroMemory(&dispInfo, sizeof(dispInfo));
5040 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
5041 dispInfo.item.iItem = nItem;
5042 dispInfo.item.iSubItem = 0;
5043 dispInfo.item.stateMask = ~0;
5044 dispInfo.item.pszText = szDispText;
5045 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
5046 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
5048 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
5049 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
5050 if (!infoPtr->hwndEdit) return 0;
5052 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
5054 if (!IsWindow(hwndSelf))
5056 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
5057 infoPtr->hwndEdit = 0;
5061 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
5062 SetFocus(infoPtr->hwndEdit);
5063 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
5064 return infoPtr->hwndEdit;
5070 * Ensures the specified item is visible, scrolling into view if necessary.
5073 * [I] infoPtr : valid pointer to the listview structure
5074 * [I] nItem : item index
5075 * [I] bPartial : partially or entirely visible
5081 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
5083 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5084 INT nScrollPosHeight = 0;
5085 INT nScrollPosWidth = 0;
5086 INT nHorzAdjust = 0;
5087 INT nVertAdjust = 0;
5090 RECT rcItem, rcTemp;
5092 rcItem.left = LVIR_BOUNDS;
5093 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
5095 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
5097 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
5099 /* scroll left/right, but in LVS_REPORT mode */
5100 if (uView == LVS_LIST)
5101 nScrollPosWidth = infoPtr->nItemWidth;
5102 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5103 nScrollPosWidth = 1;
5105 if (rcItem.left < infoPtr->rcList.left)
5108 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
5113 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
5117 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
5119 /* scroll up/down, but not in LVS_LIST mode */
5120 if (uView == LVS_REPORT)
5121 nScrollPosHeight = infoPtr->nItemHeight;
5122 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
5123 nScrollPosHeight = 1;
5125 if (rcItem.top < infoPtr->rcList.top)
5128 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
5133 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
5137 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
5139 if (nScrollPosWidth)
5141 INT diff = nHorzDiff / nScrollPosWidth;
5142 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
5143 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
5146 if (nScrollPosHeight)
5148 INT diff = nVertDiff / nScrollPosHeight;
5149 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
5150 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
5158 * Searches for an item with specific characteristics.
5161 * [I] hwnd : window handle
5162 * [I] nStart : base item index
5163 * [I] lpFindInfo : item information to look for
5166 * SUCCESS : index of item
5169 static INT LISTVIEW_FindItemW(const LISTVIEW_INFO *infoPtr, INT nStart,
5170 const LVFINDINFOW *lpFindInfo)
5172 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5173 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5174 BOOL bWrap = FALSE, bNearest = FALSE;
5175 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
5176 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
5177 POINT Position, Destination;
5180 /* Search in virtual listviews should be done by application, not by
5181 listview control, so we just send LVN_ODFINDITEMW and return the result */
5182 if (infoPtr->dwStyle & LVS_OWNERDATA)
5186 nmlv.iStart = nStart;
5187 nmlv.lvfi = *lpFindInfo;
5188 return notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
5191 if (!lpFindInfo || nItem < 0) return -1;
5194 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
5196 lvItem.mask |= LVIF_TEXT;
5197 lvItem.pszText = szDispText;
5198 lvItem.cchTextMax = DISP_TEXT_SIZE;
5201 if (lpFindInfo->flags & LVFI_WRAP)
5204 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
5205 (uView == LVS_ICON || uView ==LVS_SMALLICON))
5210 LISTVIEW_GetOrigin(infoPtr, &Origin);
5211 Destination.x = lpFindInfo->pt.x - Origin.x;
5212 Destination.y = lpFindInfo->pt.y - Origin.y;
5213 switch(lpFindInfo->vkDirection)
5215 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
5216 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
5217 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
5218 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
5219 case VK_HOME: Destination.x = Destination.y = 0; break;
5220 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5221 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5223 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
5224 Destination.x = rcArea.right;
5225 Destination.y = rcArea.bottom;
5227 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
5231 else Destination.x = Destination.y = 0;
5233 /* if LVFI_PARAM is specified, all other flags are ignored */
5234 if (lpFindInfo->flags & LVFI_PARAM)
5236 lvItem.mask |= LVIF_PARAM;
5238 lvItem.mask &= ~LVIF_TEXT;
5242 for (; nItem < nLast; nItem++)
5244 lvItem.iItem = nItem;
5245 lvItem.iSubItem = 0;
5246 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
5248 if (lvItem.mask & LVIF_PARAM)
5250 if (lpFindInfo->lParam == lvItem.lParam)
5256 if (lvItem.mask & LVIF_TEXT)
5258 if (lpFindInfo->flags & LVFI_PARTIAL)
5260 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
5264 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
5268 if (!bNearest) return nItem;
5270 /* This is very inefficient. To do a good job here,
5271 * we need a sorted array of (x,y) item positions */
5272 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5274 /* compute the distance^2 to the destination */
5275 xdist = Destination.x - Position.x;
5276 ydist = Destination.y - Position.y;
5277 dist = xdist * xdist + ydist * ydist;
5279 /* remember the distance, and item if it's closer */
5283 nNearestItem = nItem;
5290 nLast = min(nStart + 1, infoPtr->nItemCount);
5295 return nNearestItem;
5300 * Searches for an item with specific characteristics.
5303 * [I] hwnd : window handle
5304 * [I] nStart : base item index
5305 * [I] lpFindInfo : item information to look for
5308 * SUCCESS : index of item
5311 static INT LISTVIEW_FindItemA(const LISTVIEW_INFO *infoPtr, INT nStart,
5312 const LVFINDINFOA *lpFindInfo)
5314 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
5319 memcpy(&fiw, lpFindInfo, sizeof(fiw));
5320 if (hasText) fiw.psz = strW = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
5321 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
5322 textfreeT(strW, FALSE);
5328 * Retrieves the background image of the listview control.
5331 * [I] infoPtr : valid pointer to the listview structure
5332 * [O] lpBkImage : background image attributes
5338 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
5340 /* FIXME (listview, "empty stub!\n"); */
5346 * Retrieves column attributes.
5349 * [I] infoPtr : valid pointer to the listview structure
5350 * [I] nColumn : column index
5351 * [IO] lpColumn : column information
5352 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5353 * otherwise it is in fact a LPLVCOLUMNA
5359 static BOOL LISTVIEW_GetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
5361 COLUMN_INFO *lpColumnInfo;
5364 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5365 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
5367 /* initialize memory */
5368 ZeroMemory(&hdi, sizeof(hdi));
5370 if (lpColumn->mask & LVCF_TEXT)
5372 hdi.mask |= HDI_TEXT;
5373 hdi.pszText = lpColumn->pszText;
5374 hdi.cchTextMax = lpColumn->cchTextMax;
5377 if (lpColumn->mask & LVCF_IMAGE)
5378 hdi.mask |= HDI_IMAGE;
5380 if (lpColumn->mask & LVCF_ORDER)
5381 hdi.mask |= HDI_ORDER;
5383 if (lpColumn->mask & LVCF_SUBITEM)
5384 hdi.mask |= HDI_LPARAM;
5386 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
5388 if (lpColumn->mask & LVCF_FMT)
5389 lpColumn->fmt = lpColumnInfo->fmt;
5391 if (lpColumn->mask & LVCF_WIDTH)
5392 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
5394 if (lpColumn->mask & LVCF_IMAGE)
5395 lpColumn->iImage = hdi.iImage;
5397 if (lpColumn->mask & LVCF_ORDER)
5398 lpColumn->iOrder = hdi.iOrder;
5400 if (lpColumn->mask & LVCF_SUBITEM)
5401 lpColumn->iSubItem = hdi.lParam;
5407 static BOOL LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5409 TRACE("iCount=%d, lpiArray=%p\n", iCount, lpiArray);
5414 return SendMessageW(infoPtr->hwndHeader, HDM_GETORDERARRAY, iCount, (LPARAM)lpiArray);
5419 * Retrieves the column width.
5422 * [I] infoPtr : valid pointer to the listview structure
5423 * [I] int : column index
5426 * SUCCESS : column width
5429 static INT LISTVIEW_GetColumnWidth(const LISTVIEW_INFO *infoPtr, INT nColumn)
5431 INT nColumnWidth = 0;
5434 TRACE("nColumn=%d\n", nColumn);
5436 /* we have a 'column' in LIST and REPORT mode only */
5437 switch(infoPtr->dwStyle & LVS_TYPEMASK)
5440 nColumnWidth = infoPtr->nItemWidth;
5443 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDM_ITEMCHANGED.
5444 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
5445 * HDM_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
5447 * TODO: should we do the same in LVM_GETCOLUMN?
5449 hdItem.mask = HDI_WIDTH;
5450 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdItem))
5452 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr->hwndSelf, nColumn);
5455 nColumnWidth = hdItem.cxy;
5459 TRACE("nColumnWidth=%d\n", nColumnWidth);
5460 return nColumnWidth;
5465 * In list or report display mode, retrieves the number of items that can fit
5466 * vertically in the visible area. In icon or small icon display mode,
5467 * retrieves the total number of visible items.
5470 * [I] infoPtr : valid pointer to the listview structure
5473 * Number of fully visible items.
5475 static INT LISTVIEW_GetCountPerPage(const LISTVIEW_INFO *infoPtr)
5477 switch (infoPtr->dwStyle & LVS_TYPEMASK)
5481 return infoPtr->nItemCount;
5483 return LISTVIEW_GetCountPerColumn(infoPtr);
5485 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5493 * Retrieves an image list handle.
5496 * [I] infoPtr : valid pointer to the listview structure
5497 * [I] nImageList : image list identifier
5500 * SUCCESS : image list handle
5503 static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImageList)
5507 case LVSIL_NORMAL: return infoPtr->himlNormal;
5508 case LVSIL_SMALL: return infoPtr->himlSmall;
5509 case LVSIL_STATE: return infoPtr->himlState;
5514 /* LISTVIEW_GetISearchString */
5518 * Retrieves item attributes.
5521 * [I] hwnd : window handle
5522 * [IO] lpLVItem : item info
5523 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5524 * if FALSE, then lpLVItem is a LPLVITEMA.
5527 * This is the internal 'GetItem' interface -- it tries to
5528 * be smart and avoid text copies, if possible, by modifying
5529 * lpLVItem->pszText to point to the text string. Please note
5530 * that this is not always possible (e.g. OWNERDATA), so on
5531 * entry you *must* supply valid values for pszText, and cchTextMax.
5532 * The only difference to the documented interface is that upon
5533 * return, you should use *only* the lpLVItem->pszText, rather than
5534 * the buffer pointer you provided on input. Most code already does
5535 * that, so it's not a problem.
5536 * For the two cases when the text must be copied (that is,
5537 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5543 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5545 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5546 NMLVDISPINFOW dispInfo;
5552 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5554 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5557 if (lpLVItem->mask == 0) return TRUE;
5559 /* make a local copy */
5560 isubitem = lpLVItem->iSubItem;
5562 /* a quick optimization if all we're asked is the focus state
5563 * these queries are worth optimising since they are common,
5564 * and can be answered in constant time, without the heavy accesses */
5565 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5566 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5568 lpLVItem->state = 0;
5569 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5570 lpLVItem->state |= LVIS_FOCUSED;
5574 ZeroMemory(&dispInfo, sizeof(dispInfo));
5576 /* if the app stores all the data, handle it separately */
5577 if (infoPtr->dwStyle & LVS_OWNERDATA)
5579 dispInfo.item.state = 0;
5581 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5582 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) ||
5583 ((lpLVItem->mask & LVIF_STATE) && (infoPtr->uCallbackMask & lpLVItem->stateMask)))
5585 UINT mask = lpLVItem->mask;
5587 /* NOTE: copy only fields which we _know_ are initialized, some apps
5588 * depend on the uninitialized fields being 0 */
5589 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5590 dispInfo.item.iItem = lpLVItem->iItem;
5591 dispInfo.item.iSubItem = isubitem;
5592 if (lpLVItem->mask & LVIF_TEXT)
5594 if (lpLVItem->mask & LVIF_NORECOMPUTE)
5596 dispInfo.item.mask &= ~(LVIF_TEXT | LVIF_NORECOMPUTE);
5599 dispInfo.item.pszText = lpLVItem->pszText;
5600 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5603 if (lpLVItem->mask & LVIF_STATE)
5604 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5605 /* could be zeroed on LVIF_NORECOMPUTE case */
5606 if (dispInfo.item.mask != 0)
5608 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5609 dispInfo.item.stateMask = lpLVItem->stateMask;
5610 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5612 /* full size structure expected - _WIN32IE >= 0x560 */
5613 *lpLVItem = dispInfo.item;
5615 else if (lpLVItem->mask & LVIF_INDENT)
5617 /* indent member expected - _WIN32IE >= 0x300 */
5618 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5622 /* minimal structure expected */
5623 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5625 lpLVItem->mask = mask;
5626 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5630 /* make sure lParam is zeroed out */
5631 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5633 /* callback marked pointer required here */
5634 if ((lpLVItem->mask & LVIF_TEXT) && (lpLVItem->mask & LVIF_NORECOMPUTE))
5635 lpLVItem->pszText = LPSTR_TEXTCALLBACKW;
5637 /* we store only a little state, so if we're not asked, we're done */
5638 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5640 /* if focus is handled by us, report it */
5641 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5643 lpLVItem->state &= ~LVIS_FOCUSED;
5644 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5645 lpLVItem->state |= LVIS_FOCUSED;
5648 /* and do the same for selection, if we handle it */
5649 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5651 lpLVItem->state &= ~LVIS_SELECTED;
5652 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5653 lpLVItem->state |= LVIS_SELECTED;
5659 /* find the item and subitem structures before we proceed */
5660 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5661 lpItem = DPA_GetPtr(hdpaSubItems, 0);
5666 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5667 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5670 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5675 pItemHdr = &lpItem->hdr;
5677 /* Do we need to query the state from the app? */
5678 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5680 dispInfo.item.mask |= LVIF_STATE;
5681 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5684 /* Do we need to enquire about the image? */
5685 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5686 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5688 dispInfo.item.mask |= LVIF_IMAGE;
5689 dispInfo.item.iImage = I_IMAGECALLBACK;
5692 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5693 if ((lpLVItem->mask & LVIF_TEXT) && !(lpLVItem->mask & LVIF_NORECOMPUTE) &&
5694 !is_textW(pItemHdr->pszText))
5696 dispInfo.item.mask |= LVIF_TEXT;
5697 dispInfo.item.pszText = lpLVItem->pszText;
5698 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5699 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5700 *dispInfo.item.pszText = '\0';
5703 /* If we don't have all the requested info, query the application */
5704 if (dispInfo.item.mask != 0)
5706 dispInfo.item.iItem = lpLVItem->iItem;
5707 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5708 dispInfo.item.lParam = lpItem->lParam;
5709 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5710 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5713 /* we should not store values for subitems */
5714 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5716 /* Now, handle the iImage field */
5717 if (dispInfo.item.mask & LVIF_IMAGE)
5719 lpLVItem->iImage = dispInfo.item.iImage;
5720 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5721 pItemHdr->iImage = dispInfo.item.iImage;
5723 else if (lpLVItem->mask & LVIF_IMAGE)
5725 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5726 lpLVItem->iImage = pItemHdr->iImage;
5728 lpLVItem->iImage = 0;
5731 /* The pszText field */
5732 if (dispInfo.item.mask & LVIF_TEXT)
5734 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5735 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5737 lpLVItem->pszText = dispInfo.item.pszText;
5739 else if (lpLVItem->mask & LVIF_TEXT)
5741 /* if LVN_GETDISPINFO's disabled with LVIF_NORECOMPUTE return callback placeholder */
5742 if (isW || !is_textW(pItemHdr->pszText)) lpLVItem->pszText = pItemHdr->pszText;
5743 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5746 /* Next is the lParam field */
5747 if (dispInfo.item.mask & LVIF_PARAM)
5749 lpLVItem->lParam = dispInfo.item.lParam;
5750 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5751 lpItem->lParam = dispInfo.item.lParam;
5753 else if (lpLVItem->mask & LVIF_PARAM)
5754 lpLVItem->lParam = lpItem->lParam;
5756 /* if this is a subitem, we're done */
5757 if (isubitem) return TRUE;
5759 /* ... the state field (this one is different due to uCallbackmask) */
5760 if (lpLVItem->mask & LVIF_STATE)
5762 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5763 if (dispInfo.item.mask & LVIF_STATE)
5765 lpLVItem->state &= ~dispInfo.item.stateMask;
5766 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5768 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5770 lpLVItem->state &= ~LVIS_FOCUSED;
5771 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5772 lpLVItem->state |= LVIS_FOCUSED;
5774 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5776 lpLVItem->state &= ~LVIS_SELECTED;
5777 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5778 lpLVItem->state |= LVIS_SELECTED;
5782 /* and last, but not least, the indent field */
5783 if (lpLVItem->mask & LVIF_INDENT)
5784 lpLVItem->iIndent = lpItem->iIndent;
5791 * Retrieves item attributes.
5794 * [I] hwnd : window handle
5795 * [IO] lpLVItem : item info
5796 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5797 * if FALSE, then lpLVItem is a LPLVITEMA.
5800 * This is the external 'GetItem' interface -- it properly copies
5801 * the text in the provided buffer.
5807 static BOOL LISTVIEW_GetItemExtT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5812 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5815 pszText = lpLVItem->pszText;
5816 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5817 if (bResult && lpLVItem->pszText != pszText)
5819 if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW)
5820 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5822 pszText = LPSTR_TEXTCALLBACKW;
5824 lpLVItem->pszText = pszText;
5832 * Retrieves the position (upper-left) of the listview control item.
5833 * Note that for LVS_ICON style, the upper-left is that of the icon
5834 * and not the bounding box.
5837 * [I] infoPtr : valid pointer to the listview structure
5838 * [I] nItem : item index
5839 * [O] lpptPosition : coordinate information
5845 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5847 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5850 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5852 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5854 LISTVIEW_GetOrigin(infoPtr, &Origin);
5855 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5857 if (uView == LVS_ICON)
5859 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5860 lpptPosition->y += ICON_TOP_PADDING;
5862 lpptPosition->x += Origin.x;
5863 lpptPosition->y += Origin.y;
5865 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
5872 * Retrieves the bounding rectangle for a listview control item.
5875 * [I] infoPtr : valid pointer to the listview structure
5876 * [I] nItem : item index
5877 * [IO] lprc : bounding rectangle coordinates
5878 * lprc->left specifies the portion of the item for which the bounding
5879 * rectangle will be retrieved.
5881 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5882 * including the icon and label.
5885 * * Experiment shows that native control returns:
5886 * * width = min (48, length of text line)
5887 * * .left = position.x - (width - iconsize.cx)/2
5888 * * .right = .left + width
5889 * * height = #lines of text * ntmHeight + icon height + 8
5890 * * .top = position.y - 2
5891 * * .bottom = .top + height
5892 * * separation between items .y = itemSpacing.cy - height
5893 * * .x = itemSpacing.cx - width
5894 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5897 * * Experiment shows that native control returns:
5898 * * width = iconSize.cx + 16
5899 * * .left = position.x - (width - iconsize.cx)/2
5900 * * .right = .left + width
5901 * * height = iconSize.cy + 4
5902 * * .top = position.y - 2
5903 * * .bottom = .top + height
5904 * * separation between items .y = itemSpacing.cy - height
5905 * * .x = itemSpacing.cx - width
5906 * LVIR_LABEL Returns the bounding rectangle of the item text.
5909 * * Experiment shows that native control returns:
5910 * * width = text length
5911 * * .left = position.x - width/2
5912 * * .right = .left + width
5913 * * height = ntmH * linecount + 2
5914 * * .top = position.y + iconSize.cy + 6
5915 * * .bottom = .top + height
5916 * * separation between items .y = itemSpacing.cy - height
5917 * * .x = itemSpacing.cx - width
5918 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5919 * rectangles, but excludes columns in report view.
5926 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5927 * upon whether the window has the focus currently and on whether the item
5928 * is the one with the focus. Ensure that the control's record of which
5929 * item has the focus agrees with the items' records.
5931 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5933 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5934 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5935 BOOL doLabel = TRUE, oversizedBox = FALSE;
5936 POINT Position, Origin;
5939 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5941 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5943 LISTVIEW_GetOrigin(infoPtr, &Origin);
5944 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5946 /* Be smart and try to figure out the minimum we have to do */
5947 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5948 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5949 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5950 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5951 oversizedBox = TRUE;
5953 /* get what we need from the item before hand, so we make
5954 * only one request. This can speed up things, if data
5955 * is stored on the app side */
5957 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5958 if (doLabel) lvItem.mask |= LVIF_TEXT;
5959 lvItem.iItem = nItem;
5960 lvItem.iSubItem = 0;
5961 lvItem.pszText = szDispText;
5962 lvItem.cchTextMax = DISP_TEXT_SIZE;
5963 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5964 /* we got the state already up, simulate it here, to avoid a reget */
5965 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5967 lvItem.mask |= LVIF_STATE;
5968 lvItem.stateMask = LVIS_FOCUSED;
5969 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5972 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5973 lprc->left = LVIR_BOUNDS;
5977 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
5981 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc);
5985 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
5988 case LVIR_SELECTBOUNDS:
5989 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL);
5993 WARN("Unknown value: %d\n", lprc->left);
5997 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5999 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
6006 * Retrieves the spacing between listview control items.
6009 * [I] infoPtr : valid pointer to the listview structure
6010 * [IO] lprc : rectangle to receive the output
6011 * on input, lprc->top = nSubItem
6012 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
6014 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
6015 * not only those of the first column.
6016 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
6022 static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
6028 if (!lprc) return FALSE;
6030 nColumn = lprc->top;
6032 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
6033 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
6035 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
6037 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
6039 /* special case for header items */
6042 if (lprc->left != LVIR_BOUNDS)
6044 FIXME("Only LVIR_BOUNDS is implemented for header, got %d\n", lprc->left);
6048 if (infoPtr->hwndHeader)
6049 return SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, lprc->top, (LPARAM)lprc);
6052 memset(lprc, 0, sizeof(RECT));
6057 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
6059 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6062 lvItem.iItem = nItem;
6063 lvItem.iSubItem = nColumn;
6065 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
6069 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
6074 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
6078 ERR("Unknown bounds=%d\n", lprc->left);
6082 OffsetRect(lprc, Position.x, Position.y);
6089 * Retrieves the width of a label.
6092 * [I] infoPtr : valid pointer to the listview structure
6095 * SUCCESS : string width (in pixels)
6098 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *infoPtr, INT nItem)
6100 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6103 TRACE("(nItem=%d)\n", nItem);
6105 lvItem.mask = LVIF_TEXT;
6106 lvItem.iItem = nItem;
6107 lvItem.iSubItem = 0;
6108 lvItem.pszText = szDispText;
6109 lvItem.cchTextMax = DISP_TEXT_SIZE;
6110 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6112 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6117 * Retrieves the spacing between listview control items.
6120 * [I] infoPtr : valid pointer to the listview structure
6121 * [I] bSmall : flag for small or large icon
6124 * Horizontal + vertical spacing
6126 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall)
6132 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6136 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
6137 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6139 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6146 * Retrieves the state of a listview control item.
6149 * [I] infoPtr : valid pointer to the listview structure
6150 * [I] nItem : item index
6151 * [I] uMask : state mask
6154 * State specified by the mask.
6156 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
6160 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6162 lvItem.iItem = nItem;
6163 lvItem.iSubItem = 0;
6164 lvItem.mask = LVIF_STATE;
6165 lvItem.stateMask = uMask;
6166 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6168 return lvItem.state & uMask;
6173 * Retrieves the text of a listview control item or subitem.
6176 * [I] hwnd : window handle
6177 * [I] nItem : item index
6178 * [IO] lpLVItem : item information
6179 * [I] isW : TRUE if lpLVItem is Unicode
6182 * SUCCESS : string length
6185 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6187 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6189 lpLVItem->mask = LVIF_TEXT;
6190 lpLVItem->iItem = nItem;
6191 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
6193 return textlenT(lpLVItem->pszText, isW);
6198 * Searches for an item based on properties + relationships.
6201 * [I] infoPtr : valid pointer to the listview structure
6202 * [I] nItem : item index
6203 * [I] uFlags : relationship flag
6206 * SUCCESS : item index
6209 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
6211 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6213 LVFINDINFOW lvFindInfo;
6214 INT nCountPerColumn;
6218 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
6219 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
6221 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6223 if (uFlags & LVNI_CUT)
6226 if (uFlags & LVNI_DROPHILITED)
6227 uMask |= LVIS_DROPHILITED;
6229 if (uFlags & LVNI_FOCUSED)
6230 uMask |= LVIS_FOCUSED;
6232 if (uFlags & LVNI_SELECTED)
6233 uMask |= LVIS_SELECTED;
6235 /* if we're asked for the focused item, that's only one,
6236 * so it's worth optimizing */
6237 if (uFlags & LVNI_FOCUSED)
6239 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
6240 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
6243 if (uFlags & LVNI_ABOVE)
6245 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6250 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6256 /* Special case for autoarrange - move 'til the top of a list */
6257 if (is_autoarrange(infoPtr))
6259 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6260 while (nItem - nCountPerRow >= 0)
6262 nItem -= nCountPerRow;
6263 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6268 lvFindInfo.flags = LVFI_NEARESTXY;
6269 lvFindInfo.vkDirection = VK_UP;
6270 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
6271 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
6273 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6278 else if (uFlags & LVNI_BELOW)
6280 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6282 while (nItem < infoPtr->nItemCount)
6285 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6291 /* Special case for autoarrange - move 'til the bottom of a list */
6292 if (is_autoarrange(infoPtr))
6294 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6295 while (nItem + nCountPerRow < infoPtr->nItemCount )
6297 nItem += nCountPerRow;
6298 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6303 lvFindInfo.flags = LVFI_NEARESTXY;
6304 lvFindInfo.vkDirection = VK_DOWN;
6305 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
6306 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
6308 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6313 else if (uFlags & LVNI_TOLEFT)
6315 if (uView == LVS_LIST)
6317 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6318 while (nItem - nCountPerColumn >= 0)
6320 nItem -= nCountPerColumn;
6321 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6325 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6327 /* Special case for autoarrange - move 'til the beginning of a row */
6328 if (is_autoarrange(infoPtr))
6330 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6331 while (nItem % nCountPerRow > 0)
6334 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6339 lvFindInfo.flags = LVFI_NEARESTXY;
6340 lvFindInfo.vkDirection = VK_LEFT;
6341 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
6342 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
6344 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6349 else if (uFlags & LVNI_TORIGHT)
6351 if (uView == LVS_LIST)
6353 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6354 while (nItem + nCountPerColumn < infoPtr->nItemCount)
6356 nItem += nCountPerColumn;
6357 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6361 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6363 /* Special case for autoarrange - move 'til the end of a row */
6364 if (is_autoarrange(infoPtr))
6366 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6367 while (nItem % nCountPerRow < nCountPerRow - 1 )
6370 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6375 lvFindInfo.flags = LVFI_NEARESTXY;
6376 lvFindInfo.vkDirection = VK_RIGHT;
6377 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
6378 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
6380 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6389 /* search by index */
6390 for (i = nItem; i < infoPtr->nItemCount; i++)
6392 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
6400 /* LISTVIEW_GetNumberOfWorkAreas */
6404 * Retrieves the origin coordinates when in icon or small icon display mode.
6407 * [I] infoPtr : valid pointer to the listview structure
6408 * [O] lpptOrigin : coordinate information
6413 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
6415 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6416 INT nHorzPos = 0, nVertPos = 0;
6417 SCROLLINFO scrollInfo;
6419 scrollInfo.cbSize = sizeof(SCROLLINFO);
6420 scrollInfo.fMask = SIF_POS;
6422 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6423 nHorzPos = scrollInfo.nPos;
6424 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6425 nVertPos = scrollInfo.nPos;
6427 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6429 lpptOrigin->x = infoPtr->rcList.left;
6430 lpptOrigin->y = infoPtr->rcList.top;
6431 if (uView == LVS_LIST)
6432 nHorzPos *= infoPtr->nItemWidth;
6433 else if (uView == LVS_REPORT)
6434 nVertPos *= infoPtr->nItemHeight;
6436 lpptOrigin->x -= nHorzPos;
6437 lpptOrigin->y -= nVertPos;
6439 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
6444 * Retrieves the width of a string.
6447 * [I] hwnd : window handle
6448 * [I] lpszText : text string to process
6449 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6452 * SUCCESS : string width (in pixels)
6455 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6460 if (is_textT(lpszText, isW))
6462 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6463 HDC hdc = GetDC(infoPtr->hwndSelf);
6464 HFONT hOldFont = SelectObject(hdc, hFont);
6467 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6469 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6470 SelectObject(hdc, hOldFont);
6471 ReleaseDC(infoPtr->hwndSelf, hdc);
6473 return stringSize.cx;
6478 * Determines which listview item is located at the specified position.
6481 * [I] infoPtr : valid pointer to the listview structure
6482 * [IO] lpht : hit test information
6483 * [I] subitem : fill out iSubItem.
6484 * [I] select : return the index only if the hit selects the item
6487 * (mm 20001022): We must not allow iSubItem to be touched, for
6488 * an app might pass only a structure with space up to iItem!
6489 * (MS Office 97 does that for instance in the file open dialog)
6492 * SUCCESS : item index
6495 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6497 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6498 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6499 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6500 POINT Origin, Position, opt;
6505 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6509 if (subitem) lpht->iSubItem = 0;
6511 if (infoPtr->rcList.left > lpht->pt.x)
6512 lpht->flags |= LVHT_TOLEFT;
6513 else if (infoPtr->rcList.right < lpht->pt.x)
6514 lpht->flags |= LVHT_TORIGHT;
6516 if (infoPtr->rcList.top > lpht->pt.y)
6517 lpht->flags |= LVHT_ABOVE;
6518 else if (infoPtr->rcList.bottom < lpht->pt.y)
6519 lpht->flags |= LVHT_BELOW;
6521 TRACE("lpht->flags=0x%x\n", lpht->flags);
6522 if (lpht->flags) return -1;
6524 lpht->flags |= LVHT_NOWHERE;
6526 LISTVIEW_GetOrigin(infoPtr, &Origin);
6528 /* first deal with the large items */
6529 rcSearch.left = lpht->pt.x;
6530 rcSearch.top = lpht->pt.y;
6531 rcSearch.right = rcSearch.left + 1;
6532 rcSearch.bottom = rcSearch.top + 1;
6534 iterator_frameditems(&i, infoPtr, &rcSearch);
6535 iterator_next(&i); /* go to first item in the sequence */
6537 iterator_destroy(&i);
6539 TRACE("lpht->iItem=%d\n", iItem);
6540 if (iItem == -1) return -1;
6542 if (uView == LVS_REPORT && subitem)
6544 RECT bounds, *pRect;
6547 /* for top/bottom only */
6549 LISTVIEW_GetItemRect(infoPtr, iItem, &bounds);
6551 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6553 pRect = &LISTVIEW_GetColumnInfo(infoPtr, j)->rcHeader;
6554 bounds.left = pRect->left;
6555 bounds.right = pRect->right;
6557 if (PtInRect(&bounds, lpht->pt))
6563 TRACE("lpht->iSubItem=%d\n", lpht->iSubItem);
6566 lvItem.mask = LVIF_STATE | LVIF_TEXT;
6567 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6568 lvItem.stateMask = LVIS_STATEIMAGEMASK;
6569 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6570 lvItem.iItem = iItem;
6571 lvItem.iSubItem = subitem ? lpht->iSubItem : 0;
6572 lvItem.pszText = szDispText;
6573 lvItem.cchTextMax = DISP_TEXT_SIZE;
6574 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6575 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6577 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
6578 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6579 opt.x = lpht->pt.x - Position.x - Origin.x;
6580 opt.y = lpht->pt.y - Position.y - Origin.y;
6582 if (uView == LVS_REPORT)
6586 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6587 UnionRect(&rcBounds, &rcBounds, &rcState);
6589 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6590 if (!PtInRect(&rcBounds, opt)) return -1;
6592 if (PtInRect(&rcIcon, opt))
6593 lpht->flags |= LVHT_ONITEMICON;
6594 else if (PtInRect(&rcLabel, opt))
6595 lpht->flags |= LVHT_ONITEMLABEL;
6596 else if (infoPtr->himlState && PtInRect(&rcState, opt))
6597 lpht->flags |= LVHT_ONITEMSTATEICON;
6598 /* special case for LVS_EX_FULLROWSELECT */
6599 if (uView == LVS_REPORT && infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT &&
6600 !(lpht->flags & LVHT_ONITEM))
6602 lpht->flags = LVHT_ONITEM | LVHT_ABOVE;
6604 if (lpht->flags & LVHT_ONITEM)
6605 lpht->flags &= ~LVHT_NOWHERE;
6606 TRACE("lpht->flags=0x%x\n", lpht->flags);
6608 if (select && !(uView == LVS_REPORT &&
6609 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6610 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6612 if (uView == LVS_REPORT)
6614 /* get main item bounds */
6615 lvItem.iSubItem = 0;
6616 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
6617 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6618 UnionRect(&rcBounds, &rcBounds, &rcState);
6620 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6622 return lpht->iItem = iItem;
6627 * Inserts a new item in the listview control.
6630 * [I] infoPtr : valid pointer to the listview structure
6631 * [I] lpLVItem : item information
6632 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6635 * SUCCESS : new item index
6638 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6640 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6645 BOOL is_sorted, has_changed;
6647 HWND hwndSelf = infoPtr->hwndSelf;
6649 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6651 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6653 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6654 if (!lpLVItem || lpLVItem->iSubItem) return -1;
6656 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6658 if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
6660 /* insert item in listview control data structure */
6661 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6662 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6664 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6665 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6667 if (lpLVItem->iItem < 0 && !is_sorted) return -1;
6669 /* calculate new item index */
6676 while (i < infoPtr->nItemCount)
6678 hItem = DPA_GetPtr( infoPtr->hdpaItems, i);
6679 item_s = (ITEM_INFO*)DPA_GetPtr(hItem, 0);
6681 cmpv = textcmpWT(item_s->hdr.pszText, lpLVItem->pszText, TRUE);
6682 if (infoPtr->dwStyle & LVS_SORTDESCENDING) cmpv *= -1;
6684 if (cmpv >= 0) break;
6690 nItem = min(lpLVItem->iItem, infoPtr->nItemCount);
6692 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6693 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6694 if (nItem == -1) goto fail;
6695 infoPtr->nItemCount++;
6697 /* shift indices first so they don't get tangled */
6698 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6700 /* set the item attributes */
6701 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6703 /* full size structure expected - _WIN32IE >= 0x560 */
6706 else if (lpLVItem->mask & LVIF_INDENT)
6708 /* indent member expected - _WIN32IE >= 0x300 */
6709 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6713 /* minimal structure expected */
6714 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6717 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6719 item.mask |= LVIF_STATE;
6720 item.stateMask |= LVIS_STATEIMAGEMASK;
6721 item.state &= ~LVIS_STATEIMAGEMASK;
6722 item.state |= INDEXTOSTATEIMAGEMASK(1);
6724 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6726 /* make room for the position, if we are in the right mode */
6727 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6729 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6731 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6733 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6738 /* send LVN_INSERTITEM notification */
6739 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6741 nmlv.lParam = lpItem->lParam;
6742 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6743 if (!IsWindow(hwndSelf))
6746 /* align items (set position of each item) */
6747 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6751 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6752 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6754 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6756 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6759 /* now is the invalidation fun */
6760 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6764 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6765 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6766 infoPtr->nItemCount--;
6768 DPA_DeletePtr(hdpaSubItems, 0);
6769 DPA_Destroy (hdpaSubItems);
6776 * Redraws a range of items.
6779 * [I] infoPtr : valid pointer to the listview structure
6780 * [I] nFirst : first item
6781 * [I] nLast : last item
6787 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6791 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6792 max(nFirst, nLast) >= infoPtr->nItemCount)
6795 for (i = nFirst; i <= nLast; i++)
6796 LISTVIEW_InvalidateItem(infoPtr, i);
6803 * Scroll the content of a listview.
6806 * [I] infoPtr : valid pointer to the listview structure
6807 * [I] dx : horizontal scroll amount in pixels
6808 * [I] dy : vertical scroll amount in pixels
6815 * If the control is in report mode (LVS_REPORT) the control can
6816 * be scrolled only in line increments. "dy" will be rounded to the
6817 * nearest number of pixels that are a whole line. Ex: if line height
6818 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6819 * is passed, then the scroll will be 0. (per MSDN 7/2002)
6821 * For: (per experimentation with native control and CSpy ListView)
6822 * LVS_ICON dy=1 = 1 pixel (vertical only)
6824 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6826 * LVS_LIST dx=1 = 1 column (horizontal only)
6827 * but will only scroll 1 column per message
6828 * no matter what the value.
6829 * dy must be 0 or FALSE returned.
6830 * LVS_REPORT dx=1 = 1 pixel
6834 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6836 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6838 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6839 dy /= infoPtr->nItemHeight;
6842 if (dy != 0) return FALSE;
6849 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6850 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6857 * Sets the background color.
6860 * [I] infoPtr : valid pointer to the listview structure
6861 * [I] clrBk : background color
6867 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6869 TRACE("(clrBk=%x)\n", clrBk);
6871 if(infoPtr->clrBk != clrBk) {
6872 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6873 infoPtr->clrBk = clrBk;
6874 if (clrBk == CLR_NONE)
6875 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6877 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6878 LISTVIEW_InvalidateList(infoPtr);
6884 /* LISTVIEW_SetBkImage */
6886 /*** Helper for {Insert,Set}ColumnT *only* */
6887 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
6888 const LVCOLUMNW *lpColumn, BOOL isW)
6890 if (lpColumn->mask & LVCF_FMT)
6892 /* format member is valid */
6893 lphdi->mask |= HDI_FORMAT;
6895 /* set text alignment (leftmost column must be left-aligned) */
6896 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6897 lphdi->fmt |= HDF_LEFT;
6898 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6899 lphdi->fmt |= HDF_RIGHT;
6900 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6901 lphdi->fmt |= HDF_CENTER;
6903 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6904 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6906 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6908 lphdi->fmt |= HDF_IMAGE;
6909 lphdi->iImage = I_IMAGECALLBACK;
6913 if (lpColumn->mask & LVCF_WIDTH)
6915 lphdi->mask |= HDI_WIDTH;
6916 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6918 /* make it fill the remainder of the controls width */
6922 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6924 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6925 lphdi->cxy += rcHeader.right - rcHeader.left;
6928 /* retrieve the layout of the header */
6929 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6930 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
6932 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6935 lphdi->cxy = lpColumn->cx;
6938 if (lpColumn->mask & LVCF_TEXT)
6940 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6941 lphdi->fmt |= HDF_STRING;
6942 lphdi->pszText = lpColumn->pszText;
6943 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6946 if (lpColumn->mask & LVCF_IMAGE)
6948 lphdi->mask |= HDI_IMAGE;
6949 lphdi->iImage = lpColumn->iImage;
6952 if (lpColumn->mask & LVCF_ORDER)
6954 lphdi->mask |= HDI_ORDER;
6955 lphdi->iOrder = lpColumn->iOrder;
6962 * Inserts a new column.
6965 * [I] infoPtr : valid pointer to the listview structure
6966 * [I] nColumn : column index
6967 * [I] lpColumn : column information
6968 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6971 * SUCCESS : new column index
6974 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6975 const LVCOLUMNW *lpColumn, BOOL isW)
6977 COLUMN_INFO *lpColumnInfo;
6980 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6982 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6984 if (!lpColumn || nColumn < 0) return -1;
6985 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6987 ZeroMemory(&hdi, sizeof(HDITEMW));
6988 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6991 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
6992 * (can be seen in SPY) otherwise column never gets added.
6994 if (!(lpColumn->mask & LVCF_WIDTH)) {
6995 hdi.mask |= HDI_WIDTH;
7000 * when the iSubItem is available Windows copies it to the header lParam. It seems
7001 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
7003 if (lpColumn->mask & LVCF_SUBITEM)
7005 hdi.mask |= HDI_LPARAM;
7006 hdi.lParam = lpColumn->iSubItem;
7009 /* create header if not present */
7010 LISTVIEW_CreateHeader(infoPtr);
7011 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle) &&
7012 (LVS_REPORT == uView) && (WS_VISIBLE & infoPtr->dwStyle))
7014 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7017 /* insert item in header control */
7018 nNewColumn = SendMessageW(infoPtr->hwndHeader,
7019 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
7020 (WPARAM)nColumn, (LPARAM)&hdi);
7021 if (nNewColumn == -1) return -1;
7022 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
7024 /* create our own column info */
7025 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
7026 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
7028 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
7029 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, nNewColumn, (LPARAM)&lpColumnInfo->rcHeader))
7032 /* now we have to actually adjust the data */
7033 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
7035 SUBITEM_INFO *lpSubItem;
7039 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
7041 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
7042 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
7044 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
7045 if (lpSubItem->iSubItem >= nNewColumn)
7046 lpSubItem->iSubItem++;
7051 /* make space for the new column */
7052 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7053 LISTVIEW_UpdateItemSize(infoPtr);
7058 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
7061 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
7069 * Sets the attributes of a header item.
7072 * [I] infoPtr : valid pointer to the listview structure
7073 * [I] nColumn : column index
7074 * [I] lpColumn : column attributes
7075 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
7081 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
7082 const LVCOLUMNW *lpColumn, BOOL isW)
7084 HDITEMW hdi, hdiget;
7087 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
7089 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7091 ZeroMemory(&hdi, sizeof(HDITEMW));
7092 if (lpColumn->mask & LVCF_FMT)
7094 hdi.mask |= HDI_FORMAT;
7095 hdiget.mask = HDI_FORMAT;
7096 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
7097 hdi.fmt = hdiget.fmt & HDF_STRING;
7099 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
7101 /* set header item attributes */
7102 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
7103 if (!bResult) return FALSE;
7105 if (lpColumn->mask & LVCF_FMT)
7107 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
7108 int oldFmt = lpColumnInfo->fmt;
7110 lpColumnInfo->fmt = lpColumn->fmt;
7111 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
7113 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7114 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
7123 * Sets the column order array
7126 * [I] infoPtr : valid pointer to the listview structure
7127 * [I] iCount : number of elements in column order array
7128 * [I] lpiArray : pointer to column order array
7134 static BOOL LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
7136 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7147 * Sets the width of a column
7150 * [I] infoPtr : valid pointer to the listview structure
7151 * [I] nColumn : column index
7152 * [I] cx : column width
7158 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
7160 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7161 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
7165 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
7167 /* set column width only if in report or list mode */
7168 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
7170 /* take care of invalid cx values */
7171 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
7172 else if (uView == LVS_LIST && cx < 1) return FALSE;
7174 /* resize all columns if in LVS_LIST mode */
7175 if(uView == LVS_LIST)
7177 infoPtr->nItemWidth = cx;
7178 LISTVIEW_InvalidateList(infoPtr);
7182 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7184 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
7189 lvItem.mask = LVIF_TEXT;
7191 lvItem.iSubItem = nColumn;
7192 lvItem.pszText = szDispText;
7193 lvItem.cchTextMax = DISP_TEXT_SIZE;
7194 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7196 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
7197 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
7198 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
7200 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
7201 max_cx += infoPtr->iconSize.cx;
7202 max_cx += TRAILING_LABEL_PADDING;
7205 /* autosize based on listview items width */
7206 if(cx == LVSCW_AUTOSIZE)
7208 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7210 /* if iCol is the last column make it fill the remainder of the controls width */
7211 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
7216 LISTVIEW_GetOrigin(infoPtr, &Origin);
7217 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
7219 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
7223 /* Despite what the MS docs say, if this is not the last
7224 column, then MS resizes the column to the width of the
7225 largest text string in the column, including headers
7226 and items. This is different from LVSCW_AUTOSIZE in that
7227 LVSCW_AUTOSIZE ignores the header string length. */
7230 /* retrieve header text */
7231 hdi.mask = HDI_TEXT;
7232 hdi.cchTextMax = DISP_TEXT_SIZE;
7233 hdi.pszText = szDispText;
7234 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
7236 HDC hdc = GetDC(infoPtr->hwndSelf);
7237 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
7240 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
7241 cx = size.cx + TRAILING_HEADER_PADDING;
7242 /* FIXME: Take into account the header image, if one is present */
7243 SelectObject(hdc, old_font);
7244 ReleaseDC(infoPtr->hwndSelf, hdc);
7246 cx = max (cx, max_cx);
7250 if (cx < 0) return FALSE;
7252 /* call header to update the column change */
7253 hdi.mask = HDI_WIDTH;
7255 TRACE("hdi.cxy=%d\n", hdi.cxy);
7256 return Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7260 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
7263 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
7266 HBITMAP hbm_im, hbm_mask, hbm_orig;
7268 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
7269 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
7272 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
7273 ILC_COLOR | ILC_MASK, 2, 2);
7274 hdc_wnd = GetDC(infoPtr->hwndSelf);
7275 hdc = CreateCompatibleDC(hdc_wnd);
7276 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
7277 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
7278 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
7280 rc.left = rc.top = 0;
7281 rc.right = GetSystemMetrics(SM_CXSMICON);
7282 rc.bottom = GetSystemMetrics(SM_CYSMICON);
7284 hbm_orig = SelectObject(hdc, hbm_mask);
7285 FillRect(hdc, &rc, hbr_white);
7286 InflateRect(&rc, -2, -2);
7287 FillRect(hdc, &rc, hbr_black);
7289 SelectObject(hdc, hbm_im);
7290 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
7291 SelectObject(hdc, hbm_orig);
7292 ImageList_Add(himl, hbm_im, hbm_mask);
7294 SelectObject(hdc, hbm_im);
7295 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
7296 SelectObject(hdc, hbm_orig);
7297 ImageList_Add(himl, hbm_im, hbm_mask);
7299 DeleteObject(hbm_mask);
7300 DeleteObject(hbm_im);
7308 * Sets the extended listview style.
7311 * [I] infoPtr : valid pointer to the listview structure
7313 * [I] dwStyle : style
7316 * SUCCESS : previous style
7319 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwExStyle)
7321 DWORD dwOldExStyle = infoPtr->dwLvExStyle;
7325 infoPtr->dwLvExStyle = (dwOldExStyle & ~dwMask) | (dwExStyle & dwMask);
7327 infoPtr->dwLvExStyle = dwExStyle;
7329 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_CHECKBOXES)
7331 HIMAGELIST himl = 0;
7332 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7335 item.mask = LVIF_STATE;
7336 item.stateMask = LVIS_STATEIMAGEMASK;
7337 item.state = INDEXTOSTATEIMAGEMASK(1);
7338 LISTVIEW_SetItemState(infoPtr, -1, &item);
7340 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
7342 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
7345 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_HEADERDRAGDROP)
7349 /* if not already created */
7350 LISTVIEW_CreateHeader(infoPtr);
7352 dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
7353 if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
7354 dwStyle |= HDS_DRAGDROP;
7356 dwStyle &= ~HDS_DRAGDROP;
7357 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
7360 /* GRIDLINES adds decoration at top so changes sizes */
7361 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_GRIDLINES)
7363 LISTVIEW_UpdateSize(infoPtr);
7367 LISTVIEW_InvalidateList(infoPtr);
7368 return dwOldExStyle;
7373 * Sets the new hot cursor used during hot tracking and hover selection.
7376 * [I] infoPtr : valid pointer to the listview structure
7377 * [I] hCursor : the new hot cursor handle
7380 * Returns the previous hot cursor
7382 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
7384 HCURSOR oldCursor = infoPtr->hHotCursor;
7386 infoPtr->hHotCursor = hCursor;
7394 * Sets the hot item index.
7397 * [I] infoPtr : valid pointer to the listview structure
7398 * [I] iIndex : index
7401 * SUCCESS : previous hot item index
7402 * FAILURE : -1 (no hot item)
7404 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
7406 INT iOldIndex = infoPtr->nHotItem;
7408 infoPtr->nHotItem = iIndex;
7416 * Sets the amount of time the cursor must hover over an item before it is selected.
7419 * [I] infoPtr : valid pointer to the listview structure
7420 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7423 * Returns the previous hover time
7425 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
7427 DWORD oldHoverTime = infoPtr->dwHoverTime;
7429 infoPtr->dwHoverTime = dwHoverTime;
7431 return oldHoverTime;
7436 * Sets spacing for icons of LVS_ICON style.
7439 * [I] infoPtr : valid pointer to the listview structure
7440 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7441 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7444 * MAKELONG(oldcx, oldcy)
7446 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
7448 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7449 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7451 TRACE("requested=(%d,%d)\n", cx, cy);
7453 /* this is supported only for LVS_ICON style */
7454 if (uView != LVS_ICON) return oldspacing;
7456 /* set to defaults, if instructed to */
7457 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
7458 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
7460 /* if 0 then compute width
7461 * FIXME: Should scan each item and determine max width of
7462 * icon or label, then make that the width */
7464 cx = infoPtr->iconSpacing.cx;
7466 /* if 0 then compute height */
7468 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
7469 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
7472 infoPtr->iconSpacing.cx = cx;
7473 infoPtr->iconSpacing.cy = cy;
7475 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7476 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
7477 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7478 infoPtr->ntmHeight);
7480 /* these depend on the iconSpacing */
7481 LISTVIEW_UpdateItemSize(infoPtr);
7486 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
7490 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
7497 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
7498 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7507 * [I] infoPtr : valid pointer to the listview structure
7508 * [I] nType : image list type
7509 * [I] himl : image list handle
7512 * SUCCESS : old image list
7515 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7517 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7518 INT oldHeight = infoPtr->nItemHeight;
7519 HIMAGELIST himlOld = 0;
7521 TRACE("(nType=%d, himl=%p\n", nType, himl);
7526 himlOld = infoPtr->himlNormal;
7527 infoPtr->himlNormal = himl;
7528 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7529 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7533 himlOld = infoPtr->himlSmall;
7534 infoPtr->himlSmall = himl;
7535 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7539 himlOld = infoPtr->himlState;
7540 infoPtr->himlState = himl;
7541 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7542 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7546 ERR("Unknown icon type=%d\n", nType);
7550 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7551 if (infoPtr->nItemHeight != oldHeight)
7552 LISTVIEW_UpdateScroll(infoPtr);
7559 * Preallocates memory (does *not* set the actual count of items !)
7562 * [I] infoPtr : valid pointer to the listview structure
7563 * [I] nItems : item count (projected number of items to allocate)
7564 * [I] dwFlags : update flags
7570 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7572 TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
7574 if (infoPtr->dwStyle & LVS_OWNERDATA)
7576 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7577 INT nOldCount = infoPtr->nItemCount;
7579 if (nItems < nOldCount)
7581 RANGE range = { nItems, nOldCount };
7582 ranges_del(infoPtr->selectionRanges, range);
7583 if (infoPtr->nFocusedItem >= nItems)
7585 LISTVIEW_SetItemFocus(infoPtr, -1);
7586 SetRectEmpty(&infoPtr->rcFocus);
7590 infoPtr->nItemCount = nItems;
7591 LISTVIEW_UpdateScroll(infoPtr);
7593 /* the flags are valid only in ownerdata report and list modes */
7594 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7596 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7597 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7599 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7600 LISTVIEW_InvalidateList(infoPtr);
7607 LISTVIEW_GetOrigin(infoPtr, &Origin);
7608 nFrom = min(nOldCount, nItems);
7609 nTo = max(nOldCount, nItems);
7611 if (uView == LVS_REPORT)
7614 rcErase.top = nFrom * infoPtr->nItemHeight;
7615 rcErase.right = infoPtr->nItemWidth;
7616 rcErase.bottom = nTo * infoPtr->nItemHeight;
7617 OffsetRect(&rcErase, Origin.x, Origin.y);
7618 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7619 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7623 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7625 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7626 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7627 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7628 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7629 OffsetRect(&rcErase, Origin.x, Origin.y);
7630 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7631 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7633 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7635 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7636 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7637 OffsetRect(&rcErase, Origin.x, Origin.y);
7638 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7639 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7645 /* According to MSDN for non-LVS_OWNERDATA this is just
7646 * a performance issue. The control allocates its internal
7647 * data structures for the number of items specified. It
7648 * cuts down on the number of memory allocations. Therefore
7649 * we will just issue a WARN here
7651 WARN("for non-ownerdata performance option not implemented.\n");
7659 * Sets the position of an item.
7662 * [I] infoPtr : valid pointer to the listview structure
7663 * [I] nItem : item index
7664 * [I] pt : coordinate
7670 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7672 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7675 TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7677 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7678 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7680 LISTVIEW_GetOrigin(infoPtr, &Origin);
7682 /* This point value seems to be an undocumented feature.
7683 * The best guess is that it means either at the origin,
7684 * or at true beginning of the list. I will assume the origin. */
7685 if ((pt.x == -1) && (pt.y == -1))
7688 if (uView == LVS_ICON)
7690 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7691 pt.y -= ICON_TOP_PADDING;
7696 infoPtr->bAutoarrange = FALSE;
7698 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7703 * Sets the state of one or many items.
7706 * [I] infoPtr : valid pointer to the listview structure
7707 * [I] nItem : item index
7708 * [I] lpLVItem : item or subitem info
7714 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7716 BOOL bResult = TRUE;
7719 lvItem.iItem = nItem;
7720 lvItem.iSubItem = 0;
7721 lvItem.mask = LVIF_STATE;
7722 lvItem.state = lpLVItem->state;
7723 lvItem.stateMask = lpLVItem->stateMask;
7724 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7728 /* select all isn't allowed in LVS_SINGLESEL */
7729 if ((lvItem.state & lvItem.stateMask & LVIS_SELECTED) && (infoPtr->dwStyle & LVS_SINGLESEL))
7732 /* apply to all items */
7733 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7734 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7737 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7740 * Update selection mark
7742 * Investigation on windows 2k showed that selection mark was updated
7743 * whenever a new selection was made, but if the selected item was
7744 * unselected it was not updated.
7746 * we are probably still not 100% accurate, but this at least sets the
7747 * proper selection mark when it is needed
7750 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7751 (infoPtr->nSelectionMark == -1))
7754 for (i = 0; i < infoPtr->nItemCount; i++)
7756 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7758 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7760 infoPtr->nSelectionMark = i;
7764 else if (ranges_contain(infoPtr->selectionRanges, i))
7766 infoPtr->nSelectionMark = i;
7777 * Sets the text of an item or subitem.
7780 * [I] hwnd : window handle
7781 * [I] nItem : item index
7782 * [I] lpLVItem : item or subitem info
7783 * [I] isW : TRUE if input is Unicode
7789 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7793 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7795 lvItem.iItem = nItem;
7796 lvItem.iSubItem = lpLVItem->iSubItem;
7797 lvItem.mask = LVIF_TEXT;
7798 lvItem.pszText = lpLVItem->pszText;
7799 lvItem.cchTextMax = lpLVItem->cchTextMax;
7801 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7803 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7808 * Set item index that marks the start of a multiple selection.
7811 * [I] infoPtr : valid pointer to the listview structure
7812 * [I] nIndex : index
7815 * Index number or -1 if there is no selection mark.
7817 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7819 INT nOldIndex = infoPtr->nSelectionMark;
7821 TRACE("(nIndex=%d)\n", nIndex);
7823 infoPtr->nSelectionMark = nIndex;
7830 * Sets the text background color.
7833 * [I] infoPtr : valid pointer to the listview structure
7834 * [I] clrTextBk : text background color
7840 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7842 TRACE("(clrTextBk=%x)\n", clrTextBk);
7844 if (infoPtr->clrTextBk != clrTextBk)
7846 infoPtr->clrTextBk = clrTextBk;
7847 LISTVIEW_InvalidateList(infoPtr);
7855 * Sets the text foreground color.
7858 * [I] infoPtr : valid pointer to the listview structure
7859 * [I] clrText : text color
7865 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7867 TRACE("(clrText=%x)\n", clrText);
7869 if (infoPtr->clrText != clrText)
7871 infoPtr->clrText = clrText;
7872 LISTVIEW_InvalidateList(infoPtr);
7880 * Sets new ToolTip window to ListView control.
7883 * [I] infoPtr : valid pointer to the listview structure
7884 * [I] hwndNewToolTip : handle to new ToolTip
7889 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7891 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7892 infoPtr->hwndToolTip = hwndNewToolTip;
7893 return hwndOldToolTip;
7898 * sets the Unicode character format flag for the control
7900 * [I] infoPtr :valid pointer to the listview structure
7901 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
7904 * Old Unicode Format
7906 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL fUnicode)
7908 BOOL rc = infoPtr->notifyFormat;
7909 infoPtr->notifyFormat = (fUnicode)?NFR_UNICODE:NFR_ANSI;
7913 /* LISTVIEW_SetWorkAreas */
7917 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMS
7920 * [I] first : pointer to first ITEM_INFO to compare
7921 * [I] second : pointer to second ITEM_INFO to compare
7922 * [I] lParam : HWND of control
7925 * if first comes before second : negative
7926 * if first comes after second : positive
7927 * if first and second are equivalent : zero
7929 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7931 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7932 ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
7933 ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
7935 /* Forward the call to the client defined callback */
7936 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7941 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMSEX
7944 * [I] first : pointer to first ITEM_INFO to compare
7945 * [I] second : pointer to second ITEM_INFO to compare
7946 * [I] lParam : HWND of control
7949 * if first comes before second : negative
7950 * if first comes after second : positive
7951 * if first and second are equivalent : zero
7953 static INT WINAPI LISTVIEW_CallBackCompareEx(LPVOID first, LPVOID second, LPARAM lParam)
7955 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7956 INT first_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, first );
7957 INT second_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, second );
7959 /* Forward the call to the client defined callback */
7960 return (infoPtr->pfnCompare)( first_idx, second_idx, infoPtr->lParamSort );
7965 * Sorts the listview items.
7968 * [I] infoPtr : valid pointer to the listview structure
7969 * [I] pfnCompare : application-defined value
7970 * [I] lParamSort : pointer to comparison callback
7971 * [I] IsEx : TRUE when LVM_SORTITEMSEX used
7977 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare,
7978 LPARAM lParamSort, BOOL IsEx)
7980 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7983 LPVOID selectionMarkItem = NULL;
7984 LPVOID focusedItem = NULL;
7987 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7989 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7991 if (!pfnCompare) return FALSE;
7992 if (!infoPtr->hdpaItems) return FALSE;
7994 /* if there are 0 or 1 items, there is no need to sort */
7995 if (infoPtr->nItemCount < 2) return TRUE;
7997 /* clear selection */
7998 ranges_clear(infoPtr->selectionRanges);
8000 /* save selection mark and focused item */
8001 if (infoPtr->nSelectionMark >= 0)
8002 selectionMarkItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark);
8003 if (infoPtr->nFocusedItem >= 0)
8004 focusedItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
8006 infoPtr->pfnCompare = pfnCompare;
8007 infoPtr->lParamSort = lParamSort;
8009 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompareEx, (LPARAM)infoPtr);
8011 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
8013 /* restore selection ranges */
8014 for (i=0; i < infoPtr->nItemCount; i++)
8016 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
8017 lpItem = DPA_GetPtr(hdpaSubItems, 0);
8019 if (lpItem->state & LVIS_SELECTED)
8020 ranges_additem(infoPtr->selectionRanges, i);
8022 /* restore selection mark and focused item */
8023 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
8024 infoPtr->nFocusedItem = DPA_GetPtrIndex(infoPtr->hdpaItems, focusedItem);
8026 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
8028 /* refresh the display */
8029 if (uView != LVS_ICON && uView != LVS_SMALLICON)
8030 LISTVIEW_InvalidateList(infoPtr);
8037 * Update theme handle after a theme change.
8040 * [I] infoPtr : valid pointer to the listview structure
8044 * FAILURE : something else
8046 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr)
8048 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8049 CloseThemeData(theme);
8050 OpenThemeData(infoPtr->hwndSelf, themeClass);
8056 * Updates an items or rearranges the listview control.
8059 * [I] infoPtr : valid pointer to the listview structure
8060 * [I] nItem : item index
8066 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
8068 TRACE("(nItem=%d)\n", nItem);
8070 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
8072 /* rearrange with default alignment style */
8073 if (is_autoarrange(infoPtr))
8074 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8076 LISTVIEW_InvalidateItem(infoPtr, nItem);
8083 * Draw the track line at the place defined in the infoPtr structure.
8084 * The line is drawn with a XOR pen so drawing the line for the second time
8085 * in the same place erases the line.
8088 * [I] infoPtr : valid pointer to the listview structure
8094 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
8100 if (infoPtr->xTrackLine == -1)
8103 if (!(hdc = GetDC(infoPtr->hwndSelf)))
8105 hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
8106 oldROP = SetROP2(hdc, R2_XORPEN);
8107 MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
8108 LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
8109 SetROP2(hdc, oldROP);
8110 SelectObject(hdc, hOldPen);
8111 ReleaseDC(infoPtr->hwndSelf, hdc);
8117 * Called when an edit control should be displayed. This function is called after
8118 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
8121 * [I] hwnd : Handle to the listview
8122 * [I] uMsg : WM_TIMER (ignored)
8123 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
8124 * [I] dwTimer : The elapsed time (ignored)
8129 static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
8131 DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
8132 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8134 KillTimer(hwnd, idEvent);
8135 editItem->fEnabled = FALSE;
8136 /* check if the item is still selected */
8137 if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
8138 LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
8143 * Creates the listview control - the WM_NCCREATE phase.
8146 * [I] hwnd : window handle
8147 * [I] lpcs : the create parameters
8153 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
8155 LISTVIEW_INFO *infoPtr;
8158 TRACE("(lpcs=%p)\n", lpcs);
8160 /* initialize info pointer */
8161 infoPtr = Alloc(sizeof(LISTVIEW_INFO));
8162 if (!infoPtr) return FALSE;
8164 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
8166 infoPtr->hwndSelf = hwnd;
8167 infoPtr->dwStyle = lpcs->style; /* Note: may be changed in WM_CREATE */
8168 /* determine the type of structures to use */
8169 infoPtr->hwndNotify = lpcs->hwndParent;
8170 /* infoPtr->notifyFormat will be filled in WM_CREATE */
8172 /* initialize color information */
8173 infoPtr->clrBk = CLR_NONE;
8174 infoPtr->clrText = CLR_DEFAULT;
8175 infoPtr->clrTextBk = CLR_DEFAULT;
8176 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
8178 /* set default values */
8179 infoPtr->nFocusedItem = -1;
8180 infoPtr->nSelectionMark = -1;
8181 infoPtr->nHotItem = -1;
8182 infoPtr->bRedraw = TRUE;
8183 infoPtr->bNoItemMetrics = TRUE;
8184 infoPtr->bDoChangeNotify = TRUE;
8185 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
8186 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
8187 infoPtr->nEditLabelItem = -1;
8188 infoPtr->nLButtonDownItem = -1;
8189 infoPtr->dwHoverTime = -1; /* default system hover time */
8190 infoPtr->nMeasureItemHeight = 0;
8191 infoPtr->xTrackLine = -1; /* no track line */
8192 infoPtr->itemEdit.fEnabled = FALSE;
8193 infoPtr->iVersion = COMCTL32_VERSION;
8195 /* get default font (icon title) */
8196 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
8197 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
8198 infoPtr->hFont = infoPtr->hDefaultFont;
8199 LISTVIEW_SaveTextMetrics(infoPtr);
8201 /* allocate memory for the data structure */
8202 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
8203 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
8204 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
8205 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
8206 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
8210 DestroyWindow(infoPtr->hwndHeader);
8211 ranges_destroy(infoPtr->selectionRanges);
8212 DPA_Destroy(infoPtr->hdpaItems);
8213 DPA_Destroy(infoPtr->hdpaPosX);
8214 DPA_Destroy(infoPtr->hdpaPosY);
8215 DPA_Destroy(infoPtr->hdpaColumns);
8222 * Creates the listview control - the WM_CREATE phase. Most of the data is
8223 * already set up in LISTVIEW_NCCreate
8226 * [I] hwnd : window handle
8227 * [I] lpcs : the create parameters
8233 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
8235 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8236 UINT uView = lpcs->style & LVS_TYPEMASK;
8238 TRACE("(lpcs=%p)\n", lpcs);
8240 infoPtr->dwStyle = lpcs->style;
8241 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
8242 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
8244 if ((uView == LVS_REPORT) && (lpcs->style & WS_VISIBLE))
8246 if (LISTVIEW_CreateHeader(infoPtr) < 0) return -1;
8249 infoPtr->hwndHeader = 0;
8251 /* init item size to avoid division by 0 */
8252 LISTVIEW_UpdateItemSize (infoPtr);
8254 if (uView == LVS_REPORT)
8256 if (!(LVS_NOCOLUMNHEADER & lpcs->style) && (WS_VISIBLE & lpcs->style))
8258 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8260 LISTVIEW_UpdateScroll(infoPtr);
8263 OpenThemeData(hwnd, themeClass);
8265 /* initialize the icon sizes */
8266 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
8267 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
8273 * Destroys the listview control.
8276 * [I] infoPtr : valid pointer to the listview structure
8282 static LRESULT LISTVIEW_Destroy(const LISTVIEW_INFO *infoPtr)
8284 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8285 CloseThemeData(theme);
8291 * Enables the listview control.
8294 * [I] infoPtr : valid pointer to the listview structure
8295 * [I] bEnable : specifies whether to enable or disable the window
8301 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr, BOOL bEnable)
8303 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
8304 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8310 * Erases the background of the listview control.
8313 * [I] infoPtr : valid pointer to the listview structure
8314 * [I] hdc : device context handle
8320 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
8324 TRACE("(hdc=%p)\n", hdc);
8326 if (!GetClipBox(hdc, &rc)) return FALSE;
8328 if (infoPtr->clrBk == CLR_NONE)
8329 return SendMessageW(infoPtr->hwndNotify, WM_ERASEBKGND, (WPARAM)hdc, 0);
8331 /* for double buffered controls we need to do this during refresh */
8332 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
8334 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
8340 * Helper function for LISTVIEW_[HV]Scroll *only*.
8341 * Performs vertical/horizontal scrolling by a give amount.
8344 * [I] infoPtr : valid pointer to the listview structure
8345 * [I] dx : amount of horizontal scroll
8346 * [I] dy : amount of vertical scroll
8348 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
8350 /* now we can scroll the list */
8351 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
8352 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
8353 /* if we have focus, adjust rect */
8354 OffsetRect(&infoPtr->rcFocus, dx, dy);
8355 UpdateWindow(infoPtr->hwndSelf);
8360 * Performs vertical scrolling.
8363 * [I] infoPtr : valid pointer to the listview structure
8364 * [I] nScrollCode : scroll code
8365 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8366 * [I] hScrollWnd : scrollbar control window handle
8372 * SB_LINEUP/SB_LINEDOWN:
8373 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8374 * for LVS_REPORT is 1 line
8375 * for LVS_LIST cannot occur
8378 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8379 INT nScrollDiff, HWND hScrollWnd)
8381 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8382 INT nOldScrollPos, nNewScrollPos;
8383 SCROLLINFO scrollInfo;
8386 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8387 debugscrollcode(nScrollCode), nScrollDiff);
8389 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8391 scrollInfo.cbSize = sizeof(SCROLLINFO);
8392 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8394 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
8396 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
8398 nOldScrollPos = scrollInfo.nPos;
8399 switch (nScrollCode)
8405 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
8409 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
8413 nScrollDiff = -scrollInfo.nPage;
8417 nScrollDiff = scrollInfo.nPage;
8420 case SB_THUMBPOSITION:
8422 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8429 /* quit right away if pos isn't changing */
8430 if (nScrollDiff == 0) return 0;
8432 /* calculate new position, and handle overflows */
8433 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8434 if (nScrollDiff > 0) {
8435 if (nNewScrollPos < nOldScrollPos ||
8436 nNewScrollPos > scrollInfo.nMax)
8437 nNewScrollPos = scrollInfo.nMax;
8439 if (nNewScrollPos > nOldScrollPos ||
8440 nNewScrollPos < scrollInfo.nMin)
8441 nNewScrollPos = scrollInfo.nMin;
8444 /* set the new position, and reread in case it changed */
8445 scrollInfo.fMask = SIF_POS;
8446 scrollInfo.nPos = nNewScrollPos;
8447 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
8449 /* carry on only if it really changed */
8450 if (nNewScrollPos == nOldScrollPos) return 0;
8452 /* now adjust to client coordinates */
8453 nScrollDiff = nOldScrollPos - nNewScrollPos;
8454 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
8456 /* and scroll the window */
8457 scroll_list(infoPtr, 0, nScrollDiff);
8464 * Performs horizontal scrolling.
8467 * [I] infoPtr : valid pointer to the listview structure
8468 * [I] nScrollCode : scroll code
8469 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8470 * [I] hScrollWnd : scrollbar control window handle
8476 * SB_LINELEFT/SB_LINERIGHT:
8477 * for LVS_ICON, LVS_SMALLICON 1 pixel
8478 * for LVS_REPORT is 1 pixel
8479 * for LVS_LIST is 1 column --> which is a 1 because the
8480 * scroll is based on columns not pixels
8483 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8484 INT nScrollDiff, HWND hScrollWnd)
8486 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8487 INT nOldScrollPos, nNewScrollPos;
8488 SCROLLINFO scrollInfo;
8490 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8491 debugscrollcode(nScrollCode), nScrollDiff);
8493 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8495 scrollInfo.cbSize = sizeof(SCROLLINFO);
8496 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8498 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
8500 nOldScrollPos = scrollInfo.nPos;
8502 switch (nScrollCode)
8516 nScrollDiff = -scrollInfo.nPage;
8520 nScrollDiff = scrollInfo.nPage;
8523 case SB_THUMBPOSITION:
8525 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8532 /* quit right away if pos isn't changing */
8533 if (nScrollDiff == 0) return 0;
8535 /* calculate new position, and handle overflows */
8536 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8537 if (nScrollDiff > 0) {
8538 if (nNewScrollPos < nOldScrollPos ||
8539 nNewScrollPos > scrollInfo.nMax)
8540 nNewScrollPos = scrollInfo.nMax;
8542 if (nNewScrollPos > nOldScrollPos ||
8543 nNewScrollPos < scrollInfo.nMin)
8544 nNewScrollPos = scrollInfo.nMin;
8547 /* set the new position, and reread in case it changed */
8548 scrollInfo.fMask = SIF_POS;
8549 scrollInfo.nPos = nNewScrollPos;
8550 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
8552 /* carry on only if it really changed */
8553 if (nNewScrollPos == nOldScrollPos) return 0;
8555 if(uView == LVS_REPORT)
8556 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
8558 /* now adjust to client coordinates */
8559 nScrollDiff = nOldScrollPos - nNewScrollPos;
8560 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
8562 /* and scroll the window */
8563 scroll_list(infoPtr, nScrollDiff, 0);
8568 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
8570 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8571 INT gcWheelDelta = 0;
8572 INT pulScrollLines = 3;
8573 SCROLLINFO scrollInfo;
8575 TRACE("(wheelDelta=%d)\n", wheelDelta);
8577 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8578 gcWheelDelta -= wheelDelta;
8580 scrollInfo.cbSize = sizeof(SCROLLINFO);
8581 scrollInfo.fMask = SIF_POS;
8588 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8589 * should be fixed in the future.
8591 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
8592 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8596 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8598 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
8599 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8600 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
8605 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8616 * [I] infoPtr : valid pointer to the listview structure
8617 * [I] nVirtualKey : virtual key
8618 * [I] lKeyData : key data
8623 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8625 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8626 HWND hwndSelf = infoPtr->hwndSelf;
8628 NMLVKEYDOWN nmKeyDown;
8630 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
8632 /* send LVN_KEYDOWN notification */
8633 nmKeyDown.wVKey = nVirtualKey;
8634 nmKeyDown.flags = 0;
8635 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8636 if (!IsWindow(hwndSelf))
8639 switch (nVirtualKey)
8642 nItem = infoPtr->nFocusedItem;
8643 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
8644 toggle_checkbox_state(infoPtr, infoPtr->nFocusedItem);
8648 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8650 if (!notify(infoPtr, NM_RETURN)) return 0;
8651 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8656 if (infoPtr->nItemCount > 0)
8661 if (infoPtr->nItemCount > 0)
8662 nItem = infoPtr->nItemCount - 1;
8666 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TOLEFT);
8670 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_ABOVE);
8674 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TORIGHT);
8678 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_BELOW);
8682 if (uView == LVS_REPORT)
8684 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8685 if (infoPtr->nFocusedItem == topidx)
8686 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8691 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8692 * LISTVIEW_GetCountPerRow(infoPtr);
8693 if(nItem < 0) nItem = 0;
8697 if (uView == LVS_REPORT)
8699 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8700 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8701 if (infoPtr->nFocusedItem == topidx + cnt - 1)
8702 nItem = infoPtr->nFocusedItem + cnt - 1;
8704 nItem = topidx + cnt - 1;
8707 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8708 * LISTVIEW_GetCountPerRow(infoPtr);
8709 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8713 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8714 LISTVIEW_KeySelection(infoPtr, nItem, nVirtualKey == VK_SPACE);
8724 * [I] infoPtr : valid pointer to the listview structure
8729 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8733 /* if we did not have the focus, there's nothing to do */
8734 if (!infoPtr->bFocus) return 0;
8736 /* send NM_KILLFOCUS notification */
8737 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8739 /* if we have a focus rectangle, get rid of it */
8740 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8742 /* set window focus flag */
8743 infoPtr->bFocus = FALSE;
8745 /* invalidate the selected items before resetting focus flag */
8746 LISTVIEW_InvalidateSelectedItems(infoPtr);
8753 * Processes double click messages (left mouse button).
8756 * [I] infoPtr : valid pointer to the listview structure
8757 * [I] wKey : key flag
8758 * [I] x,y : mouse coordinate
8763 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8765 LVHITTESTINFO htInfo;
8767 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8769 /* Cancel the item edition if any */
8770 if (infoPtr->itemEdit.fEnabled)
8772 KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
8773 infoPtr->itemEdit.fEnabled = FALSE;
8776 /* send NM_RELEASEDCAPTURE notification */
8777 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8782 /* send NM_DBLCLK notification */
8783 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8784 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8786 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8787 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8794 * Processes mouse down messages (left mouse button).
8797 * infoPtr [I ] valid pointer to the listview structure
8798 * wKey [I ] key flag
8799 * x,y [I ] mouse coordinate
8804 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8806 LVHITTESTINFO lvHitTestInfo;
8807 static BOOL bGroupSelect = TRUE;
8808 POINT pt = { x, y };
8811 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8813 /* send NM_RELEASEDCAPTURE notification */
8814 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8816 /* set left button down flag and record the click position */
8817 infoPtr->bLButtonDown = TRUE;
8818 infoPtr->ptClickPos = pt;
8819 infoPtr->bDragging = FALSE;
8821 lvHitTestInfo.pt.x = x;
8822 lvHitTestInfo.pt.y = y;
8824 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8825 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
8826 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8828 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8830 toggle_checkbox_state(infoPtr, nItem);
8834 if (infoPtr->dwStyle & LVS_SINGLESEL)
8836 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8837 infoPtr->nEditLabelItem = nItem;
8839 LISTVIEW_SetSelection(infoPtr, nItem);
8843 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8847 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8848 LISTVIEW_SetItemFocus(infoPtr, nItem);
8849 infoPtr->nSelectionMark = nItem;
8855 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8856 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8858 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8859 infoPtr->nSelectionMark = nItem;
8862 else if (wKey & MK_CONTROL)
8866 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8868 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8869 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8870 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8871 infoPtr->nSelectionMark = nItem;
8873 else if (wKey & MK_SHIFT)
8875 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8879 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8881 infoPtr->nEditLabelItem = nItem;
8882 infoPtr->nLButtonDownItem = nItem;
8884 LISTVIEW_SetItemFocus(infoPtr, nItem);
8887 /* set selection (clears other pre-existing selections) */
8888 LISTVIEW_SetSelection(infoPtr, nItem);
8892 if (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE)
8893 if(lvHitTestInfo.iItem != -1) notify_itemactivate(infoPtr,&lvHitTestInfo);
8897 /* remove all selections */
8898 if (!(wKey & MK_CONTROL) && !(wKey & MK_SHIFT))
8899 LISTVIEW_DeselectAll(infoPtr);
8908 * Processes mouse up messages (left mouse button).
8911 * infoPtr [I ] valid pointer to the listview structure
8912 * wKey [I ] key flag
8913 * x,y [I ] mouse coordinate
8918 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8920 LVHITTESTINFO lvHitTestInfo;
8922 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8924 if (!infoPtr->bLButtonDown) return 0;
8926 lvHitTestInfo.pt.x = x;
8927 lvHitTestInfo.pt.y = y;
8929 /* send NM_CLICK notification */
8930 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8931 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8933 /* set left button flag */
8934 infoPtr->bLButtonDown = FALSE;
8936 /* set a single selection, reset others */
8937 if(lvHitTestInfo.iItem == infoPtr->nLButtonDownItem && lvHitTestInfo.iItem != -1)
8938 LISTVIEW_SetSelection(infoPtr, infoPtr->nLButtonDownItem);
8939 infoPtr->nLButtonDownItem = -1;
8941 if (infoPtr->bDragging)
8943 infoPtr->bDragging = FALSE;
8947 /* if we clicked on a selected item, edit the label */
8948 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8950 /* we want to make sure the user doesn't want to do a double click. So we will
8951 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
8953 infoPtr->itemEdit.fEnabled = TRUE;
8954 infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
8955 SetTimer(infoPtr->hwndSelf,
8956 (UINT_PTR)&infoPtr->itemEdit,
8957 GetDoubleClickTime(),
8958 LISTVIEW_DelayedEditItem);
8961 if (!infoPtr->bFocus)
8962 SetFocus(infoPtr->hwndSelf);
8969 * Destroys the listview control (called after WM_DESTROY).
8972 * [I] infoPtr : valid pointer to the listview structure
8977 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8981 /* delete all items */
8982 LISTVIEW_DeleteAllItems(infoPtr, TRUE);
8984 /* destroy data structure */
8985 DPA_Destroy(infoPtr->hdpaItems);
8986 DPA_Destroy(infoPtr->hdpaPosX);
8987 DPA_Destroy(infoPtr->hdpaPosY);
8988 DPA_Destroy(infoPtr->hdpaColumns);
8989 ranges_destroy(infoPtr->selectionRanges);
8991 /* destroy image lists */
8992 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8994 if (infoPtr->himlNormal)
8995 ImageList_Destroy(infoPtr->himlNormal);
8996 if (infoPtr->himlSmall)
8997 ImageList_Destroy(infoPtr->himlSmall);
8998 if (infoPtr->himlState)
8999 ImageList_Destroy(infoPtr->himlState);
9002 /* destroy font, bkgnd brush */
9004 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
9005 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
9007 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
9009 /* free listview info pointer*/
9017 * Handles notifications from header.
9020 * [I] infoPtr : valid pointer to the listview structure
9021 * [I] nCtrlId : control identifier
9022 * [I] lpnmh : notification information
9027 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
9029 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9030 HWND hwndSelf = infoPtr->hwndSelf;
9032 TRACE("(lpnmh=%p)\n", lpnmh);
9034 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
9036 switch (lpnmh->hdr.code)
9041 COLUMN_INFO *lpColumnInfo;
9045 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
9048 /* remove the old line (if any) */
9049 LISTVIEW_DrawTrackLine(infoPtr);
9051 /* compute & draw the new line */
9052 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
9053 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
9054 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
9055 infoPtr->xTrackLine = x + ptOrigin.x;
9056 LISTVIEW_DrawTrackLine(infoPtr);
9062 /* remove the track line (if any) */
9063 LISTVIEW_DrawTrackLine(infoPtr);
9064 infoPtr->xTrackLine = -1;
9068 FIXME("Changing column order not implemented\n");
9071 case HDN_ITEMCHANGINGW:
9072 case HDN_ITEMCHANGINGA:
9073 return notify_forward_header(infoPtr, lpnmh);
9075 case HDN_ITEMCHANGEDW:
9076 case HDN_ITEMCHANGEDA:
9078 COLUMN_INFO *lpColumnInfo;
9081 notify_forward_header(infoPtr, lpnmh);
9082 if (!IsWindow(hwndSelf))
9085 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
9089 hdi.mask = HDI_WIDTH;
9090 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, &hdi)) return 0;
9094 cxy = lpnmh->pitem->cxy;
9096 /* determine how much we change since the last know position */
9097 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
9098 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
9101 lpColumnInfo->rcHeader.right += dx;
9102 if (lpnmh->iItem + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
9103 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
9106 /* only needs to update the scrolls */
9107 infoPtr->nItemWidth += dx;
9108 LISTVIEW_UpdateScroll(infoPtr);
9110 LISTVIEW_UpdateItemSize(infoPtr);
9111 if (uView == LVS_REPORT && is_redrawing(infoPtr))
9114 RECT rcCol = lpColumnInfo->rcHeader;
9116 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
9117 OffsetRect(&rcCol, ptOrigin.x, 0);
9119 rcCol.top = infoPtr->rcList.top;
9120 rcCol.bottom = infoPtr->rcList.bottom;
9122 /* resizing left-aligned columns leaves most of the left side untouched */
9123 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
9125 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
9128 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
9131 /* when shrinking the last column clear the now unused field */
9132 if (lpnmh->iItem == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
9138 /* deal with right from rightmost column area */
9139 right.left = rcCol.right;
9140 right.top = rcCol.top;
9141 right.bottom = rcCol.bottom;
9142 right.right = infoPtr->rcList.right;
9144 LISTVIEW_InvalidateRect(infoPtr, &right);
9147 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
9153 case HDN_ITEMCLICKW:
9154 case HDN_ITEMCLICKA:
9156 /* Handle sorting by Header Column */
9159 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
9161 nmlv.iSubItem = lpnmh->iItem;
9162 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
9163 notify_forward_header(infoPtr, lpnmh);
9167 case HDN_DIVIDERDBLCLICKW:
9168 case HDN_DIVIDERDBLCLICKA:
9169 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
9178 * Paint non-client area of control.
9181 * [I] infoPtr : valid pointer to the listview structureof the sender
9182 * [I] region : update region
9185 * TRUE - frame was painted
9186 * FALSE - call default window proc
9188 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
9190 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
9194 int cxEdge = GetSystemMetrics (SM_CXEDGE),
9195 cyEdge = GetSystemMetrics (SM_CYEDGE);
9197 if (!theme) return FALSE;
9199 GetWindowRect(infoPtr->hwndSelf, &r);
9201 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
9202 r.right - cxEdge, r.bottom - cyEdge);
9203 if (region != (HRGN)1)
9204 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
9205 OffsetRect(&r, -r.left, -r.top);
9207 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
9208 OffsetRect(&r, -r.left, -r.top);
9210 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
9211 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
9212 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
9213 ReleaseDC(infoPtr->hwndSelf, dc);
9215 /* Call default proc to get the scrollbars etc. painted */
9216 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
9223 * Determines the type of structure to use.
9226 * [I] infoPtr : valid pointer to the listview structureof the sender
9227 * [I] hwndFrom : listview window handle
9228 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
9233 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
9235 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
9237 if (nCommand == NF_REQUERY)
9238 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
9240 return infoPtr->notifyFormat;
9245 * Paints/Repaints the listview control.
9248 * [I] infoPtr : valid pointer to the listview structure
9249 * [I] hdc : device context handle
9254 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
9256 TRACE("(hdc=%p)\n", hdc);
9258 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
9260 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9262 infoPtr->bNoItemMetrics = FALSE;
9263 LISTVIEW_UpdateItemSize(infoPtr);
9264 if (uView == LVS_ICON || uView == LVS_SMALLICON)
9265 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9266 LISTVIEW_UpdateScroll(infoPtr);
9269 if (infoPtr->hwndHeader) UpdateWindow(infoPtr->hwndHeader);
9272 LISTVIEW_Refresh(infoPtr, hdc, NULL);
9277 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
9279 LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
9280 EndPaint(infoPtr->hwndSelf, &ps);
9289 * Paints/Repaints the listview control.
9292 * [I] infoPtr : valid pointer to the listview structure
9293 * [I] hdc : device context handle
9294 * [I] options : drawing options
9299 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
9301 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
9303 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
9306 if (options & PRF_ERASEBKGND)
9307 LISTVIEW_EraseBkgnd(infoPtr, hdc);
9309 if (options & PRF_CLIENT)
9310 LISTVIEW_Paint(infoPtr, hdc);
9318 * Processes double click messages (right mouse button).
9321 * [I] infoPtr : valid pointer to the listview structure
9322 * [I] wKey : key flag
9323 * [I] x,y : mouse coordinate
9328 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9330 LVHITTESTINFO lvHitTestInfo;
9332 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9334 /* send NM_RELEASEDCAPTURE notification */
9335 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9337 /* send NM_RDBLCLK notification */
9338 lvHitTestInfo.pt.x = x;
9339 lvHitTestInfo.pt.y = y;
9340 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9341 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
9348 * Processes mouse down messages (right mouse button).
9351 * [I] infoPtr : valid pointer to the listview structure
9352 * [I] wKey : key flag
9353 * [I] x,y : mouse coordinate
9358 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9360 LVHITTESTINFO lvHitTestInfo;
9363 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9365 /* send NM_RELEASEDCAPTURE notification */
9366 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9368 /* make sure the listview control window has the focus */
9369 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
9371 /* set right button down flag */
9372 infoPtr->bRButtonDown = TRUE;
9374 /* determine the index of the selected item */
9375 lvHitTestInfo.pt.x = x;
9376 lvHitTestInfo.pt.y = y;
9377 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
9379 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9381 LISTVIEW_SetItemFocus(infoPtr, nItem);
9382 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9383 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9384 LISTVIEW_SetSelection(infoPtr, nItem);
9388 LISTVIEW_DeselectAll(infoPtr);
9396 * Processes mouse up messages (right mouse button).
9399 * [I] infoPtr : valid pointer to the listview structure
9400 * [I] wKey : key flag
9401 * [I] x,y : mouse coordinate
9406 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9408 LVHITTESTINFO lvHitTestInfo;
9411 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9413 if (!infoPtr->bRButtonDown) return 0;
9415 /* set button flag */
9416 infoPtr->bRButtonDown = FALSE;
9418 /* Send NM_RCLICK notification */
9419 lvHitTestInfo.pt.x = x;
9420 lvHitTestInfo.pt.y = y;
9421 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9422 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
9424 /* Change to screen coordinate for WM_CONTEXTMENU */
9425 pt = lvHitTestInfo.pt;
9426 ClientToScreen(infoPtr->hwndSelf, &pt);
9428 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9429 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
9430 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
9441 * [I] infoPtr : valid pointer to the listview structure
9442 * [I] hwnd : window handle of window containing the cursor
9443 * [I] nHittest : hit-test code
9444 * [I] wMouseMsg : ideintifier of the mouse message
9447 * TRUE if cursor is set
9450 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
9452 LVHITTESTINFO lvHitTestInfo;
9454 if(!(LISTVIEW_isHotTracking(infoPtr))) return FALSE;
9456 if(!infoPtr->hHotCursor) return FALSE;
9458 GetCursorPos(&lvHitTestInfo.pt);
9459 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
9461 SetCursor(infoPtr->hHotCursor);
9471 * [I] infoPtr : valid pointer to the listview structure
9472 * [I] hwndLoseFocus : handle of previously focused window
9477 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
9479 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
9481 /* if we have the focus already, there's nothing to do */
9482 if (infoPtr->bFocus) return 0;
9484 /* send NM_SETFOCUS notification */
9485 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
9487 /* set window focus flag */
9488 infoPtr->bFocus = TRUE;
9490 /* put the focus rect back on */
9491 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
9493 /* redraw all visible selected items */
9494 LISTVIEW_InvalidateSelectedItems(infoPtr);
9504 * [I] infoPtr : valid pointer to the listview structure
9505 * [I] fRedraw : font handle
9506 * [I] fRedraw : redraw flag
9511 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
9513 HFONT oldFont = infoPtr->hFont;
9515 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
9517 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9518 if (infoPtr->hFont == oldFont) return 0;
9520 LISTVIEW_SaveTextMetrics(infoPtr);
9522 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
9524 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
9525 LISTVIEW_UpdateSize(infoPtr);
9526 LISTVIEW_UpdateScroll(infoPtr);
9529 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
9536 * Message handling for WM_SETREDRAW.
9537 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9540 * [I] infoPtr : valid pointer to the listview structure
9541 * [I] bRedraw: state of redraw flag
9544 * DefWinProc return value
9546 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
9548 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
9550 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9551 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
9553 infoPtr->bRedraw = bRedraw;
9555 if(!bRedraw) return 0;
9557 if (is_autoarrange(infoPtr))
9558 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9559 LISTVIEW_UpdateScroll(infoPtr);
9561 /* despite what the WM_SETREDRAW docs says, apps expect us
9562 * to invalidate the listview here... stupid! */
9563 LISTVIEW_InvalidateList(infoPtr);
9570 * Resizes the listview control. This function processes WM_SIZE
9571 * messages. At this time, the width and height are not used.
9574 * [I] infoPtr : valid pointer to the listview structure
9575 * [I] Width : new width
9576 * [I] Height : new height
9581 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
9583 RECT rcOld = infoPtr->rcList;
9585 TRACE("(width=%d, height=%d)\n", Width, Height);
9587 LISTVIEW_UpdateSize(infoPtr);
9588 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
9590 /* do not bother with display related stuff if we're not redrawing */
9591 if (!is_redrawing(infoPtr)) return 0;
9593 if (is_autoarrange(infoPtr))
9594 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9596 LISTVIEW_UpdateScroll(infoPtr);
9598 /* refresh all only for lists whose height changed significantly */
9599 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
9600 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
9601 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
9602 LISTVIEW_InvalidateList(infoPtr);
9609 * Sets the size information.
9612 * [I] infoPtr : valid pointer to the listview structure
9617 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
9619 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9621 TRACE("uView=%d, rcList(old)=%s\n", uView, wine_dbgstr_rect(&infoPtr->rcList));
9623 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
9625 if (uView == LVS_LIST)
9627 /* Apparently the "LIST" style is supposed to have the same
9628 * number of items in a column even if there is no scroll bar.
9629 * Since if a scroll bar already exists then the bottom is already
9630 * reduced, only reduce if the scroll bar does not currently exist.
9631 * The "2" is there to mimic the native control. I think it may be
9632 * related to either padding or edges. (GLA 7/2002)
9634 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
9635 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
9636 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
9638 else if (uView == LVS_REPORT)
9643 hl.prc = &infoPtr->rcList;
9645 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9646 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp.flags, wp.x, wp.y, wp.cx, wp.cy);
9647 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy,
9648 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9649 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9650 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy);
9652 infoPtr->rcList.top = max(wp.cy, 0);
9653 infoPtr->rcList.top += (infoPtr->dwLvExStyle & LVS_EX_GRIDLINES) ? 2 : 0;
9656 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
9661 * Processes WM_STYLECHANGED messages.
9664 * [I] infoPtr : valid pointer to the listview structure
9665 * [I] wStyleType : window style type (normal or extended)
9666 * [I] lpss : window style information
9671 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9672 const STYLESTRUCT *lpss)
9674 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9675 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9678 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9679 wStyleType, lpss->styleOld, lpss->styleNew);
9681 if (wStyleType != GWL_STYLE) return 0;
9683 infoPtr->dwStyle = lpss->styleNew;
9685 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9686 ((lpss->styleNew & WS_HSCROLL) == 0))
9687 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9689 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9690 ((lpss->styleNew & WS_VSCROLL) == 0))
9691 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9693 if (uNewView != uOldView)
9695 SIZE oldIconSize = infoPtr->iconSize;
9698 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9699 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9701 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9702 SetRectEmpty(&infoPtr->rcFocus);
9704 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9705 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
9707 if (uNewView == LVS_ICON)
9709 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
9711 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9712 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9713 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
9716 else if (uNewView == LVS_REPORT)
9721 LISTVIEW_CreateHeader( infoPtr );
9723 hl.prc = &infoPtr->rcList;
9725 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9726 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
9727 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9728 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9731 LISTVIEW_UpdateItemSize(infoPtr);
9734 if (uNewView == LVS_REPORT)
9736 if ((lpss->styleOld ^ lpss->styleNew) & LVS_NOCOLUMNHEADER)
9738 if (lpss->styleNew & LVS_NOCOLUMNHEADER)
9740 /* Turn off the header control */
9741 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
9742 TRACE("Hide header control, was 0x%08x\n", style);
9743 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style | HDS_HIDDEN);
9745 /* Turn on the header control */
9746 if ((style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE)) & HDS_HIDDEN)
9748 TRACE("Show header control, was 0x%08x\n", style);
9749 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, (style & ~HDS_HIDDEN) | WS_VISIBLE);
9755 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9756 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9757 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9759 /* update the size of the client area */
9760 LISTVIEW_UpdateSize(infoPtr);
9762 /* add scrollbars if needed */
9763 LISTVIEW_UpdateScroll(infoPtr);
9765 /* invalidate client area + erase background */
9766 LISTVIEW_InvalidateList(infoPtr);
9773 * Processes WM_STYLECHANGING messages.
9776 * [I] infoPtr : valid pointer to the listview structure
9777 * [I] wStyleType : window style type (normal or extended)
9778 * [I0] lpss : window style information
9783 static INT LISTVIEW_StyleChanging(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9786 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9787 wStyleType, lpss->styleOld, lpss->styleNew);
9789 /* don't forward LVS_OWNERDATA only if not already set to */
9790 if ((lpss->styleNew ^ lpss->styleOld) & LVS_OWNERDATA)
9792 if (lpss->styleOld & LVS_OWNERDATA)
9793 lpss->styleNew |= LVS_OWNERDATA;
9795 lpss->styleNew &= ~LVS_OWNERDATA;
9803 * Processes WM_SHOWWINDOW messages.
9806 * [I] infoPtr : valid pointer to the listview structure
9807 * [I] bShown : window is being shown (FALSE when hidden)
9808 * [I] iStatus : window show status
9813 static LRESULT LISTVIEW_ShowWindow(LISTVIEW_INFO *infoPtr, BOOL bShown, INT iStatus)
9815 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9817 /* header delayed creation */
9818 if ((uView == LVS_REPORT) && bShown)
9820 LISTVIEW_CreateHeader(infoPtr);
9822 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle))
9823 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9831 * Processes CCM_GETVERSION messages.
9834 * [I] infoPtr : valid pointer to the listview structure
9839 static inline LRESULT LISTVIEW_GetVersion(LISTVIEW_INFO *infoPtr)
9841 return infoPtr->iVersion;
9846 * Processes CCM_SETVERSION messages.
9849 * [I] infoPtr : valid pointer to the listview structure
9850 * [I] iVersion : version to be set
9853 * -1 when requested version is greater than DLL version;
9854 * previous version otherwise
9856 static LRESULT LISTVIEW_SetVersion(LISTVIEW_INFO *infoPtr, DWORD iVersion)
9858 INT iOldVersion = infoPtr->iVersion;
9860 if (iVersion > COMCTL32_VERSION)
9863 infoPtr->iVersion = iVersion;
9865 TRACE("new version %d\n", iVersion);
9872 * Window procedure of the listview control.
9875 static LRESULT WINAPI
9876 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9878 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9880 TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg, wParam, lParam);
9882 if (!infoPtr && (uMsg != WM_NCCREATE))
9883 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9887 case LVM_APPROXIMATEVIEWRECT:
9888 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9889 LOWORD(lParam), HIWORD(lParam));
9891 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9893 /* case LVM_CANCELEDITLABEL: */
9895 case LVM_CREATEDRAGIMAGE:
9896 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9898 case LVM_DELETEALLITEMS:
9899 return LISTVIEW_DeleteAllItems(infoPtr, FALSE);
9901 case LVM_DELETECOLUMN:
9902 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9904 case LVM_DELETEITEM:
9905 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9907 case LVM_EDITLABELW:
9908 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9910 case LVM_EDITLABELA:
9911 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9913 /* case LVM_ENABLEGROUPVIEW: */
9915 case LVM_ENSUREVISIBLE:
9916 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9919 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9922 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9924 case LVM_GETBKCOLOR:
9925 return infoPtr->clrBk;
9927 /* case LVM_GETBKIMAGE: */
9929 case LVM_GETCALLBACKMASK:
9930 return infoPtr->uCallbackMask;
9932 case LVM_GETCOLUMNA:
9933 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9935 case LVM_GETCOLUMNW:
9936 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9938 case LVM_GETCOLUMNORDERARRAY:
9939 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9941 case LVM_GETCOLUMNWIDTH:
9942 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9944 case LVM_GETCOUNTPERPAGE:
9945 return LISTVIEW_GetCountPerPage(infoPtr);
9947 case LVM_GETEDITCONTROL:
9948 return (LRESULT)infoPtr->hwndEdit;
9950 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9951 return infoPtr->dwLvExStyle;
9953 /* case LVM_GETGROUPINFO: */
9955 /* case LVM_GETGROUPMETRICS: */
9958 return (LRESULT)infoPtr->hwndHeader;
9960 case LVM_GETHOTCURSOR:
9961 return (LRESULT)infoPtr->hHotCursor;
9963 case LVM_GETHOTITEM:
9964 return infoPtr->nHotItem;
9966 case LVM_GETHOVERTIME:
9967 return infoPtr->dwHoverTime;
9969 case LVM_GETIMAGELIST:
9970 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9972 /* case LVM_GETINSERTMARK: */
9974 /* case LVM_GETINSERTMARKCOLOR: */
9976 /* case LVM_GETINSERTMARKRECT: */
9978 case LVM_GETISEARCHSTRINGA:
9979 case LVM_GETISEARCHSTRINGW:
9980 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9984 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9987 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9989 case LVM_GETITEMCOUNT:
9990 return infoPtr->nItemCount;
9992 case LVM_GETITEMPOSITION:
9993 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9995 case LVM_GETITEMRECT:
9996 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9998 case LVM_GETITEMSPACING:
9999 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
10001 case LVM_GETITEMSTATE:
10002 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
10004 case LVM_GETITEMTEXTA:
10005 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
10007 case LVM_GETITEMTEXTW:
10008 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
10010 case LVM_GETNEXTITEM:
10011 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
10013 case LVM_GETNUMBEROFWORKAREAS:
10014 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
10017 case LVM_GETORIGIN:
10018 if (!lParam) return FALSE;
10019 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT ||
10020 (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST) return FALSE;
10021 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
10024 /* case LVM_GETOUTLINECOLOR: */
10026 /* case LVM_GETSELECTEDCOLUMN: */
10028 case LVM_GETSELECTEDCOUNT:
10029 return LISTVIEW_GetSelectedCount(infoPtr);
10031 case LVM_GETSELECTIONMARK:
10032 return infoPtr->nSelectionMark;
10034 case LVM_GETSTRINGWIDTHA:
10035 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
10037 case LVM_GETSTRINGWIDTHW:
10038 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
10040 case LVM_GETSUBITEMRECT:
10041 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
10043 case LVM_GETTEXTBKCOLOR:
10044 return infoPtr->clrTextBk;
10046 case LVM_GETTEXTCOLOR:
10047 return infoPtr->clrText;
10049 /* case LVM_GETTILEINFO: */
10051 /* case LVM_GETTILEVIEWINFO: */
10053 case LVM_GETTOOLTIPS:
10054 if( !infoPtr->hwndToolTip )
10055 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
10056 return (LRESULT)infoPtr->hwndToolTip;
10058 case LVM_GETTOPINDEX:
10059 return LISTVIEW_GetTopIndex(infoPtr);
10061 case LVM_GETUNICODEFORMAT:
10062 return (infoPtr->notifyFormat == NFR_UNICODE);
10064 /* case LVM_GETVIEW: */
10066 case LVM_GETVIEWRECT:
10067 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
10069 case LVM_GETWORKAREAS:
10070 FIXME("LVM_GETWORKAREAS: unimplemented\n");
10073 /* case LVM_HASGROUP: */
10076 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, TRUE);
10078 case LVM_INSERTCOLUMNA:
10079 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10081 case LVM_INSERTCOLUMNW:
10082 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10084 /* case LVM_INSERTGROUP: */
10086 /* case LVM_INSERTGROUPSORTED: */
10088 case LVM_INSERTITEMA:
10089 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
10091 case LVM_INSERTITEMW:
10092 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
10094 /* case LVM_INSERTMARKHITTEST: */
10096 /* case LVM_ISGROUPVIEWENABLED: */
10098 /* case LVM_MAPIDTOINDEX: */
10100 /* case LVM_MAPINDEXTOID: */
10102 /* case LVM_MOVEGROUP: */
10104 /* case LVM_MOVEITEMTOGROUP: */
10106 case LVM_REDRAWITEMS:
10107 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
10109 /* case LVM_REMOVEALLGROUPS: */
10111 /* case LVM_REMOVEGROUP: */
10114 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
10116 case LVM_SETBKCOLOR:
10117 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
10119 /* case LVM_SETBKIMAGE: */
10121 case LVM_SETCALLBACKMASK:
10122 infoPtr->uCallbackMask = (UINT)wParam;
10125 case LVM_SETCOLUMNA:
10126 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10128 case LVM_SETCOLUMNW:
10129 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10131 case LVM_SETCOLUMNORDERARRAY:
10132 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
10134 case LVM_SETCOLUMNWIDTH:
10135 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
10137 case LVM_SETEXTENDEDLISTVIEWSTYLE:
10138 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
10140 /* case LVM_SETGROUPINFO: */
10142 /* case LVM_SETGROUPMETRICS: */
10144 case LVM_SETHOTCURSOR:
10145 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
10147 case LVM_SETHOTITEM:
10148 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
10150 case LVM_SETHOVERTIME:
10151 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
10153 case LVM_SETICONSPACING:
10154 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10156 case LVM_SETIMAGELIST:
10157 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
10159 /* case LVM_SETINFOTIP: */
10161 /* case LVM_SETINSERTMARK: */
10163 /* case LVM_SETINSERTMARKCOLOR: */
10168 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
10169 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, (uMsg == LVM_SETITEMW));
10172 case LVM_SETITEMCOUNT:
10173 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
10175 case LVM_SETITEMPOSITION:
10178 pt.x = (short)LOWORD(lParam);
10179 pt.y = (short)HIWORD(lParam);
10180 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
10183 case LVM_SETITEMPOSITION32:
10184 if (lParam == 0) return FALSE;
10185 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
10187 case LVM_SETITEMSTATE:
10188 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
10190 case LVM_SETITEMTEXTA:
10191 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
10193 case LVM_SETITEMTEXTW:
10194 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
10196 /* case LVM_SETOUTLINECOLOR: */
10198 /* case LVM_SETSELECTEDCOLUMN: */
10200 case LVM_SETSELECTIONMARK:
10201 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
10203 case LVM_SETTEXTBKCOLOR:
10204 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
10206 case LVM_SETTEXTCOLOR:
10207 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
10209 /* case LVM_SETTILEINFO: */
10211 /* case LVM_SETTILEVIEWINFO: */
10213 /* case LVM_SETTILEWIDTH: */
10215 case LVM_SETTOOLTIPS:
10216 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
10218 case LVM_SETUNICODEFORMAT:
10219 return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
10221 /* case LVM_SETVIEW: */
10223 /* case LVM_SETWORKAREAS: */
10225 /* case LVM_SORTGROUPS: */
10227 case LVM_SORTITEMS:
10228 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam, FALSE);
10230 case LVM_SORTITEMSEX:
10231 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam, TRUE);
10233 case LVM_SUBITEMHITTEST:
10234 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
10237 return LISTVIEW_Update(infoPtr, (INT)wParam);
10239 case CCM_GETVERSION:
10240 return LISTVIEW_GetVersion(infoPtr);
10242 case CCM_SETVERSION:
10243 return LISTVIEW_SetVersion(infoPtr, wParam);
10246 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
10249 return LISTVIEW_Command(infoPtr, wParam, lParam);
10252 return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
10255 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
10258 return LISTVIEW_Destroy(infoPtr);
10261 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
10263 case WM_ERASEBKGND:
10264 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
10266 case WM_GETDLGCODE:
10267 return DLGC_WANTCHARS | DLGC_WANTARROWS;
10270 return (LRESULT)infoPtr->hFont;
10273 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10276 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
10279 return LISTVIEW_KillFocus(infoPtr);
10281 case WM_LBUTTONDBLCLK:
10282 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10284 case WM_LBUTTONDOWN:
10285 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10288 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10291 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10293 case WM_MOUSEHOVER:
10294 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10297 return LISTVIEW_NCDestroy(infoPtr);
10300 if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
10305 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
10306 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
10309 case WM_NOTIFYFORMAT:
10310 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
10312 case WM_PRINTCLIENT:
10313 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
10316 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
10318 case WM_RBUTTONDBLCLK:
10319 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10321 case WM_RBUTTONDOWN:
10322 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10325 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10328 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
10333 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
10336 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
10339 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
10341 case WM_SHOWWINDOW:
10342 LISTVIEW_ShowWindow(infoPtr, (BOOL)wParam, (INT)lParam);
10343 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10346 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10348 case WM_STYLECHANGED:
10349 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10351 case WM_STYLECHANGING:
10352 return LISTVIEW_StyleChanging(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10354 case WM_SYSCOLORCHANGE:
10355 COMCTL32_RefreshSysColors();
10358 /* case WM_TIMER: */
10359 case WM_THEMECHANGED:
10360 return LISTVIEW_ThemeChanged(infoPtr);
10363 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10365 case WM_MOUSEWHEEL:
10366 if (wParam & (MK_SHIFT | MK_CONTROL))
10367 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10368 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
10370 case WM_WINDOWPOSCHANGED:
10371 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
10373 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
10374 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
10375 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
10377 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
10379 MEASUREITEMSTRUCT mis;
10380 mis.CtlType = ODT_LISTVIEW;
10381 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
10385 mis.itemHeight= infoPtr->nItemHeight;
10386 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
10387 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
10388 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
10391 LISTVIEW_UpdateSize(infoPtr);
10392 LISTVIEW_UpdateScroll(infoPtr);
10394 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10396 /* case WM_WININICHANGE: */
10399 if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
10400 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
10403 /* call default window procedure */
10404 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10411 * Registers the window class.
10419 void LISTVIEW_Register(void)
10421 WNDCLASSW wndClass;
10423 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
10424 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
10425 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
10426 wndClass.cbClsExtra = 0;
10427 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
10428 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
10429 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
10430 wndClass.lpszClassName = WC_LISTVIEWW;
10431 RegisterClassW(&wndClass);
10436 * Unregisters the window class.
10444 void LISTVIEW_Unregister(void)
10446 UnregisterClassW(WC_LISTVIEWW, NULL);
10451 * Handle any WM_COMMAND messages
10454 * [I] infoPtr : valid pointer to the listview structure
10455 * [I] wParam : the first message parameter
10456 * [I] lParam : the second message parameter
10461 static LRESULT LISTVIEW_Command(const LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
10463 switch (HIWORD(wParam))
10468 * Adjust the edit window size
10470 WCHAR buffer[1024];
10471 HDC hdc = GetDC(infoPtr->hwndEdit);
10472 HFONT hFont, hOldFont = 0;
10476 if (!infoPtr->hwndEdit || !hdc) return 0;
10477 GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
10478 GetWindowRect(infoPtr->hwndEdit, &rect);
10480 /* Select font to get the right dimension of the string */
10481 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
10484 hOldFont = SelectObject(hdc, hFont);
10487 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
10489 TEXTMETRICW textMetric;
10491 /* Add Extra spacing for the next character */
10492 GetTextMetricsW(hdc, &textMetric);
10493 sz.cx += (textMetric.tmMaxCharWidth * 2);
10501 rect.bottom - rect.top,
10502 SWP_DRAWFRAME|SWP_NOMOVE);
10505 SelectObject(hdc, hOldFont);
10507 ReleaseDC(infoPtr->hwndEdit, hdc);
10513 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
10522 * Subclassed edit control windproc function
10525 * [I] hwnd : the edit window handle
10526 * [I] uMsg : the message that is to be processed
10527 * [I] wParam : first message parameter
10528 * [I] lParam : second message parameter
10529 * [I] isW : TRUE if input is Unicode
10534 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
10536 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
10537 BOOL cancel = FALSE;
10539 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10540 hwnd, uMsg, wParam, lParam, isW);
10544 case WM_GETDLGCODE:
10545 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
10552 WNDPROC editProc = infoPtr->EditWndProc;
10553 infoPtr->EditWndProc = 0;
10554 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
10555 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
10559 if (VK_ESCAPE == (INT)wParam)
10564 else if (VK_RETURN == (INT)wParam)
10568 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
10571 /* kill the edit */
10572 if (infoPtr->hwndEdit)
10574 LPWSTR buffer = NULL;
10576 infoPtr->hwndEdit = 0;
10579 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
10583 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
10585 if (isW) GetWindowTextW(hwnd, buffer, len+1);
10586 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
10590 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
10595 SendMessageW(hwnd, WM_CLOSE, 0, 0);
10601 * Subclassed edit control Unicode windproc function
10604 * [I] hwnd : the edit window handle
10605 * [I] uMsg : the message that is to be processed
10606 * [I] wParam : first message parameter
10607 * [I] lParam : second message parameter
10611 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10613 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
10618 * Subclassed edit control ANSI windproc function
10621 * [I] hwnd : the edit window handle
10622 * [I] uMsg : the message that is to be processed
10623 * [I] wParam : first message parameter
10624 * [I] lParam : second message parameter
10628 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10630 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
10635 * Creates a subclassed edit control
10638 * [I] infoPtr : valid pointer to the listview structure
10639 * [I] text : initial text for the edit
10640 * [I] style : the window style
10641 * [I] isW : TRUE if input is Unicode
10645 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
10646 INT x, INT y, INT width, INT height, BOOL isW)
10648 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
10653 TEXTMETRICW textMetric;
10654 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
10656 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
10658 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
10659 hdc = GetDC(infoPtr->hwndSelf);
10661 /* Select the font to get appropriate metric dimensions */
10662 if(infoPtr->hFont != 0)
10663 hOldFont = SelectObject(hdc, infoPtr->hFont);
10665 /*Get String Length in pixels */
10666 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
10668 /*Add Extra spacing for the next character */
10669 GetTextMetricsW(hdc, &textMetric);
10670 sz.cx += (textMetric.tmMaxCharWidth * 2);
10672 if(infoPtr->hFont != 0)
10673 SelectObject(hdc, hOldFont);
10675 ReleaseDC(infoPtr->hwndSelf, hdc);
10677 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10679 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10681 if (!hedit) return 0;
10683 infoPtr->EditWndProc = (WNDPROC)
10684 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
10685 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
10687 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);