4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2001 CodeWeavers Inc.
8 * Copyright 2002 Dimitrie O. Paun
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * This code was audited for completeness against the documented features
27 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
29 * Unless otherwise noted, we believe this code to be complete, as per
30 * the specification mentioned above.
31 * If you discover missing features, or bugs, please note them below.
35 * Default Message Processing
36 * -- EN_KILLFOCUS should be handled in WM_COMMAND
37 * -- WM_CREATE: create the icon and small icon image lists at this point only if
38 * the LVS_SHAREIMAGELISTS style is not specified.
39 * -- WM_ERASEBKGND: forward this message to the parent window if the bkgnd
41 * -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
42 * or small icon and the LVS_AUTOARRANGE style is specified.
47 * -- Hot item handling, mouse hovering
48 * -- Workareas support
53 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
54 * -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
55 * -- LVA_SNAPTOGRID not implemented
56 * -- LISTVIEW_ApproximateViewRect partially implemented
57 * -- LISTVIEW_[GS]etColumnOrderArray stubs
58 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
59 * -- LISTVIEW_SetIconSpacing is incomplete
60 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
63 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
64 * linear in the number of items in the list, and this is
65 * unacceptable for large lists.
66 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
67 * instead of inserting in the right spot
68 * -- we should keep an ordered array of coordinates in iconic mode
69 * this would allow to frame items (iterator_frameditems),
70 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
77 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
84 * -- LVS_NOSCROLL (see Q137520)
85 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
87 * -- LVS_TYPESTYLEMASK
90 * -- LVS_EX_BORDERSELECT
92 * -- LVS_EX_HEADERDRAGDROP
95 * -- LVS_EX_MULTIWORKAREAS
97 * -- LVS_EX_SIMPLESELECT
98 * -- LVS_EX_TWOCLICKACTIVATE
99 * -- LVS_EX_UNDERLINECOLD
100 * -- LVS_EX_UNDERLINEHOT
103 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
106 * -- LVN_MARQUEEBEGIN
112 * -- LVM_CANCELEDITLABEL
113 * -- LVM_ENABLEGROUPVIEW
114 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
115 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
116 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
117 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
118 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
119 * -- LVM_GETINSERTMARKRECT
120 * -- LVM_GETNUMBEROFWORKAREAS
121 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
122 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
123 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
124 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
125 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
126 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
127 * -- LVM_GETVIEW, LVM_SETVIEW
128 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
129 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
130 * -- LVM_INSERTGROUPSORTED
131 * -- LVM_INSERTMARKHITTEST
132 * -- LVM_ISGROUPVIEWENABLED
133 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
135 * -- LVM_MOVEITEMTOGROUP
137 * -- LVM_SETTILEWIDTH
141 * -- ListView_GetCheckSate, ListView_SetCheckState
142 * -- ListView_GetHoverTime, ListView_SetHoverTime
143 * -- ListView_GetISearchString
144 * -- ListView_GetNumberOfWorkAreas
145 * -- ListView_GetOrigin
146 * -- ListView_GetUnicodeFormat, ListView_SetUnicodeFormat
147 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
152 * Known differences in message stream from native control (not known if
153 * these differences cause problems):
154 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
155 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
156 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
157 * processing for "USEDOUBLECLICKTIME".
161 #include "wine/port.h"
176 #include "commctrl.h"
177 #include "comctl32.h"
180 #include "wine/debug.h"
181 #include "wine/unicode.h"
183 WINE_DEFAULT_DEBUG_CHANNEL(listview);
185 /* make sure you set this to 0 for production use! */
186 #define DEBUG_RANGES 1
188 typedef struct tagCOLUMN_INFO
190 RECT rcHeader; /* tracks the header's rectangle */
191 int fmt; /* same as LVCOLUMN.fmt */
194 typedef struct tagITEMHDR
198 } ITEMHDR, *LPITEMHDR;
200 typedef struct tagSUBITEM_INFO
206 typedef struct tagITEM_INFO
214 typedef struct tagRANGE
220 typedef struct tagRANGES
225 typedef struct tagITERATOR
234 typedef struct tagDELAYED_ITEM_EDIT
240 typedef struct tagLISTVIEW_INFO
247 HIMAGELIST himlNormal;
248 HIMAGELIST himlSmall;
249 HIMAGELIST himlState;
253 POINT ptClickPos; /* point where the user clicked */
254 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
257 RANGES selectionRanges;
262 RECT rcList; /* This rectangle is really the window
263 * client rectangle possibly reduced by the
264 * horizontal scroll bar and/or header - see
265 * LISTVIEW_UpdateSize. This rectangle offset
266 * by the LISTVIEW_GetOrigin value is in
267 * client coordinates */
276 INT ntmHeight; /* Some cached metrics of the font used */
277 INT ntmMaxCharWidth; /* by the listview to draw items */
279 BOOL bRedraw; /* Turns on/off repaints & invalidations */
280 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
282 BOOL bDoChangeNotify; /* send change notification messages? */
285 DWORD dwStyle; /* the cached window GWL_STYLE */
286 DWORD dwLvExStyle; /* extended listview style */
287 INT nItemCount; /* the number of items in the list */
288 HDPA hdpaItems; /* array ITEM_INFO pointers */
289 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
290 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
291 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
292 POINT currIconPos; /* this is the position next icon will be placed */
293 PFNLVCOMPARE pfnCompare;
301 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
303 DWORD lastKeyPressTimestamp;
305 INT nSearchParamLength;
306 WCHAR szSearchParam[ MAX_PATH ];
308 INT nMeasureItemHeight;
309 INT xTrackLine; /* The x coefficient of the track line or -1 if none */
310 DELAYED_ITEM_EDIT itemEdit; /* Pointer to this structure will be the timer ID */
312 DWORD iVersion; /* CCM_[G,S]ETVERSION */
318 /* How many we debug buffer to allocate */
319 #define DEBUG_BUFFERS 20
320 /* The size of a single debug bbuffer */
321 #define DEBUG_BUFFER_SIZE 256
323 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
324 #define SB_INTERNAL -1
326 /* maximum size of a label */
327 #define DISP_TEXT_SIZE 512
329 /* padding for items in list and small icon display modes */
330 #define WIDTH_PADDING 12
332 /* padding for items in list, report and small icon display modes */
333 #define HEIGHT_PADDING 1
335 /* offset of items in report display mode */
336 #define REPORT_MARGINX 2
338 /* padding for icon in large icon display mode
339 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
340 * that HITTEST will see.
341 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
342 * ICON_TOP_PADDING - sum of the two above.
343 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
344 * LABEL_HOR_PADDING - between text and sides of box
345 * LABEL_VERT_PADDING - between bottom of text and end of box
347 * ICON_LR_PADDING - additional width above icon size.
348 * ICON_LR_HALF - half of the above value
350 #define ICON_TOP_PADDING_NOTHITABLE 2
351 #define ICON_TOP_PADDING_HITABLE 2
352 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
353 #define ICON_BOTTOM_PADDING 4
354 #define LABEL_HOR_PADDING 5
355 #define LABEL_VERT_PADDING 7
356 #define ICON_LR_PADDING 16
357 #define ICON_LR_HALF (ICON_LR_PADDING/2)
359 /* default label width for items in list and small icon display modes */
360 #define DEFAULT_LABEL_WIDTH 40
362 /* default column width for items in list display mode */
363 #define DEFAULT_COLUMN_WIDTH 128
365 /* Size of "line" scroll for V & H scrolls */
366 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
368 /* Padding between image and label */
369 #define IMAGE_PADDING 2
371 /* Padding behind the label */
372 #define TRAILING_LABEL_PADDING 12
373 #define TRAILING_HEADER_PADDING 11
375 /* Border for the icon caption */
376 #define CAPTION_BORDER 2
378 /* Standard DrawText flags */
379 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
380 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
381 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
383 /* Image index from state */
384 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
386 /* The time in milliseconds to reset the search in the list */
387 #define KEY_DELAY 450
389 /* Dump the LISTVIEW_INFO structure to the debug channel */
390 #define LISTVIEW_DUMP(iP) do { \
391 TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
392 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
393 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
394 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
395 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
396 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
397 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
398 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
399 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
400 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
403 static const WCHAR themeClass[] = {'L','i','s','t','V','i','e','w',0};
406 * forward declarations
408 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *, LPLVITEMW, BOOL);
409 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *, INT, LPRECT);
410 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *, INT, LPPOINT);
411 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *, INT, LPPOINT);
412 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *, INT, LPRECT);
413 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *, INT);
414 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *, LPPOINT);
415 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *, LPRECT);
416 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
417 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LVITEMW *, BOOL);
418 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *);
419 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
420 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
421 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
422 static LRESULT LISTVIEW_Command(const LISTVIEW_INFO *, WPARAM, LPARAM);
423 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *, LPCWSTR, BOOL);
424 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT, BOOL);
425 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *, INT, UINT);
426 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
427 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
428 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
429 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *);
430 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
431 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
432 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
433 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
435 /******** Text handling functions *************************************/
437 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
438 * text string. The string may be ANSI or Unicode, in which case
439 * the boolean isW tells us the type of the string.
441 * The name of the function tell what type of strings it expects:
442 * W: Unicode, T: ANSI/Unicode - function of isW
445 static inline BOOL is_textW(LPCWSTR text)
447 return text != NULL && text != LPSTR_TEXTCALLBACKW;
450 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
452 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
453 return is_textW(text);
456 static inline int textlenT(LPCWSTR text, BOOL isW)
458 return !is_textT(text, isW) ? 0 :
459 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
462 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
465 if (isSrcW) lstrcpynW(dest, src, max);
466 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
468 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
469 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
472 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
474 LPWSTR wstr = (LPWSTR)text;
476 if (!isW && is_textT(text, isW))
478 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
479 wstr = Alloc(len * sizeof(WCHAR));
480 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
482 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
486 static inline void textfreeT(LPWSTR wstr, BOOL isW)
488 if (!isW && is_textT(wstr, isW)) Free (wstr);
492 * dest is a pointer to a Unicode string
493 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
495 static BOOL textsetptrT(LPWSTR *dest, LPCWSTR src, BOOL isW)
499 if (src == LPSTR_TEXTCALLBACKW)
501 if (is_textW(*dest)) Free(*dest);
502 *dest = LPSTR_TEXTCALLBACKW;
506 LPWSTR pszText = textdupTtoW(src, isW);
507 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
508 bResult = Str_SetPtrW(dest, pszText);
509 textfreeT(pszText, isW);
515 * compares a Unicode to a Unicode/ANSI text string
517 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
519 if (!aw) return bt ? -1 : 0;
520 if (!bt) return aw ? 1 : 0;
521 if (aw == LPSTR_TEXTCALLBACKW)
522 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
523 if (bt != LPSTR_TEXTCALLBACKW)
525 LPWSTR bw = textdupTtoW(bt, isW);
526 int r = bw ? lstrcmpW(aw, bw) : 1;
534 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
538 n = min(min(n, lstrlenW(s1)), lstrlenW(s2));
539 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
540 return res ? res - sizeof(WCHAR) : res;
543 /******** Debugging functions *****************************************/
545 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
547 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
548 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
551 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
553 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
554 n = min(textlenT(text, isW), n);
555 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
558 static char* debug_getbuf(void)
560 static int index = 0;
561 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
562 return buffers[index++ % DEBUG_BUFFERS];
565 static inline const char* debugrange(const RANGE *lprng)
567 if (!lprng) return "(null)";
568 return wine_dbg_sprintf("[%d, %d]", lprng->lower, lprng->upper);
571 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
573 char* buf = debug_getbuf(), *text = buf;
574 int len, size = DEBUG_BUFFER_SIZE;
576 if (pScrollInfo == NULL) return "(null)";
577 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
578 if (len == -1) goto end; buf += len; size -= len;
579 if (pScrollInfo->fMask & SIF_RANGE)
580 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
582 if (len == -1) goto end; buf += len; size -= len;
583 if (pScrollInfo->fMask & SIF_PAGE)
584 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
586 if (len == -1) goto end; buf += len; size -= len;
587 if (pScrollInfo->fMask & SIF_POS)
588 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
590 if (len == -1) goto end; buf += len; size -= len;
591 if (pScrollInfo->fMask & SIF_TRACKPOS)
592 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
594 if (len == -1) goto end; buf += len; size -= len;
597 buf = text + strlen(text);
599 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
603 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
605 if (!plvnm) return "(null)";
606 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
607 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
608 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
609 plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam);
612 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
614 char* buf = debug_getbuf(), *text = buf;
615 int len, size = DEBUG_BUFFER_SIZE;
617 if (lpLVItem == NULL) return "(null)";
618 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
619 if (len == -1) goto end; buf += len; size -= len;
620 if (lpLVItem->mask & LVIF_STATE)
621 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
623 if (len == -1) goto end; buf += len; size -= len;
624 if (lpLVItem->mask & LVIF_TEXT)
625 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
627 if (len == -1) goto end; buf += len; size -= len;
628 if (lpLVItem->mask & LVIF_IMAGE)
629 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
631 if (len == -1) goto end; buf += len; size -= len;
632 if (lpLVItem->mask & LVIF_PARAM)
633 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
635 if (len == -1) goto end; buf += len; size -= len;
636 if (lpLVItem->mask & LVIF_INDENT)
637 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
639 if (len == -1) goto end; buf += len; size -= len;
642 buf = text + strlen(text);
644 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
648 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
650 char* buf = debug_getbuf(), *text = buf;
651 int len, size = DEBUG_BUFFER_SIZE;
653 if (lpColumn == NULL) return "(null)";
654 len = snprintf(buf, size, "{");
655 if (len == -1) goto end; buf += len; size -= len;
656 if (lpColumn->mask & LVCF_SUBITEM)
657 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
659 if (len == -1) goto end; buf += len; size -= len;
660 if (lpColumn->mask & LVCF_FMT)
661 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
663 if (len == -1) goto end; buf += len; size -= len;
664 if (lpColumn->mask & LVCF_WIDTH)
665 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
667 if (len == -1) goto end; buf += len; size -= len;
668 if (lpColumn->mask & LVCF_TEXT)
669 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
671 if (len == -1) goto end; buf += len; size -= len;
672 if (lpColumn->mask & LVCF_IMAGE)
673 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
675 if (len == -1) goto end; buf += len; size -= len;
676 if (lpColumn->mask & LVCF_ORDER)
677 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
679 if (len == -1) goto end; buf += len; size -= len;
682 buf = text + strlen(text);
684 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
688 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
690 if (!lpht) return "(null)";
692 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
693 wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
696 /* Return the corresponding text for a given scroll value */
697 static inline LPCSTR debugscrollcode(int nScrollCode)
701 case SB_LINELEFT: return "SB_LINELEFT";
702 case SB_LINERIGHT: return "SB_LINERIGHT";
703 case SB_PAGELEFT: return "SB_PAGELEFT";
704 case SB_PAGERIGHT: return "SB_PAGERIGHT";
705 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
706 case SB_THUMBTRACK: return "SB_THUMBTRACK";
707 case SB_ENDSCROLL: return "SB_ENDSCROLL";
708 case SB_INTERNAL: return "SB_INTERNAL";
709 default: return "unknown";
714 /******** Notification functions ************************************/
716 static LRESULT notify_forward_header(const LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
718 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
719 (WPARAM)lpnmh->hdr.idFrom, (LPARAM)lpnmh);
722 static LRESULT notify_hdr(const LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
726 TRACE("(code=%d)\n", code);
728 pnmh->hwndFrom = infoPtr->hwndSelf;
729 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
731 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, pnmh->idFrom, (LPARAM)pnmh);
733 TRACE(" <= %ld\n", result);
738 static inline BOOL notify(const LISTVIEW_INFO *infoPtr, INT code)
741 HWND hwnd = infoPtr->hwndSelf;
742 notify_hdr(infoPtr, code, &nmh);
743 return IsWindow(hwnd);
746 static inline void notify_itemactivate(const LISTVIEW_INFO *infoPtr, const LVHITTESTINFO *htInfo)
757 item.mask = LVIF_PARAM|LVIF_STATE;
758 item.iItem = htInfo->iItem;
760 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
761 nmia.lParam = item.lParam;
762 nmia.uOldState = item.state;
763 nmia.uNewState = item.state | LVIS_ACTIVATING;
764 nmia.uChanged = LVIF_STATE;
767 nmia.iItem = htInfo->iItem;
768 nmia.iSubItem = htInfo->iSubItem;
769 nmia.ptAction = htInfo->pt;
771 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
772 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
773 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
775 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
778 static inline LRESULT notify_listview(const LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
780 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
781 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
784 static BOOL notify_click(const LISTVIEW_INFO *infoPtr, INT code, const LVHITTESTINFO *lvht)
788 HWND hwnd = infoPtr->hwndSelf;
790 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
791 ZeroMemory(&nmlv, sizeof(nmlv));
792 nmlv.iItem = lvht->iItem;
793 nmlv.iSubItem = lvht->iSubItem;
794 nmlv.ptAction = lvht->pt;
795 item.mask = LVIF_PARAM;
796 item.iItem = lvht->iItem;
798 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
799 notify_listview(infoPtr, code, &nmlv);
800 return IsWindow(hwnd);
803 static BOOL notify_deleteitem(const LISTVIEW_INFO *infoPtr, INT nItem)
807 HWND hwnd = infoPtr->hwndSelf;
809 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
811 item.mask = LVIF_PARAM;
814 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
815 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
816 return IsWindow(hwnd);
819 static int get_ansi_notification(UINT unicodeNotificationCode)
821 switch (unicodeNotificationCode)
823 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
824 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
825 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
826 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
827 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
828 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
830 ERR("unknown notification %x\n", unicodeNotificationCode);
836 Send notification. depends on dispinfoW having same
837 structure as dispinfoA.
838 infoPtr : listview struct
839 notificationCode : *Unicode* notification code
840 pdi : dispinfo structure (can be unicode or ansi)
841 isW : TRUE if dispinfo is Unicode
843 static BOOL notify_dispinfoT(const LISTVIEW_INFO *infoPtr, UINT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
845 BOOL bResult = FALSE;
846 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
847 INT cchTempBufMax = 0, savCchTextMax = 0;
849 LPWSTR pszTempBuf = NULL, savPszText = NULL;
851 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
853 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
854 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
857 if (convertToAnsi || convertToUnicode)
859 if (notificationCode != LVN_GETDISPINFOW)
861 cchTempBufMax = convertToUnicode ?
862 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
863 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
867 cchTempBufMax = pdi->item.cchTextMax;
868 *pdi->item.pszText = 0; /* make sure we don't process garbage */
871 pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
872 if (!pszTempBuf) return FALSE;
874 if (convertToUnicode)
875 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
876 pszTempBuf, cchTempBufMax);
878 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
879 cchTempBufMax, NULL, NULL);
881 savCchTextMax = pdi->item.cchTextMax;
882 savPszText = pdi->item.pszText;
883 pdi->item.pszText = pszTempBuf;
884 pdi->item.cchTextMax = cchTempBufMax;
887 if (infoPtr->notifyFormat == NFR_ANSI)
888 realNotifCode = get_ansi_notification(notificationCode);
890 realNotifCode = notificationCode;
891 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
892 bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
894 if (convertToUnicode || convertToAnsi)
896 if (convertToUnicode) /* note : pointer can be changed by app ! */
897 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
898 savCchTextMax, NULL, NULL);
900 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
901 savPszText, savCchTextMax);
902 pdi->item.pszText = savPszText; /* restores our buffer */
903 pdi->item.cchTextMax = savCchTextMax;
909 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, const LISTVIEW_INFO *infoPtr, HDC hdc,
910 const RECT *rcBounds, const LVITEMW *lplvItem)
912 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
913 lpnmlvcd->nmcd.hdc = hdc;
914 lpnmlvcd->nmcd.rc = *rcBounds;
915 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
916 lpnmlvcd->clrText = infoPtr->clrText;
917 if (!lplvItem) return;
918 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
919 lpnmlvcd->iSubItem = lplvItem->iSubItem;
920 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
921 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
922 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
923 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
926 static inline DWORD notify_customdraw (const LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
928 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
931 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
932 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
933 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
934 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
935 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
936 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
940 static void prepaint_setup (const LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd, BOOL SubItem)
942 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
943 lpnmlvcd->clrTextBk = comctl32_color.clrWindow;
944 if (lpnmlvcd->clrText == CLR_DEFAULT)
945 lpnmlvcd->clrText = comctl32_color.clrWindowText;
947 /* apparently, for selected items, we have to override the returned values */
950 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
954 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
955 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
957 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
959 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
960 lpnmlvcd->clrText = comctl32_color.clrBtnText;
965 /* Set the text attributes */
966 if (lpnmlvcd->clrTextBk != CLR_NONE)
968 SetBkMode(hdc, OPAQUE);
969 SetBkColor(hdc,lpnmlvcd->clrTextBk);
972 SetBkMode(hdc, TRANSPARENT);
973 SetTextColor(hdc, lpnmlvcd->clrText);
976 static inline DWORD notify_postpaint (const LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
978 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
981 /******** Item iterator functions **********************************/
983 static RANGES ranges_create(int count);
984 static void ranges_destroy(RANGES ranges);
985 static BOOL ranges_add(RANGES ranges, RANGE range);
986 static BOOL ranges_del(RANGES ranges, RANGE range);
987 static void ranges_dump(RANGES ranges);
989 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
991 RANGE range = { nItem, nItem + 1 };
993 return ranges_add(ranges, range);
996 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
998 RANGE range = { nItem, nItem + 1 };
1000 return ranges_del(ranges, range);
1004 * ITERATOR DOCUMENTATION
1006 * The iterator functions allow for easy, and convenient iteration
1007 * over items of interest in the list. Typically, you create a
1008 * iterator, use it, and destroy it, as such:
1011 * iterator_xxxitems(&i, ...);
1012 * while (iterator_{prev,next}(&i)
1014 * //code which uses i.nItem
1016 * iterator_destroy(&i);
1018 * where xxx is either: framed, or visible.
1019 * Note that it is important that the code destroys the iterator
1020 * after it's done with it, as the creation of the iterator may
1021 * allocate memory, which thus needs to be freed.
1023 * You can iterate both forwards, and backwards through the list,
1024 * by using iterator_next or iterator_prev respectively.
1026 * Lower numbered items are draw on top of higher number items in
1027 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1028 * items may overlap). So, to test items, you should use
1030 * which lists the items top to bottom (in Z-order).
1031 * For drawing items, you should use
1033 * which lists the items bottom to top (in Z-order).
1034 * If you keep iterating over the items after the end-of-items
1035 * marker (-1) is returned, the iterator will start from the
1036 * beginning. Typically, you don't need to test for -1,
1037 * because iterator_{next,prev} will return TRUE if more items
1038 * are to be iterated over, or FALSE otherwise.
1040 * Note: the iterator is defined to be bidirectional. That is,
1041 * any number of prev followed by any number of next, or
1042 * five versa, should leave the iterator at the same item:
1043 * prev * n, next * n = next * n, prev * n
1045 * The iterator has a notion of an out-of-order, special item,
1046 * which sits at the start of the list. This is used in
1047 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1048 * which needs to be first, as it may overlap other items.
1050 * The code is a bit messy because we have:
1051 * - a special item to deal with
1052 * - simple range, or composite range
1054 * If you find bugs, or want to add features, please make sure you
1055 * always check/modify *both* iterator_prev, and iterator_next.
1059 * This function iterates through the items in increasing order,
1060 * but prefixed by the special item, then -1. That is:
1061 * special, 1, 2, 3, ..., n, -1.
1062 * Each item is listed only once.
1064 static inline BOOL iterator_next(ITERATOR* i)
1068 i->nItem = i->nSpecial;
1069 if (i->nItem != -1) return TRUE;
1071 if (i->nItem == i->nSpecial)
1073 if (i->ranges) i->index = 0;
1079 if (i->nItem == i->nSpecial) i->nItem++;
1080 if (i->nItem < i->range.upper) return TRUE;
1085 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1086 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1089 else if (i->nItem >= i->range.upper) goto end;
1091 i->nItem = i->range.lower;
1092 if (i->nItem >= 0) goto testitem;
1099 * This function iterates through the items in decreasing order,
1100 * followed by the special item, then -1. That is:
1101 * n, n-1, ..., 3, 2, 1, special, -1.
1102 * Each item is listed only once.
1104 static inline BOOL iterator_prev(ITERATOR* i)
1111 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1114 if (i->nItem == i->nSpecial)
1122 if (i->nItem == i->nSpecial) i->nItem--;
1123 if (i->nItem >= i->range.lower) return TRUE;
1129 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1132 else if (!start && i->nItem < i->range.lower) goto end;
1134 i->nItem = i->range.upper;
1135 if (i->nItem > 0) goto testitem;
1137 return (i->nItem = i->nSpecial) != -1;
1140 static RANGE iterator_range(const ITERATOR *i)
1144 if (!i->ranges) return i->range;
1146 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1148 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1149 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1151 else range.lower = range.upper = 0;
1157 * Releases resources associated with this ierator.
1159 static inline void iterator_destroy(const ITERATOR *i)
1161 ranges_destroy(i->ranges);
1165 * Create an empty iterator.
1167 static inline BOOL iterator_empty(ITERATOR* i)
1169 ZeroMemory(i, sizeof(*i));
1170 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1175 * Create an iterator over a range.
1177 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1185 * Create an iterator over a bunch of ranges.
1186 * Please note that the iterator will take ownership of the ranges,
1187 * and will free them upon destruction.
1189 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1197 * Creates an iterator over the items which intersect lprc.
1199 static BOOL iterator_frameditems(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *lprc)
1201 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1202 RECT frame = *lprc, rcItem, rcTemp;
1205 /* in case we fail, we want to return an empty iterator */
1206 if (!iterator_empty(i)) return FALSE;
1208 LISTVIEW_GetOrigin(infoPtr, &Origin);
1210 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc));
1211 OffsetRect(&frame, -Origin.x, -Origin.y);
1213 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1217 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1219 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1220 if (IntersectRect(&rcTemp, &rcItem, lprc))
1221 i->nSpecial = infoPtr->nFocusedItem;
1223 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1224 /* to do better here, we need to have PosX, and PosY sorted */
1225 TRACE("building icon ranges:\n");
1226 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1228 rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1229 rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1230 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1231 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1232 if (IntersectRect(&rcTemp, &rcItem, &frame))
1233 ranges_additem(i->ranges, nItem);
1237 else if (uView == LVS_REPORT)
1241 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1242 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1244 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1245 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1246 if (range.upper <= range.lower) return TRUE;
1247 if (!iterator_rangeitems(i, range)) return FALSE;
1248 TRACE(" report=%s\n", debugrange(&i->range));
1252 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1253 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1254 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1255 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1256 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1257 INT lower = nFirstCol * nPerCol + nFirstRow;
1261 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1262 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1264 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1266 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1267 TRACE("building list ranges:\n");
1268 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1270 item_range.lower = nCol * nPerCol + nFirstRow;
1271 if(item_range.lower >= infoPtr->nItemCount) break;
1272 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1273 TRACE(" list=%s\n", debugrange(&item_range));
1274 ranges_add(i->ranges, item_range);
1282 * Creates an iterator over the items which intersect the visible region of hdc.
1284 static BOOL iterator_visibleitems(ITERATOR *i, const LISTVIEW_INFO *infoPtr, HDC hdc)
1286 POINT Origin, Position;
1287 RECT rcItem, rcClip;
1290 rgntype = GetClipBox(hdc, &rcClip);
1291 if (rgntype == NULLREGION) return iterator_empty(i);
1292 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1293 if (rgntype == SIMPLEREGION) return TRUE;
1295 /* first deal with the special item */
1296 if (i->nSpecial != -1)
1298 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1299 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1302 /* if we can't deal with the region, we'll just go with the simple range */
1303 LISTVIEW_GetOrigin(infoPtr, &Origin);
1304 TRACE("building visible range:\n");
1305 if (!i->ranges && i->range.lower < i->range.upper)
1307 if (!(i->ranges = ranges_create(50))) return TRUE;
1308 if (!ranges_add(i->ranges, i->range))
1310 ranges_destroy(i->ranges);
1316 /* now delete the invisible items from the list */
1317 while(iterator_next(i))
1319 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1320 rcItem.left = Position.x + Origin.x;
1321 rcItem.top = Position.y + Origin.y;
1322 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1323 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1324 if (!RectVisible(hdc, &rcItem))
1325 ranges_delitem(i->ranges, i->nItem);
1327 /* the iterator should restart on the next iterator_next */
1333 /******** Misc helper functions ************************************/
1335 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1336 WPARAM wParam, LPARAM lParam, BOOL isW)
1338 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1339 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1342 static inline BOOL is_autoarrange(const LISTVIEW_INFO *infoPtr)
1344 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1346 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1347 (uView == LVS_ICON || uView == LVS_SMALLICON);
1350 static void toggle_checkbox_state(LISTVIEW_INFO *infoPtr, INT nItem)
1352 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
1353 if(state == 1 || state == 2)
1357 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
1358 lvitem.stateMask = LVIS_STATEIMAGEMASK;
1359 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
1363 /******** Internal API functions ************************************/
1365 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(const LISTVIEW_INFO *infoPtr, INT nSubItem)
1367 static COLUMN_INFO mainItem;
1369 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1370 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1371 return DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1374 static INT LISTVIEW_CreateHeader(LISTVIEW_INFO *infoPtr)
1376 DWORD dFlags = WS_CHILD | HDS_HORZ | HDS_FULLDRAG | HDS_DRAGDROP;
1379 if (infoPtr->hwndHeader) return 0;
1381 TRACE("Creating header for list %p\n", infoPtr->hwndSelf);
1383 /* setup creation flags */
1384 dFlags |= (LVS_NOSORTHEADER & infoPtr->dwStyle) ? 0 : HDS_BUTTONS;
1385 dFlags |= (LVS_NOCOLUMNHEADER & infoPtr->dwStyle) ? HDS_HIDDEN : 0;
1387 hInst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
1390 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL, dFlags,
1391 0, 0, 0, 0, infoPtr->hwndSelf, NULL, hInst, NULL);
1392 if (!infoPtr->hwndHeader) return -1;
1394 /* set header unicode format */
1395 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, TRUE, 0);
1397 /* set header font */
1398 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
1400 LISTVIEW_UpdateSize(infoPtr);
1405 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO *infoPtr, INT nSubItem, LPRECT lprc)
1407 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1410 static inline BOOL LISTVIEW_GetItemW(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1412 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1415 /* 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 ( (infoPtr->dwStyle & LVS_TYPEMASK) == 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 DrawFocusRect(hdc, &infoPtr->rcFocus);
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 WORD wDragWidth = GetSystemMetrics(SM_CXDRAG);
3393 WORD wDragHeight= GetSystemMetrics(SM_CYDRAG);
3395 rect.left = infoPtr->ptClickPos.x - wDragWidth;
3396 rect.right = infoPtr->ptClickPos.x + wDragWidth;
3397 rect.top = infoPtr->ptClickPos.y - wDragHeight;
3398 rect.bottom = infoPtr->ptClickPos.y + wDragHeight;
3403 if (!PtInRect(&rect, tmp))
3405 LVHITTESTINFO lvHitTestInfo;
3408 lvHitTestInfo.pt = infoPtr->ptClickPos;
3409 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3411 ZeroMemory(&nmlv, sizeof(nmlv));
3412 nmlv.iItem = lvHitTestInfo.iItem;
3413 nmlv.ptAction = infoPtr->ptClickPos;
3415 if (!infoPtr->bDragging)
3417 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3418 infoPtr->bDragging = TRUE;
3425 infoPtr->bLButtonDown = FALSE;
3427 /* see if we are supposed to be tracking mouse hovering */
3428 if (LISTVIEW_isHotTracking(infoPtr)) {
3429 /* fill in the trackinfo struct */
3430 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3431 trackinfo.dwFlags = TME_QUERY;
3432 trackinfo.hwndTrack = infoPtr->hwndSelf;
3433 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3435 /* see if we are already tracking this hwnd */
3436 _TrackMouseEvent(&trackinfo);
3438 if(!(trackinfo.dwFlags & TME_HOVER)) {
3439 trackinfo.dwFlags = TME_HOVER;
3441 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3442 _TrackMouseEvent(&trackinfo);
3451 * Tests whether the item is assignable to a list with style lStyle
3453 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3455 if ( (lpLVItem->mask & LVIF_TEXT) &&
3456 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3457 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3465 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3468 * [I] infoPtr : valid pointer to the listview structure
3469 * [I] lpLVItem : valid pointer to new item attributes
3470 * [I] isNew : the item being set is being inserted
3471 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3472 * [O] bChanged : will be set to TRUE if the item really changed
3478 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3480 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3485 /* stateMask is ignored for LVM_INSERTITEM */
3486 UINT stateMask = isNew ? ~0 : lpLVItem->stateMask;
3490 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3492 if (lpLVItem->mask == 0) return TRUE;
3494 if (infoPtr->dwStyle & LVS_OWNERDATA)
3496 /* a virtual listview only stores selection and focus */
3497 if (lpLVItem->mask & ~LVIF_STATE)
3503 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3504 lpItem = DPA_GetPtr(hdpaSubItems, 0);
3508 /* we need to get the lParam and state of the item */
3509 item.iItem = lpLVItem->iItem;
3510 item.iSubItem = lpLVItem->iSubItem;
3511 item.mask = LVIF_STATE | LVIF_PARAM;
3512 item.stateMask = ~0;
3515 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3517 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3518 /* determine what fields will change */
3519 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & stateMask & ~infoPtr->uCallbackMask))
3520 uChanged |= LVIF_STATE;
3522 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3523 uChanged |= LVIF_IMAGE;
3525 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3526 uChanged |= LVIF_PARAM;
3528 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3529 uChanged |= LVIF_INDENT;
3531 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3532 uChanged |= LVIF_TEXT;
3534 TRACE("uChanged=0x%x\n", uChanged);
3535 if (!uChanged) return TRUE;
3538 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3539 nmlv.iItem = lpLVItem->iItem;
3540 nmlv.uNewState = (item.state & ~stateMask) | (lpLVItem->state & stateMask);
3541 nmlv.uOldState = item.state;
3542 nmlv.uChanged = uChanged;
3543 nmlv.lParam = item.lParam;
3545 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3546 /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
3548 if(lpItem && !isNew && infoPtr->bDoChangeNotify)
3550 HWND hwndSelf = infoPtr->hwndSelf;
3552 if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3554 if (!IsWindow(hwndSelf))
3558 /* copy information */
3559 if (lpLVItem->mask & LVIF_TEXT)
3560 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3562 if (lpLVItem->mask & LVIF_IMAGE)
3563 lpItem->hdr.iImage = lpLVItem->iImage;
3565 if (lpLVItem->mask & LVIF_PARAM)
3566 lpItem->lParam = lpLVItem->lParam;
3568 if (lpLVItem->mask & LVIF_INDENT)
3569 lpItem->iIndent = lpLVItem->iIndent;
3571 if (uChanged & LVIF_STATE)
3573 if (lpItem && (stateMask & ~infoPtr->uCallbackMask))
3575 lpItem->state &= ~stateMask;
3576 lpItem->state |= (lpLVItem->state & stateMask);
3578 if (lpLVItem->state & stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3580 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3581 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3583 else if (stateMask & LVIS_SELECTED)
3585 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3587 /* if we are asked to change focus, and we manage it, do it */
3588 if (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3590 if (lpLVItem->state & LVIS_FOCUSED)
3592 if (infoPtr->nFocusedItem != -1)
3594 /* remove current focus */
3595 item.mask = LVIF_STATE;
3597 item.stateMask = LVIS_FOCUSED;
3599 /* recurse with redrawing an item */
3600 LISTVIEW_SetItemState(infoPtr, infoPtr->nFocusedItem, &item);
3603 infoPtr->nFocusedItem = lpLVItem->iItem;
3604 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3606 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3608 infoPtr->nFocusedItem = -1;
3613 /* if we're inserting the item, we're done */
3614 if (isNew) return TRUE;
3616 /* send LVN_ITEMCHANGED notification */
3617 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3618 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3625 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3628 * [I] infoPtr : valid pointer to the listview structure
3629 * [I] lpLVItem : valid pointer to new subitem attributes
3630 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3631 * [O] bChanged : will be set to TRUE if the item really changed
3637 static BOOL set_sub_item(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3640 SUBITEM_INFO *lpSubItem;
3642 /* we do not support subitems for virtual listviews */
3643 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3645 /* set subitem only if column is present */
3646 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3648 /* First do some sanity checks */
3649 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
3650 particularly useful. We currently do not actually do anything with
3651 the flag on subitems.
3653 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)) return FALSE;
3654 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))) return TRUE;
3656 /* get the subitem structure, and create it if not there */
3657 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3658 assert (hdpaSubItems);
3660 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3663 SUBITEM_INFO *tmpSubItem;
3666 lpSubItem = Alloc(sizeof(SUBITEM_INFO));
3667 if (!lpSubItem) return FALSE;
3668 /* we could binary search here, if need be...*/
3669 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3671 tmpSubItem = DPA_GetPtr(hdpaSubItems, i);
3672 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3674 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3679 lpSubItem->iSubItem = lpLVItem->iSubItem;
3680 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3684 if (lpLVItem->mask & LVIF_IMAGE)
3685 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3687 lpSubItem->hdr.iImage = lpLVItem->iImage;
3691 if (lpLVItem->mask & LVIF_TEXT)
3692 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3694 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3703 * Sets item attributes.
3706 * [I] infoPtr : valid pointer to the listview structure
3707 * [I] lpLVItem : new item attributes
3708 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3714 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem, BOOL isW)
3716 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3717 HWND hwndSelf = infoPtr->hwndSelf;
3718 LPWSTR pszText = NULL;
3719 BOOL bResult, bChanged = FALSE;
3721 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3723 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3726 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3727 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3729 pszText = lpLVItem->pszText;
3730 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
3733 /* actually set the fields */
3734 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3736 if (lpLVItem->iSubItem)
3737 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3739 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3740 if (!IsWindow(hwndSelf))
3743 /* redraw item, if necessary */
3744 if (bChanged && !infoPtr->bIsDrawing)
3746 /* this little optimization eliminates some nasty flicker */
3747 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3748 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
3749 lpLVItem->iSubItem > 0 && lpLVItem->iSubItem <= DPA_GetPtrCount(infoPtr->hdpaColumns) )
3750 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3752 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3757 textfreeT(lpLVItem->pszText, isW);
3758 lpLVItem->pszText = pszText;
3766 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3769 * [I] infoPtr : valid pointer to the listview structure
3774 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *infoPtr)
3776 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3778 SCROLLINFO scrollInfo;
3780 scrollInfo.cbSize = sizeof(SCROLLINFO);
3781 scrollInfo.fMask = SIF_POS;
3783 if (uView == LVS_LIST)
3785 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3786 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3788 else if (uView == LVS_REPORT)
3790 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3791 nItem = scrollInfo.nPos;
3795 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3796 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3799 TRACE("nItem=%d\n", nItem);
3807 * Erases the background of the given rectangle
3810 * [I] infoPtr : valid pointer to the listview structure
3811 * [I] hdc : device context handle
3812 * [I] lprcBox : clipping rectangle
3818 static inline BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3820 if (!infoPtr->hBkBrush) return FALSE;
3822 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
3824 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3832 * [I] infoPtr : valid pointer to the listview structure
3833 * [I] hdc : device context handle
3834 * [I] nItem : item index
3835 * [I] nSubItem : subitem index
3836 * [I] pos : item position in client coordinates
3837 * [I] cdmode : custom draw mode
3843 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3845 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3846 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3847 static WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3848 DWORD cdsubitemmode = CDRF_DODEFAULT;
3850 RECT rcSelect, rcBox, rcIcon, rcLabel, rcStateIcon;
3851 NMLVCUSTOMDRAW nmlvcd;
3856 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
3858 /* get information needed for drawing the item */
3859 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
3860 if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
3861 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3862 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3863 lvItem.iItem = nItem;
3864 lvItem.iSubItem = nSubItem;
3867 lvItem.cchTextMax = DISP_TEXT_SIZE;
3868 lvItem.pszText = szDispText;
3869 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3870 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3871 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3872 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
3873 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3875 /* now check if we need to update the focus rectangle */
3876 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3878 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3879 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
3880 OffsetRect(&rcBox, pos.x, pos.y);
3881 OffsetRect(&rcSelect, pos.x, pos.y);
3882 OffsetRect(&rcIcon, pos.x, pos.y);
3883 OffsetRect(&rcStateIcon, pos.x, pos.y);
3884 OffsetRect(&rcLabel, pos.x, pos.y);
3885 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
3886 wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect),
3887 wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
3889 /* fill in the custom draw structure */
3890 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3892 hOldFont = GetCurrentObject(hdc, OBJ_FONT);
3893 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3894 if (cdmode & CDRF_SKIPDEFAULT) goto postpaint;
3895 if (cdmode & CDRF_NOTIFYITEMDRAW)
3896 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3897 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3898 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3899 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3900 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3902 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3903 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3905 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3906 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
3907 else if ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) == FALSE)
3908 prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE);
3910 /* in full row select, subitems, will just use main item's colors */
3911 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3912 nmlvcd.clrTextBk = CLR_NONE;
3915 if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && (nSubItem == 0))
3917 UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
3920 TRACE("uStateImage=%d\n", uStateImage);
3921 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
3922 rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
3927 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3928 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3930 TRACE("iImage=%d\n", lvItem.iImage);
3931 ImageList_DrawEx(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3932 rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk, CLR_DEFAULT,
3933 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3936 /* Don't bother painting item being edited */
3937 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3939 /* FIXME: temporary hack */
3940 rcSelect.left = rcLabel.left;
3942 /* draw the selection background, if we're drawing the main item */
3945 /* in icon mode, the label rect is really what we want to draw the
3947 if (uView == LVS_ICON)
3950 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3951 rcSelect.right = rcBox.right;
3953 if (nmlvcd.clrTextBk != CLR_NONE)
3954 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3955 if(lprcFocus) *lprcFocus = rcSelect;
3958 /* figure out the text drawing flags */
3959 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3960 if (uView == LVS_ICON)
3961 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3964 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3966 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3967 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3968 default: uFormat |= DT_LEFT;
3971 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3973 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3974 else rcLabel.left += LABEL_HOR_PADDING;
3976 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3978 /* for GRIDLINES reduce the bottom so the text formats correctly */
3979 if (uView == LVS_REPORT && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
3982 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3985 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3986 notify_postpaint(infoPtr, &nmlvcd);
3987 if (cdsubitemmode & CDRF_NEWFONT)
3988 SelectObject(hdc, hOldFont);
3994 * Draws listview items when in owner draw mode.
3997 * [I] infoPtr : valid pointer to the listview structure
3998 * [I] hdc : device context handle
4003 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4005 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
4006 DWORD cditemmode = CDRF_DODEFAULT;
4007 NMLVCUSTOMDRAW nmlvcd;
4008 POINT Origin, Position;
4014 ZeroMemory(&dis, sizeof(dis));
4016 /* Get scroll info once before loop */
4017 LISTVIEW_GetOrigin(infoPtr, &Origin);
4019 /* iterate through the invalidated rows */
4020 while(iterator_next(i))
4022 item.iItem = i->nItem;
4024 item.mask = LVIF_PARAM | LVIF_STATE;
4025 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4026 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
4028 dis.CtlType = ODT_LISTVIEW;
4030 dis.itemID = item.iItem;
4031 dis.itemAction = ODA_DRAWENTIRE;
4033 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
4034 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
4035 dis.hwndItem = infoPtr->hwndSelf;
4037 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
4038 dis.rcItem.left = Position.x + Origin.x;
4039 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
4040 dis.rcItem.top = Position.y + Origin.y;
4041 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
4042 dis.itemData = item.lParam;
4044 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
4047 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
4048 * structure for the rest. of the paint cycle
4050 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
4051 if (cdmode & CDRF_NOTIFYITEMDRAW)
4052 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4054 if (!(cditemmode & CDRF_SKIPDEFAULT))
4056 prepaint_setup (infoPtr, hdc, &nmlvcd, FALSE);
4057 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
4060 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
4061 notify_postpaint(infoPtr, &nmlvcd);
4067 * Draws listview items when in report display mode.
4070 * [I] infoPtr : valid pointer to the listview structure
4071 * [I] hdc : device context handle
4072 * [I] cdmode : custom draw mode
4077 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4080 RECT rcClip, rcItem;
4081 POINT Origin, Position;
4087 /* figure out what to draw */
4088 rgntype = GetClipBox(hdc, &rcClip);
4089 if (rgntype == NULLREGION) return;
4091 /* Get scroll info once before loop */
4092 LISTVIEW_GetOrigin(infoPtr, &Origin);
4094 /* narrow down the columns we need to paint */
4095 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
4097 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
4098 if (rcItem.right + Origin.x >= rcClip.left) break;
4100 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
4102 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
4103 if (rcItem.left + Origin.x < rcClip.right) break;
4105 iterator_rangeitems(&j, colRange);
4107 /* in full row select, we _have_ to draw the main item */
4108 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
4111 /* iterate through the invalidated rows */
4112 while(iterator_next(i))
4114 /* iterate through the invalidated columns */
4115 while(iterator_next(&j))
4117 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4118 Position.x += Origin.x;
4119 Position.y += Origin.y;
4121 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
4123 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4125 rcItem.bottom = infoPtr->nItemHeight;
4126 OffsetRect(&rcItem, Position.x, Position.y);
4127 if (!RectVisible(hdc, &rcItem)) continue;
4130 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
4133 iterator_destroy(&j);
4138 * Draws the gridlines if necessary when in report display mode.
4141 * [I] infoPtr : valid pointer to the listview structure
4142 * [I] hdc : device context handle
4147 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO *infoPtr, HDC hdc)
4152 RECT rcClip, rcItem;
4160 /* figure out what to draw */
4161 rgntype = GetClipBox(hdc, &rcClip);
4162 if (rgntype == NULLREGION) return;
4164 /* Get scroll info once before loop */
4165 LISTVIEW_GetOrigin(infoPtr, &Origin);
4167 /* narrow down the columns we need to paint */
4168 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
4170 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
4171 if (rcItem.right + Origin.x >= rcClip.left) break;
4173 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
4175 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
4176 if (rcItem.left + Origin.x < rcClip.right) break;
4178 /* is right most vertical line visible? */
4179 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcItem);
4180 rmost = (rcItem.right + Origin.x < rcClip.right);
4182 if ((hPen = CreatePen( PS_SOLID, 1, comctl32_color.clr3dFace )))
4184 hOldPen = SelectObject ( hdc, hPen );
4186 /* draw the vertical lines for the columns */
4187 iterator_rangeitems(&j, colRange);
4188 while(iterator_next(&j))
4190 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4191 if (rcItem.left == 0) continue; /* skip first column */
4192 rcItem.left += Origin.x;
4193 rcItem.right += Origin.x;
4194 rcItem.top = infoPtr->rcList.top;
4195 rcItem.bottom = infoPtr->rcList.bottom;
4196 TRACE("vert col=%d, rcItem=%s\n", j.nItem, wine_dbgstr_rect(&rcItem));
4197 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4198 LineTo (hdc, rcItem.left, rcItem.bottom);
4200 iterator_destroy(&j);
4201 /* draw rightmost grid line if visible */
4204 MoveToEx (hdc, rcItem.right, rcItem.top, NULL);
4205 LineTo (hdc, rcItem.right, rcItem.bottom);
4208 /* draw the horizontial lines for the rows */
4209 itemheight = LISTVIEW_CalculateItemHeight(infoPtr);
4210 rcItem.left = infoPtr->rcList.left;
4211 rcItem.right = infoPtr->rcList.right;
4212 rcItem.bottom = rcItem.top = Origin.y - 1;
4213 MoveToEx(hdc, rcItem.left, rcItem.top, NULL);
4214 LineTo(hdc, rcItem.right, rcItem.top);
4215 for(y=itemheight-1+Origin.y; y<=infoPtr->rcList.bottom; y+=itemheight)
4217 rcItem.bottom = rcItem.top = y;
4218 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem));
4219 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4220 LineTo (hdc, rcItem.right, rcItem.top);
4223 SelectObject( hdc, hOldPen );
4224 DeleteObject( hPen );
4230 * Draws listview items when in list display mode.
4233 * [I] infoPtr : valid pointer to the listview structure
4234 * [I] hdc : device context handle
4235 * [I] cdmode : custom draw mode
4240 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4242 POINT Origin, Position;
4244 /* Get scroll info once before loop */
4245 LISTVIEW_GetOrigin(infoPtr, &Origin);
4247 while(iterator_prev(i))
4249 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4250 Position.x += Origin.x;
4251 Position.y += Origin.y;
4253 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
4260 * Draws listview items.
4263 * [I] infoPtr : valid pointer to the listview structure
4264 * [I] hdc : device context handle
4265 * [I] prcErase : rect to be erased before refresh (may be NULL)
4270 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcErase)
4272 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4273 COLORREF oldTextColor = 0, oldBkColor = 0, oldClrTextBk, oldClrText;
4274 NMLVCUSTOMDRAW nmlvcd;
4281 HBITMAP hbmp = NULL;
4283 LISTVIEW_DUMP(infoPtr);
4285 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4286 TRACE("double buffering\n");
4288 hdc = CreateCompatibleDC(hdcOrig);
4290 ERR("Failed to create DC for backbuffer\n");
4293 hbmp = CreateCompatibleBitmap(hdcOrig, infoPtr->rcList.right,
4294 infoPtr->rcList.bottom);
4296 ERR("Failed to create bitmap for backbuffer\n");
4301 SelectObject(hdc, hbmp);
4302 SelectObject(hdc, infoPtr->hFont);
4304 /* Save dc values we're gonna trash while drawing
4305 * FIXME: Should be done in LISTVIEW_DrawItem() */
4306 hOldFont = SelectObject(hdc, infoPtr->hFont);
4307 oldBkMode = GetBkMode(hdc);
4308 oldBkColor = GetBkColor(hdc);
4309 oldTextColor = GetTextColor(hdc);
4312 infoPtr->bIsDrawing = TRUE;
4315 LISTVIEW_FillBkgnd(infoPtr, hdc, prcErase);
4316 } else if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4317 /* If no erasing was done (usually because RedrawWindow was called
4318 * with RDW_INVALIDATE only) we need to copy the old contents into
4319 * the backbuffer before continuing. */
4320 BitBlt(hdc, infoPtr->rcList.left, infoPtr->rcList.top,
4321 infoPtr->rcList.right - infoPtr->rcList.left,
4322 infoPtr->rcList.bottom - infoPtr->rcList.top,
4323 hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4326 /* FIXME: Shouldn't need to do this */
4327 oldClrTextBk = infoPtr->clrTextBk;
4328 oldClrText = infoPtr->clrText;
4330 infoPtr->cditemmode = CDRF_DODEFAULT;
4332 GetClientRect(infoPtr->hwndSelf, &rcClient);
4333 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4334 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4335 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4336 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4338 /* Use these colors to draw the items */
4339 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4340 infoPtr->clrText = nmlvcd.clrText;
4342 /* nothing to draw */
4343 if(infoPtr->nItemCount == 0) goto enddraw;
4345 /* figure out what we need to draw */
4346 iterator_visibleitems(&i, infoPtr, hdc);
4348 /* send cache hint notification */
4349 if (infoPtr->dwStyle & LVS_OWNERDATA)
4351 RANGE range = iterator_range(&i);
4354 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4355 nmlv.iFrom = range.lower;
4356 nmlv.iTo = range.upper - 1;
4357 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4360 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4361 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4364 if (uView == LVS_REPORT)
4365 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4366 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4367 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4369 /* if we have a focus rect, draw it */
4370 if (infoPtr->bFocus)
4371 DrawFocusRect(hdc, &infoPtr->rcFocus);
4373 iterator_destroy(&i);
4376 /* For LVS_EX_GRIDLINES go and draw lines */
4377 /* This includes the case where there were *no* items */
4378 if ((uView == LVS_REPORT) && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
4379 LISTVIEW_RefreshReportGrid(infoPtr, hdc);
4381 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4382 notify_postpaint(infoPtr, &nmlvcd);
4384 infoPtr->clrTextBk = oldClrTextBk;
4385 infoPtr->clrText = oldClrText;
4388 BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top,
4389 infoPtr->rcList.right - infoPtr->rcList.left,
4390 infoPtr->rcList.bottom - infoPtr->rcList.top,
4391 hdc, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4396 SelectObject(hdc, hOldFont);
4397 SetBkMode(hdc, oldBkMode);
4398 SetBkColor(hdc, oldBkColor);
4399 SetTextColor(hdc, oldTextColor);
4402 infoPtr->bIsDrawing = FALSE;
4408 * Calculates the approximate width and height of a given number of items.
4411 * [I] infoPtr : valid pointer to the listview structure
4412 * [I] nItemCount : number of items
4413 * [I] wWidth : width
4414 * [I] wHeight : height
4417 * Returns a DWORD. The width in the low word and the height in high word.
4419 static DWORD LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO *infoPtr, INT nItemCount,
4420 WORD wWidth, WORD wHeight)
4422 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4423 INT nItemCountPerColumn = 1;
4424 INT nColumnCount = 0;
4425 DWORD dwViewRect = 0;
4427 if (nItemCount == -1)
4428 nItemCount = infoPtr->nItemCount;
4430 if (uView == LVS_LIST)
4432 if (wHeight == 0xFFFF)
4434 /* use current height */
4435 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4438 if (wHeight < infoPtr->nItemHeight)
4439 wHeight = infoPtr->nItemHeight;
4443 if (infoPtr->nItemHeight > 0)
4445 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4446 if (nItemCountPerColumn == 0)
4447 nItemCountPerColumn = 1;
4449 if (nItemCount % nItemCountPerColumn != 0)
4450 nColumnCount = nItemCount / nItemCountPerColumn;
4452 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4456 /* Microsoft padding magic */
4457 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4458 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4460 dwViewRect = MAKELONG(wWidth, wHeight);
4462 else if (uView == LVS_REPORT)
4466 if (infoPtr->nItemCount > 0)
4468 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4469 wWidth = rcBox.right - rcBox.left;
4470 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4474 /* use current height and width */
4475 if (wHeight == 0xffff)
4476 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4477 if (wWidth == 0xffff)
4478 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4481 dwViewRect = MAKELONG(wWidth, wHeight);
4483 else if (uView == LVS_SMALLICON)
4484 FIXME("uView == LVS_SMALLICON: not implemented\n");
4485 else if (uView == LVS_ICON)
4486 FIXME("uView == LVS_ICON: not implemented\n");
4494 * Create a drag image list for the specified item.
4497 * [I] infoPtr : valid pointer to the listview structure
4498 * [I] iItem : index of item
4499 * [O] lppt : Upper-left corner of the image
4502 * Returns a handle to the image list if successful, NULL otherwise.
4504 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4510 HBITMAP hbmp, hOldbmp;
4511 HIMAGELIST dragList = 0;
4512 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
4514 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4517 rcItem.left = LVIR_BOUNDS;
4518 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4521 lppt->x = rcItem.left;
4522 lppt->y = rcItem.top;
4524 size.cx = rcItem.right - rcItem.left;
4525 size.cy = rcItem.bottom - rcItem.top;
4527 hdcOrig = GetDC(infoPtr->hwndSelf);
4528 hdc = CreateCompatibleDC(hdcOrig);
4529 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4530 hOldbmp = SelectObject(hdc, hbmp);
4532 rcItem.left = rcItem.top = 0;
4533 rcItem.right = size.cx;
4534 rcItem.bottom = size.cy;
4535 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4538 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4540 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4541 SelectObject(hdc, hOldbmp);
4542 ImageList_Add(dragList, hbmp, 0);
4545 SelectObject(hdc, hOldbmp);
4549 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4551 TRACE("ret=%p\n", dragList);
4559 * Removes all listview items and subitems.
4562 * [I] infoPtr : valid pointer to the listview structure
4568 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr, BOOL destroy)
4571 HDPA hdpaSubItems = NULL;
4578 /* we do it directly, to avoid notifications */
4579 ranges_clear(infoPtr->selectionRanges);
4580 infoPtr->nSelectionMark = -1;
4581 infoPtr->nFocusedItem = -1;
4582 SetRectEmpty(&infoPtr->rcFocus);
4583 /* But we are supposed to leave nHotItem as is! */
4586 /* send LVN_DELETEALLITEMS notification */
4587 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4589 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4591 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4593 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4595 /* send LVN_DELETEITEM notification, if not suppressed
4596 and if it is not a virtual listview */
4597 if (!bSuppress) notify_deleteitem(infoPtr, i);
4598 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
4599 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4601 hdrItem = DPA_GetPtr(hdpaSubItems, j);
4602 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4605 DPA_Destroy(hdpaSubItems);
4606 DPA_DeletePtr(infoPtr->hdpaItems, i);
4608 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4609 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4610 infoPtr->nItemCount --;
4615 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4616 LISTVIEW_UpdateScroll(infoPtr);
4618 LISTVIEW_InvalidateList(infoPtr);
4625 * Scrolls, and updates the columns, when a column is changing width.
4628 * [I] infoPtr : valid pointer to the listview structure
4629 * [I] nColumn : column to scroll
4630 * [I] dx : amount of scroll, in pixels
4635 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4637 COLUMN_INFO *lpColumnInfo;
4642 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4643 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4644 rcCol = lpColumnInfo->rcHeader;
4645 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4646 rcCol.left = rcCol.right;
4648 /* adjust the other columns */
4649 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4651 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4652 lpColumnInfo->rcHeader.left += dx;
4653 lpColumnInfo->rcHeader.right += dx;
4656 /* do not update screen if not in report mode */
4657 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4659 /* if we have a focus, we must first erase the focus rect */
4660 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4662 /* Need to reset the item width when inserting a new column */
4663 infoPtr->nItemWidth += dx;
4665 LISTVIEW_UpdateScroll(infoPtr);
4666 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4668 /* scroll to cover the deleted column, and invalidate for redraw */
4669 rcOld = infoPtr->rcList;
4670 rcOld.left = ptOrigin.x + rcCol.left + dx;
4671 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4673 /* we can restore focus now */
4674 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4679 * Removes a column from the listview control.
4682 * [I] infoPtr : valid pointer to the listview structure
4683 * [I] nColumn : column index
4689 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4693 TRACE("nColumn=%d\n", nColumn);
4695 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4696 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4698 /* While the MSDN specifically says that column zero should not be deleted,
4699 what actually happens is that the column itself is deleted but no items or subitems
4703 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4705 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4708 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4709 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4711 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4713 SUBITEM_INFO *lpSubItem, *lpDelItem;
4715 INT nItem, nSubItem, i;
4717 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4719 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
4722 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4724 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
4725 if (lpSubItem->iSubItem == nColumn)
4728 lpDelItem = lpSubItem;
4730 else if (lpSubItem->iSubItem > nColumn)
4732 lpSubItem->iSubItem--;
4736 /* if we found our subitem, zapp it */
4740 if (is_textW(lpDelItem->hdr.pszText))
4741 Free(lpDelItem->hdr.pszText);
4746 /* free dpa memory */
4747 DPA_DeletePtr(hdpaSubItems, nSubItem);
4752 /* update the other column info */
4753 LISTVIEW_UpdateItemSize(infoPtr);
4754 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4755 LISTVIEW_InvalidateList(infoPtr);
4757 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4764 * Invalidates the listview after an item's insertion or deletion.
4767 * [I] infoPtr : valid pointer to the listview structure
4768 * [I] nItem : item index
4769 * [I] dir : -1 if deleting, 1 if inserting
4774 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4776 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4777 INT nPerCol, nItemCol, nItemRow;
4781 /* if we don't refresh, what's the point of scrolling? */
4782 if (!is_redrawing(infoPtr)) return;
4784 assert (abs(dir) == 1);
4786 /* arrange icons if autoarrange is on */
4787 if (is_autoarrange(infoPtr))
4789 BOOL arrange = TRUE;
4790 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4791 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4792 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4795 /* scrollbars need updating */
4796 LISTVIEW_UpdateScroll(infoPtr);
4798 /* figure out the item's position */
4799 if (uView == LVS_REPORT)
4800 nPerCol = infoPtr->nItemCount + 1;
4801 else if (uView == LVS_LIST)
4802 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4803 else /* LVS_ICON, or LVS_SMALLICON */
4806 nItemCol = nItem / nPerCol;
4807 nItemRow = nItem % nPerCol;
4808 LISTVIEW_GetOrigin(infoPtr, &Origin);
4810 /* move the items below up a slot */
4811 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4812 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4813 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4814 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4815 OffsetRect(&rcScroll, Origin.x, Origin.y);
4816 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
4817 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4819 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
4820 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4821 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4824 /* report has only that column, so we're done */
4825 if (uView == LVS_REPORT) return;
4827 /* now for LISTs, we have to deal with the columns to the right */
4828 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4830 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4831 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4832 OffsetRect(&rcScroll, Origin.x, Origin.y);
4833 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4834 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4835 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4840 * Removes an item from the listview control.
4843 * [I] infoPtr : valid pointer to the listview structure
4844 * [I] nItem : item index
4850 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4853 const UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4854 const BOOL is_icon = (uView == LVS_SMALLICON || uView == LVS_ICON);
4856 TRACE("(nItem=%d)\n", nItem);
4858 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4860 /* remove selection, and focus */
4862 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4863 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4865 /* send LVN_DELETEITEM notification. */
4866 if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
4868 /* we need to do this here, because we'll be deleting stuff */
4870 LISTVIEW_InvalidateItem(infoPtr, nItem);
4872 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4878 hdpaSubItems = DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4879 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4881 hdrItem = DPA_GetPtr(hdpaSubItems, i);
4882 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4885 DPA_Destroy(hdpaSubItems);
4890 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4891 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4894 infoPtr->nItemCount--;
4895 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4897 /* now is the invalidation fun */
4899 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4906 * Callback implementation for editlabel control
4909 * [I] infoPtr : valid pointer to the listview structure
4910 * [I] pszText : modified text
4911 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4917 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4919 HWND hwndSelf = infoPtr->hwndSelf;
4920 NMLVDISPINFOW dispInfo;
4921 INT editedItem = infoPtr->nEditLabelItem;
4924 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4926 infoPtr->nEditLabelItem = -1;
4928 ZeroMemory(&dispInfo, sizeof(dispInfo));
4929 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4930 dispInfo.item.iItem = editedItem;
4931 dispInfo.item.iSubItem = 0;
4932 dispInfo.item.stateMask = ~0;
4933 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4936 bSame = (lstrcmpW(dispInfo.item.pszText, pszText) == 0);
4939 LPWSTR tmp = textdupTtoW(pszText, FALSE);
4940 bSame = (lstrcmpW(dispInfo.item.pszText, tmp) == 0);
4941 textfreeT(tmp, FALSE);
4943 if (bSame) return TRUE;
4945 /* add the text from the edit in */
4946 dispInfo.item.mask |= LVIF_TEXT;
4947 dispInfo.item.pszText = pszText;
4948 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4950 /* Do we need to update the Item Text */
4951 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4952 if (!IsWindow(hwndSelf))
4954 if (!pszText) return TRUE;
4956 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4958 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, editedItem);
4959 ITEM_INFO* lpItem = DPA_GetPtr(hdpaSubItems, 0);
4960 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4962 LISTVIEW_InvalidateItem(infoPtr, editedItem);
4967 ZeroMemory(&dispInfo, sizeof(dispInfo));
4968 dispInfo.item.mask = LVIF_TEXT;
4969 dispInfo.item.iItem = editedItem;
4970 dispInfo.item.iSubItem = 0;
4971 dispInfo.item.pszText = pszText;
4972 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4973 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4978 * Begin in place editing of specified list view item
4981 * [I] infoPtr : valid pointer to the listview structure
4982 * [I] nItem : item index
4983 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4989 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4991 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4992 NMLVDISPINFOW dispInfo;
4994 HWND hwndSelf = infoPtr->hwndSelf;
4996 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4998 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4999 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5001 infoPtr->nEditLabelItem = nItem;
5003 /* Is the EditBox still there, if so remove it */
5004 if(infoPtr->hwndEdit != 0)
5006 SetFocus(infoPtr->hwndSelf);
5007 infoPtr->hwndEdit = 0;
5010 LISTVIEW_SetSelection(infoPtr, nItem);
5011 LISTVIEW_SetItemFocus(infoPtr, nItem);
5012 LISTVIEW_InvalidateItem(infoPtr, nItem);
5014 rect.left = LVIR_LABEL;
5015 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
5017 ZeroMemory(&dispInfo, sizeof(dispInfo));
5018 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
5019 dispInfo.item.iItem = nItem;
5020 dispInfo.item.iSubItem = 0;
5021 dispInfo.item.stateMask = ~0;
5022 dispInfo.item.pszText = szDispText;
5023 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
5024 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
5026 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
5027 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
5028 if (!infoPtr->hwndEdit) return 0;
5030 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
5032 if (!IsWindow(hwndSelf))
5034 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
5035 infoPtr->hwndEdit = 0;
5039 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
5040 SetFocus(infoPtr->hwndEdit);
5041 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
5042 return infoPtr->hwndEdit;
5048 * Ensures the specified item is visible, scrolling into view if necessary.
5051 * [I] infoPtr : valid pointer to the listview structure
5052 * [I] nItem : item index
5053 * [I] bPartial : partially or entirely visible
5059 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
5061 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5062 INT nScrollPosHeight = 0;
5063 INT nScrollPosWidth = 0;
5064 INT nHorzAdjust = 0;
5065 INT nVertAdjust = 0;
5068 RECT rcItem, rcTemp;
5070 rcItem.left = LVIR_BOUNDS;
5071 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
5073 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
5075 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
5077 /* scroll left/right, but in LVS_REPORT mode */
5078 if (uView == LVS_LIST)
5079 nScrollPosWidth = infoPtr->nItemWidth;
5080 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5081 nScrollPosWidth = 1;
5083 if (rcItem.left < infoPtr->rcList.left)
5086 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
5091 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
5095 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
5097 /* scroll up/down, but not in LVS_LIST mode */
5098 if (uView == LVS_REPORT)
5099 nScrollPosHeight = infoPtr->nItemHeight;
5100 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
5101 nScrollPosHeight = 1;
5103 if (rcItem.top < infoPtr->rcList.top)
5106 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
5111 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
5115 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
5117 if (nScrollPosWidth)
5119 INT diff = nHorzDiff / nScrollPosWidth;
5120 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
5121 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
5124 if (nScrollPosHeight)
5126 INT diff = nVertDiff / nScrollPosHeight;
5127 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
5128 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
5136 * Searches for an item with specific characteristics.
5139 * [I] hwnd : window handle
5140 * [I] nStart : base item index
5141 * [I] lpFindInfo : item information to look for
5144 * SUCCESS : index of item
5147 static INT LISTVIEW_FindItemW(const LISTVIEW_INFO *infoPtr, INT nStart,
5148 const LVFINDINFOW *lpFindInfo)
5150 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5151 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5152 BOOL bWrap = FALSE, bNearest = FALSE;
5153 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
5154 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
5155 POINT Position, Destination;
5158 /* Search in virtual listviews should be done by application, not by
5159 listview control, so we just send LVN_ODFINDITEMW and return the result */
5160 if (infoPtr->dwStyle & LVS_OWNERDATA)
5164 nmlv.iStart = nStart;
5165 nmlv.lvfi = *lpFindInfo;
5166 return notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
5169 if (!lpFindInfo || nItem < 0) return -1;
5172 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
5174 lvItem.mask |= LVIF_TEXT;
5175 lvItem.pszText = szDispText;
5176 lvItem.cchTextMax = DISP_TEXT_SIZE;
5179 if (lpFindInfo->flags & LVFI_WRAP)
5182 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
5183 (uView == LVS_ICON || uView ==LVS_SMALLICON))
5188 LISTVIEW_GetOrigin(infoPtr, &Origin);
5189 Destination.x = lpFindInfo->pt.x - Origin.x;
5190 Destination.y = lpFindInfo->pt.y - Origin.y;
5191 switch(lpFindInfo->vkDirection)
5193 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
5194 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
5195 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
5196 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
5197 case VK_HOME: Destination.x = Destination.y = 0; break;
5198 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5199 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5201 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
5202 Destination.x = rcArea.right;
5203 Destination.y = rcArea.bottom;
5205 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
5209 else Destination.x = Destination.y = 0;
5211 /* if LVFI_PARAM is specified, all other flags are ignored */
5212 if (lpFindInfo->flags & LVFI_PARAM)
5214 lvItem.mask |= LVIF_PARAM;
5216 lvItem.mask &= ~LVIF_TEXT;
5220 for (; nItem < nLast; nItem++)
5222 lvItem.iItem = nItem;
5223 lvItem.iSubItem = 0;
5224 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
5226 if (lvItem.mask & LVIF_PARAM)
5228 if (lpFindInfo->lParam == lvItem.lParam)
5234 if (lvItem.mask & LVIF_TEXT)
5236 if (lpFindInfo->flags & LVFI_PARTIAL)
5238 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
5242 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
5246 if (!bNearest) return nItem;
5248 /* This is very inefficient. To do a good job here,
5249 * we need a sorted array of (x,y) item positions */
5250 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5252 /* compute the distance^2 to the destination */
5253 xdist = Destination.x - Position.x;
5254 ydist = Destination.y - Position.y;
5255 dist = xdist * xdist + ydist * ydist;
5257 /* remember the distance, and item if it's closer */
5261 nNearestItem = nItem;
5268 nLast = min(nStart + 1, infoPtr->nItemCount);
5273 return nNearestItem;
5278 * Searches for an item with specific characteristics.
5281 * [I] hwnd : window handle
5282 * [I] nStart : base item index
5283 * [I] lpFindInfo : item information to look for
5286 * SUCCESS : index of item
5289 static INT LISTVIEW_FindItemA(const LISTVIEW_INFO *infoPtr, INT nStart,
5290 const LVFINDINFOA *lpFindInfo)
5292 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
5297 memcpy(&fiw, lpFindInfo, sizeof(fiw));
5298 if (hasText) fiw.psz = strW = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
5299 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
5300 textfreeT(strW, FALSE);
5306 * Retrieves the background image of the listview control.
5309 * [I] infoPtr : valid pointer to the listview structure
5310 * [O] lpBkImage : background image attributes
5316 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
5318 /* FIXME (listview, "empty stub!\n"); */
5324 * Retrieves column attributes.
5327 * [I] infoPtr : valid pointer to the listview structure
5328 * [I] nColumn : column index
5329 * [IO] lpColumn : column information
5330 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5331 * otherwise it is in fact a LPLVCOLUMNA
5337 static BOOL LISTVIEW_GetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
5339 COLUMN_INFO *lpColumnInfo;
5342 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5343 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
5345 /* initialize memory */
5346 ZeroMemory(&hdi, sizeof(hdi));
5348 if (lpColumn->mask & LVCF_TEXT)
5350 hdi.mask |= HDI_TEXT;
5351 hdi.pszText = lpColumn->pszText;
5352 hdi.cchTextMax = lpColumn->cchTextMax;
5355 if (lpColumn->mask & LVCF_IMAGE)
5356 hdi.mask |= HDI_IMAGE;
5358 if (lpColumn->mask & LVCF_ORDER)
5359 hdi.mask |= HDI_ORDER;
5361 if (lpColumn->mask & LVCF_SUBITEM)
5362 hdi.mask |= HDI_LPARAM;
5364 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
5366 if (lpColumn->mask & LVCF_FMT)
5367 lpColumn->fmt = lpColumnInfo->fmt;
5369 if (lpColumn->mask & LVCF_WIDTH)
5370 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
5372 if (lpColumn->mask & LVCF_IMAGE)
5373 lpColumn->iImage = hdi.iImage;
5375 if (lpColumn->mask & LVCF_ORDER)
5376 lpColumn->iOrder = hdi.iOrder;
5378 if (lpColumn->mask & LVCF_SUBITEM)
5379 lpColumn->iSubItem = hdi.lParam;
5385 static BOOL LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5387 TRACE("iCount=%d, lpiArray=%p\n", iCount, lpiArray);
5392 return SendMessageW(infoPtr->hwndHeader, HDM_GETORDERARRAY, iCount, (LPARAM)lpiArray);
5397 * Retrieves the column width.
5400 * [I] infoPtr : valid pointer to the listview structure
5401 * [I] int : column index
5404 * SUCCESS : column width
5407 static INT LISTVIEW_GetColumnWidth(const LISTVIEW_INFO *infoPtr, INT nColumn)
5409 INT nColumnWidth = 0;
5412 TRACE("nColumn=%d\n", nColumn);
5414 /* we have a 'column' in LIST and REPORT mode only */
5415 switch(infoPtr->dwStyle & LVS_TYPEMASK)
5418 nColumnWidth = infoPtr->nItemWidth;
5421 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDM_ITEMCHANGED.
5422 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
5423 * HDM_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
5425 * TODO: should we do the same in LVM_GETCOLUMN?
5427 hdItem.mask = HDI_WIDTH;
5428 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdItem))
5430 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr->hwndSelf, nColumn);
5433 nColumnWidth = hdItem.cxy;
5437 TRACE("nColumnWidth=%d\n", nColumnWidth);
5438 return nColumnWidth;
5443 * In list or report display mode, retrieves the number of items that can fit
5444 * vertically in the visible area. In icon or small icon display mode,
5445 * retrieves the total number of visible items.
5448 * [I] infoPtr : valid pointer to the listview structure
5451 * Number of fully visible items.
5453 static INT LISTVIEW_GetCountPerPage(const LISTVIEW_INFO *infoPtr)
5455 switch (infoPtr->dwStyle & LVS_TYPEMASK)
5459 return infoPtr->nItemCount;
5461 return LISTVIEW_GetCountPerColumn(infoPtr);
5463 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5471 * Retrieves an image list handle.
5474 * [I] infoPtr : valid pointer to the listview structure
5475 * [I] nImageList : image list identifier
5478 * SUCCESS : image list handle
5481 static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImageList)
5485 case LVSIL_NORMAL: return infoPtr->himlNormal;
5486 case LVSIL_SMALL: return infoPtr->himlSmall;
5487 case LVSIL_STATE: return infoPtr->himlState;
5492 /* LISTVIEW_GetISearchString */
5496 * Retrieves item attributes.
5499 * [I] hwnd : window handle
5500 * [IO] lpLVItem : item info
5501 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5502 * if FALSE, then lpLVItem is a LPLVITEMA.
5505 * This is the internal 'GetItem' interface -- it tries to
5506 * be smart and avoid text copies, if possible, by modifying
5507 * lpLVItem->pszText to point to the text string. Please note
5508 * that this is not always possible (e.g. OWNERDATA), so on
5509 * entry you *must* supply valid values for pszText, and cchTextMax.
5510 * The only difference to the documented interface is that upon
5511 * return, you should use *only* the lpLVItem->pszText, rather than
5512 * the buffer pointer you provided on input. Most code already does
5513 * that, so it's not a problem.
5514 * For the two cases when the text must be copied (that is,
5515 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5521 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5523 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5524 NMLVDISPINFOW dispInfo;
5530 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5532 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5535 if (lpLVItem->mask == 0) return TRUE;
5537 /* make a local copy */
5538 isubitem = lpLVItem->iSubItem;
5540 /* a quick optimization if all we're asked is the focus state
5541 * these queries are worth optimising since they are common,
5542 * and can be answered in constant time, without the heavy accesses */
5543 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5544 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5546 lpLVItem->state = 0;
5547 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5548 lpLVItem->state |= LVIS_FOCUSED;
5552 ZeroMemory(&dispInfo, sizeof(dispInfo));
5554 /* if the app stores all the data, handle it separately */
5555 if (infoPtr->dwStyle & LVS_OWNERDATA)
5557 dispInfo.item.state = 0;
5559 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5560 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5562 UINT mask = lpLVItem->mask;
5564 /* NOTE: copy only fields which we _know_ are initialized, some apps
5565 * depend on the uninitialized fields being 0 */
5566 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5567 dispInfo.item.iItem = lpLVItem->iItem;
5568 dispInfo.item.iSubItem = isubitem;
5569 if (lpLVItem->mask & LVIF_TEXT)
5571 if (lpLVItem->mask & LVIF_NORECOMPUTE)
5573 dispInfo.item.mask &= ~(LVIF_TEXT | LVIF_NORECOMPUTE);
5576 dispInfo.item.pszText = lpLVItem->pszText;
5577 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5580 if (lpLVItem->mask & LVIF_STATE)
5581 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5582 /* could be zeroed on LVIF_NORECOMPUTE case */
5583 if (dispInfo.item.mask != 0)
5585 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5586 dispInfo.item.stateMask = lpLVItem->stateMask;
5587 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5589 /* full size structure expected - _WIN32IE >= 0x560 */
5590 *lpLVItem = dispInfo.item;
5592 else if (lpLVItem->mask & LVIF_INDENT)
5594 /* indent member expected - _WIN32IE >= 0x300 */
5595 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5599 /* minimal structure expected */
5600 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5602 lpLVItem->mask = mask;
5603 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5607 /* make sure lParam is zeroed out */
5608 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5610 /* callback marked pointer required here */
5611 if ((lpLVItem->mask & LVIF_TEXT) && (lpLVItem->mask & LVIF_NORECOMPUTE))
5612 lpLVItem->pszText = LPSTR_TEXTCALLBACKW;
5614 /* we store only a little state, so if we're not asked, we're done */
5615 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5617 /* if focus is handled by us, report it */
5618 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5620 lpLVItem->state &= ~LVIS_FOCUSED;
5621 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5622 lpLVItem->state |= LVIS_FOCUSED;
5625 /* and do the same for selection, if we handle it */
5626 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5628 lpLVItem->state &= ~LVIS_SELECTED;
5629 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5630 lpLVItem->state |= LVIS_SELECTED;
5636 /* find the item and subitem structures before we proceed */
5637 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5638 lpItem = DPA_GetPtr(hdpaSubItems, 0);
5643 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5644 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5647 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5652 pItemHdr = &lpItem->hdr;
5654 /* Do we need to query the state from the app? */
5655 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5657 dispInfo.item.mask |= LVIF_STATE;
5658 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5661 /* Do we need to enquire about the image? */
5662 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5663 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5665 dispInfo.item.mask |= LVIF_IMAGE;
5666 dispInfo.item.iImage = I_IMAGECALLBACK;
5669 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5670 if ((lpLVItem->mask & LVIF_TEXT) && !(lpLVItem->mask & LVIF_NORECOMPUTE) &&
5671 !is_textW(pItemHdr->pszText))
5673 dispInfo.item.mask |= LVIF_TEXT;
5674 dispInfo.item.pszText = lpLVItem->pszText;
5675 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5676 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5677 *dispInfo.item.pszText = '\0';
5680 /* If we don't have all the requested info, query the application */
5681 if (dispInfo.item.mask != 0)
5683 dispInfo.item.iItem = lpLVItem->iItem;
5684 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5685 dispInfo.item.lParam = lpItem->lParam;
5686 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5687 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5690 /* we should not store values for subitems */
5691 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5693 /* Now, handle the iImage field */
5694 if (dispInfo.item.mask & LVIF_IMAGE)
5696 lpLVItem->iImage = dispInfo.item.iImage;
5697 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5698 pItemHdr->iImage = dispInfo.item.iImage;
5700 else if (lpLVItem->mask & LVIF_IMAGE)
5702 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5703 lpLVItem->iImage = pItemHdr->iImage;
5705 lpLVItem->iImage = 0;
5708 /* The pszText field */
5709 if (dispInfo.item.mask & LVIF_TEXT)
5711 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5712 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5714 lpLVItem->pszText = dispInfo.item.pszText;
5716 else if (lpLVItem->mask & LVIF_TEXT)
5718 /* if LVN_GETDISPINFO's disabled with LVIF_NORECOMPUTE return callback placeholder */
5719 if (isW || !is_textW(pItemHdr->pszText)) lpLVItem->pszText = pItemHdr->pszText;
5720 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5723 /* Next is the lParam field */
5724 if (dispInfo.item.mask & LVIF_PARAM)
5726 lpLVItem->lParam = dispInfo.item.lParam;
5727 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5728 lpItem->lParam = dispInfo.item.lParam;
5730 else if (lpLVItem->mask & LVIF_PARAM)
5731 lpLVItem->lParam = lpItem->lParam;
5733 /* if this is a subitem, we're done */
5734 if (isubitem) return TRUE;
5736 /* ... the state field (this one is different due to uCallbackmask) */
5737 if (lpLVItem->mask & LVIF_STATE)
5739 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5740 if (dispInfo.item.mask & LVIF_STATE)
5742 lpLVItem->state &= ~dispInfo.item.stateMask;
5743 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5745 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5747 lpLVItem->state &= ~LVIS_FOCUSED;
5748 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5749 lpLVItem->state |= LVIS_FOCUSED;
5751 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5753 lpLVItem->state &= ~LVIS_SELECTED;
5754 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5755 lpLVItem->state |= LVIS_SELECTED;
5759 /* and last, but not least, the indent field */
5760 if (lpLVItem->mask & LVIF_INDENT)
5761 lpLVItem->iIndent = lpItem->iIndent;
5768 * Retrieves item attributes.
5771 * [I] hwnd : window handle
5772 * [IO] lpLVItem : item info
5773 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5774 * if FALSE, then lpLVItem is a LPLVITEMA.
5777 * This is the external 'GetItem' interface -- it properly copies
5778 * the text in the provided buffer.
5784 static BOOL LISTVIEW_GetItemExtT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5789 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5792 pszText = lpLVItem->pszText;
5793 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5794 if (bResult && lpLVItem->pszText != pszText)
5796 if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW)
5797 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5799 pszText = LPSTR_TEXTCALLBACKW;
5801 lpLVItem->pszText = pszText;
5809 * Retrieves the position (upper-left) of the listview control item.
5810 * Note that for LVS_ICON style, the upper-left is that of the icon
5811 * and not the bounding box.
5814 * [I] infoPtr : valid pointer to the listview structure
5815 * [I] nItem : item index
5816 * [O] lpptPosition : coordinate information
5822 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5824 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5827 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5829 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5831 LISTVIEW_GetOrigin(infoPtr, &Origin);
5832 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5834 if (uView == LVS_ICON)
5836 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5837 lpptPosition->y += ICON_TOP_PADDING;
5839 lpptPosition->x += Origin.x;
5840 lpptPosition->y += Origin.y;
5842 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
5849 * Retrieves the bounding rectangle for a listview control item.
5852 * [I] infoPtr : valid pointer to the listview structure
5853 * [I] nItem : item index
5854 * [IO] lprc : bounding rectangle coordinates
5855 * lprc->left specifies the portion of the item for which the bounding
5856 * rectangle will be retrieved.
5858 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5859 * including the icon and label.
5862 * * Experiment shows that native control returns:
5863 * * width = min (48, length of text line)
5864 * * .left = position.x - (width - iconsize.cx)/2
5865 * * .right = .left + width
5866 * * height = #lines of text * ntmHeight + icon height + 8
5867 * * .top = position.y - 2
5868 * * .bottom = .top + height
5869 * * separation between items .y = itemSpacing.cy - height
5870 * * .x = itemSpacing.cx - width
5871 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5874 * * Experiment shows that native control returns:
5875 * * width = iconSize.cx + 16
5876 * * .left = position.x - (width - iconsize.cx)/2
5877 * * .right = .left + width
5878 * * height = iconSize.cy + 4
5879 * * .top = position.y - 2
5880 * * .bottom = .top + height
5881 * * separation between items .y = itemSpacing.cy - height
5882 * * .x = itemSpacing.cx - width
5883 * LVIR_LABEL Returns the bounding rectangle of the item text.
5886 * * Experiment shows that native control returns:
5887 * * width = text length
5888 * * .left = position.x - width/2
5889 * * .right = .left + width
5890 * * height = ntmH * linecount + 2
5891 * * .top = position.y + iconSize.cy + 6
5892 * * .bottom = .top + height
5893 * * separation between items .y = itemSpacing.cy - height
5894 * * .x = itemSpacing.cx - width
5895 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5896 * rectangles, but excludes columns in report view.
5903 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5904 * upon whether the window has the focus currently and on whether the item
5905 * is the one with the focus. Ensure that the control's record of which
5906 * item has the focus agrees with the items' records.
5908 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5910 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5911 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5912 BOOL doLabel = TRUE, oversizedBox = FALSE;
5913 POINT Position, Origin;
5916 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5918 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5920 LISTVIEW_GetOrigin(infoPtr, &Origin);
5921 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5923 /* Be smart and try to figure out the minimum we have to do */
5924 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5925 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5926 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5927 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5928 oversizedBox = TRUE;
5930 /* get what we need from the item before hand, so we make
5931 * only one request. This can speed up things, if data
5932 * is stored on the app side */
5934 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5935 if (doLabel) lvItem.mask |= LVIF_TEXT;
5936 lvItem.iItem = nItem;
5937 lvItem.iSubItem = 0;
5938 lvItem.pszText = szDispText;
5939 lvItem.cchTextMax = DISP_TEXT_SIZE;
5940 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5941 /* we got the state already up, simulate it here, to avoid a reget */
5942 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5944 lvItem.mask |= LVIF_STATE;
5945 lvItem.stateMask = LVIS_FOCUSED;
5946 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5949 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5950 lprc->left = LVIR_BOUNDS;
5954 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
5958 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc);
5962 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
5965 case LVIR_SELECTBOUNDS:
5966 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL);
5970 WARN("Unknown value: %d\n", lprc->left);
5974 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5976 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
5983 * Retrieves the spacing between listview control items.
5986 * [I] infoPtr : valid pointer to the listview structure
5987 * [IO] lprc : rectangle to receive the output
5988 * on input, lprc->top = nSubItem
5989 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5991 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5992 * not only those of the first column.
5993 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5999 static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
6005 if (!lprc) return FALSE;
6007 nColumn = lprc->top;
6009 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
6010 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
6012 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
6014 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
6016 /* special case for header items */
6019 if (lprc->left != LVIR_BOUNDS)
6021 FIXME("Only LVIR_BOUNDS is implemented for header, got %d\n", lprc->left);
6025 if (infoPtr->hwndHeader)
6026 return Header_GetItemRect(infoPtr->hwndHeader, lprc->top, lprc);
6029 memset(lprc, 0, sizeof(RECT));
6034 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
6036 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6039 lvItem.iItem = nItem;
6040 lvItem.iSubItem = nColumn;
6042 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
6046 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
6051 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
6055 ERR("Unknown bounds=%d\n", lprc->left);
6059 OffsetRect(lprc, Position.x, Position.y);
6066 * Retrieves the width of a label.
6069 * [I] infoPtr : valid pointer to the listview structure
6072 * SUCCESS : string width (in pixels)
6075 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *infoPtr, INT nItem)
6077 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6080 TRACE("(nItem=%d)\n", nItem);
6082 lvItem.mask = LVIF_TEXT;
6083 lvItem.iItem = nItem;
6084 lvItem.iSubItem = 0;
6085 lvItem.pszText = szDispText;
6086 lvItem.cchTextMax = DISP_TEXT_SIZE;
6087 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6089 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6094 * Retrieves the spacing between listview control items.
6097 * [I] infoPtr : valid pointer to the listview structure
6098 * [I] bSmall : flag for small or large icon
6101 * Horizontal + vertical spacing
6103 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall)
6109 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6113 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
6114 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6116 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6123 * Retrieves the state of a listview control item.
6126 * [I] infoPtr : valid pointer to the listview structure
6127 * [I] nItem : item index
6128 * [I] uMask : state mask
6131 * State specified by the mask.
6133 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
6137 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6139 lvItem.iItem = nItem;
6140 lvItem.iSubItem = 0;
6141 lvItem.mask = LVIF_STATE;
6142 lvItem.stateMask = uMask;
6143 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6145 return lvItem.state & uMask;
6150 * Retrieves the text of a listview control item or subitem.
6153 * [I] hwnd : window handle
6154 * [I] nItem : item index
6155 * [IO] lpLVItem : item information
6156 * [I] isW : TRUE if lpLVItem is Unicode
6159 * SUCCESS : string length
6162 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6164 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6166 lpLVItem->mask = LVIF_TEXT;
6167 lpLVItem->iItem = nItem;
6168 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
6170 return textlenT(lpLVItem->pszText, isW);
6175 * Searches for an item based on properties + relationships.
6178 * [I] infoPtr : valid pointer to the listview structure
6179 * [I] nItem : item index
6180 * [I] uFlags : relationship flag
6183 * SUCCESS : item index
6186 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
6188 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6190 LVFINDINFOW lvFindInfo;
6191 INT nCountPerColumn;
6195 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
6196 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
6198 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6200 if (uFlags & LVNI_CUT)
6203 if (uFlags & LVNI_DROPHILITED)
6204 uMask |= LVIS_DROPHILITED;
6206 if (uFlags & LVNI_FOCUSED)
6207 uMask |= LVIS_FOCUSED;
6209 if (uFlags & LVNI_SELECTED)
6210 uMask |= LVIS_SELECTED;
6212 /* if we're asked for the focused item, that's only one,
6213 * so it's worth optimizing */
6214 if (uFlags & LVNI_FOCUSED)
6216 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
6217 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
6220 if (uFlags & LVNI_ABOVE)
6222 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6227 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6233 /* Special case for autoarrange - move 'til the top of a list */
6234 if (is_autoarrange(infoPtr))
6236 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6237 while (nItem - nCountPerRow >= 0)
6239 nItem -= nCountPerRow;
6240 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6245 lvFindInfo.flags = LVFI_NEARESTXY;
6246 lvFindInfo.vkDirection = VK_UP;
6247 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6248 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6250 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6255 else if (uFlags & LVNI_BELOW)
6257 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6259 while (nItem < infoPtr->nItemCount)
6262 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6268 /* Special case for autoarrange - move 'til the bottom of a list */
6269 if (is_autoarrange(infoPtr))
6271 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6272 while (nItem + nCountPerRow < infoPtr->nItemCount )
6274 nItem += nCountPerRow;
6275 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6280 lvFindInfo.flags = LVFI_NEARESTXY;
6281 lvFindInfo.vkDirection = VK_DOWN;
6282 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6283 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6285 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6290 else if (uFlags & LVNI_TOLEFT)
6292 if (uView == LVS_LIST)
6294 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6295 while (nItem - nCountPerColumn >= 0)
6297 nItem -= nCountPerColumn;
6298 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6302 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6304 /* Special case for autoarrange - move 'til the beginning of a row */
6305 if (is_autoarrange(infoPtr))
6307 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6308 while (nItem % nCountPerRow > 0)
6311 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6316 lvFindInfo.flags = LVFI_NEARESTXY;
6317 lvFindInfo.vkDirection = VK_LEFT;
6318 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6319 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6321 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6326 else if (uFlags & LVNI_TORIGHT)
6328 if (uView == LVS_LIST)
6330 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6331 while (nItem + nCountPerColumn < infoPtr->nItemCount)
6333 nItem += nCountPerColumn;
6334 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6338 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6340 /* Special case for autoarrange - move 'til the end of a row */
6341 if (is_autoarrange(infoPtr))
6343 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6344 while (nItem % nCountPerRow < nCountPerRow - 1 )
6347 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6352 lvFindInfo.flags = LVFI_NEARESTXY;
6353 lvFindInfo.vkDirection = VK_RIGHT;
6354 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6355 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6357 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6366 /* search by index */
6367 for (i = nItem; i < infoPtr->nItemCount; i++)
6369 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
6377 /* LISTVIEW_GetNumberOfWorkAreas */
6381 * Retrieves the origin coordinates when in icon or small icon display mode.
6384 * [I] infoPtr : valid pointer to the listview structure
6385 * [O] lpptOrigin : coordinate information
6390 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
6392 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6393 INT nHorzPos = 0, nVertPos = 0;
6394 SCROLLINFO scrollInfo;
6396 scrollInfo.cbSize = sizeof(SCROLLINFO);
6397 scrollInfo.fMask = SIF_POS;
6399 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6400 nHorzPos = scrollInfo.nPos;
6401 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6402 nVertPos = scrollInfo.nPos;
6404 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6406 lpptOrigin->x = infoPtr->rcList.left;
6407 lpptOrigin->y = infoPtr->rcList.top;
6408 if (uView == LVS_LIST)
6409 nHorzPos *= infoPtr->nItemWidth;
6410 else if (uView == LVS_REPORT)
6411 nVertPos *= infoPtr->nItemHeight;
6413 lpptOrigin->x -= nHorzPos;
6414 lpptOrigin->y -= nVertPos;
6416 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
6421 * Retrieves the width of a string.
6424 * [I] hwnd : window handle
6425 * [I] lpszText : text string to process
6426 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6429 * SUCCESS : string width (in pixels)
6432 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6437 if (is_textT(lpszText, isW))
6439 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6440 HDC hdc = GetDC(infoPtr->hwndSelf);
6441 HFONT hOldFont = SelectObject(hdc, hFont);
6444 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6446 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6447 SelectObject(hdc, hOldFont);
6448 ReleaseDC(infoPtr->hwndSelf, hdc);
6450 return stringSize.cx;
6455 * Determines which listview item is located at the specified position.
6458 * [I] infoPtr : valid pointer to the listview structure
6459 * [IO] lpht : hit test information
6460 * [I] subitem : fill out iSubItem.
6461 * [I] select : return the index only if the hit selects the item
6464 * (mm 20001022): We must not allow iSubItem to be touched, for
6465 * an app might pass only a structure with space up to iItem!
6466 * (MS Office 97 does that for instance in the file open dialog)
6469 * SUCCESS : item index
6472 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6474 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6475 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6476 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6477 POINT Origin, Position, opt;
6482 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6486 if (subitem) lpht->iSubItem = 0;
6488 if (infoPtr->rcList.left > lpht->pt.x)
6489 lpht->flags |= LVHT_TOLEFT;
6490 else if (infoPtr->rcList.right < lpht->pt.x)
6491 lpht->flags |= LVHT_TORIGHT;
6493 if (infoPtr->rcList.top > lpht->pt.y)
6494 lpht->flags |= LVHT_ABOVE;
6495 else if (infoPtr->rcList.bottom < lpht->pt.y)
6496 lpht->flags |= LVHT_BELOW;
6498 TRACE("lpht->flags=0x%x\n", lpht->flags);
6499 if (lpht->flags) return -1;
6501 lpht->flags |= LVHT_NOWHERE;
6503 LISTVIEW_GetOrigin(infoPtr, &Origin);
6505 /* first deal with the large items */
6506 rcSearch.left = lpht->pt.x;
6507 rcSearch.top = lpht->pt.y;
6508 rcSearch.right = rcSearch.left + 1;
6509 rcSearch.bottom = rcSearch.top + 1;
6511 iterator_frameditems(&i, infoPtr, &rcSearch);
6512 iterator_next(&i); /* go to first item in the sequence */
6514 iterator_destroy(&i);
6516 TRACE("lpht->iItem=%d\n", iItem);
6517 if (iItem == -1) return -1;
6519 lvItem.mask = LVIF_STATE | LVIF_TEXT;
6520 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6521 lvItem.stateMask = LVIS_STATEIMAGEMASK;
6522 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6523 lvItem.iItem = iItem;
6524 lvItem.iSubItem = 0;
6525 lvItem.pszText = szDispText;
6526 lvItem.cchTextMax = DISP_TEXT_SIZE;
6527 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6528 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6530 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
6531 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6532 opt.x = lpht->pt.x - Position.x - Origin.x;
6533 opt.y = lpht->pt.y - Position.y - Origin.y;
6535 if (uView == LVS_REPORT)
6539 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6540 UnionRect(&rcBounds, &rcBounds, &rcState);
6542 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6543 if (!PtInRect(&rcBounds, opt)) return -1;
6545 if (PtInRect(&rcIcon, opt))
6546 lpht->flags |= LVHT_ONITEMICON;
6547 else if (PtInRect(&rcLabel, opt))
6548 lpht->flags |= LVHT_ONITEMLABEL;
6549 else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6550 lpht->flags |= LVHT_ONITEMSTATEICON;
6551 if (lpht->flags & LVHT_ONITEM)
6552 lpht->flags &= ~LVHT_NOWHERE;
6554 TRACE("lpht->flags=0x%x\n", lpht->flags);
6555 if (uView == LVS_REPORT && subitem)
6559 rcBounds.right = rcBounds.left;
6560 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6562 rcBounds.left = rcBounds.right;
6563 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6564 if (PtInRect(&rcBounds, opt))
6572 if (select && !(uView == LVS_REPORT &&
6573 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6574 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6576 if (uView == LVS_REPORT)
6578 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6579 UnionRect(&rcBounds, &rcBounds, &rcState);
6581 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6583 return lpht->iItem = iItem;
6587 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6588 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6589 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6590 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6591 their own sort proc. when sending LVM_SORTITEMS.
6594 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6596 LVS_SORTXXX must be specified,
6597 LVS_OWNERDRAW is not set,
6598 <item>.pszText is not LPSTR_TEXTCALLBACK.
6600 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6601 are sorted based on item text..."
6603 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6605 ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
6606 ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
6607 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6609 /* if we're sorting descending, negate the return value */
6610 return (((const LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6615 * Inserts a new item in the listview control.
6618 * [I] infoPtr : valid pointer to the listview structure
6619 * [I] lpLVItem : item information
6620 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6623 * SUCCESS : new item index
6626 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6628 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6633 BOOL is_sorted, has_changed;
6635 HWND hwndSelf = infoPtr->hwndSelf;
6637 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6639 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6641 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6642 if (!lpLVItem || lpLVItem->iSubItem) return -1;
6644 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6646 if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
6648 /* insert item in listview control data structure */
6649 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6650 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6652 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6653 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6655 if (lpLVItem->iItem < 0 && !is_sorted) return -1;
6657 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6658 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6659 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6660 if (nItem == -1) goto fail;
6661 infoPtr->nItemCount++;
6663 /* shift indices first so they don't get tangled */
6664 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6666 /* set the item attributes */
6667 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6669 /* full size structure expected - _WIN32IE >= 0x560 */
6672 else if (lpLVItem->mask & LVIF_INDENT)
6674 /* indent member expected - _WIN32IE >= 0x300 */
6675 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6679 /* minimal structure expected */
6680 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6683 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6685 item.mask |= LVIF_STATE;
6686 item.stateMask |= LVIS_STATEIMAGEMASK;
6687 item.state &= ~LVIS_STATEIMAGEMASK;
6688 item.state |= INDEXTOSTATEIMAGEMASK(1);
6690 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6692 /* if we're sorted, sort the list, and update the index */
6695 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6696 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6697 assert(nItem != -1);
6700 /* make room for the position, if we are in the right mode */
6701 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6703 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6705 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6707 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6712 /* send LVN_INSERTITEM notification */
6713 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6715 nmlv.lParam = lpItem->lParam;
6716 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6717 if (!IsWindow(hwndSelf))
6720 /* align items (set position of each item) */
6721 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6725 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6726 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6728 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6730 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6733 /* now is the invalidation fun */
6734 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6738 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6739 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6740 infoPtr->nItemCount--;
6742 DPA_DeletePtr(hdpaSubItems, 0);
6743 DPA_Destroy (hdpaSubItems);
6750 * Redraws a range of items.
6753 * [I] infoPtr : valid pointer to the listview structure
6754 * [I] nFirst : first item
6755 * [I] nLast : last item
6761 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6765 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6766 max(nFirst, nLast) >= infoPtr->nItemCount)
6769 for (i = nFirst; i <= nLast; i++)
6770 LISTVIEW_InvalidateItem(infoPtr, i);
6777 * Scroll the content of a listview.
6780 * [I] infoPtr : valid pointer to the listview structure
6781 * [I] dx : horizontal scroll amount in pixels
6782 * [I] dy : vertical scroll amount in pixels
6789 * If the control is in report mode (LVS_REPORT) the control can
6790 * be scrolled only in line increments. "dy" will be rounded to the
6791 * nearest number of pixels that are a whole line. Ex: if line height
6792 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6793 * is passed, then the scroll will be 0. (per MSDN 7/2002)
6795 * For: (per experimentation with native control and CSpy ListView)
6796 * LVS_ICON dy=1 = 1 pixel (vertical only)
6798 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6800 * LVS_LIST dx=1 = 1 column (horizontal only)
6801 * but will only scroll 1 column per message
6802 * no matter what the value.
6803 * dy must be 0 or FALSE returned.
6804 * LVS_REPORT dx=1 = 1 pixel
6808 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6810 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6812 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6813 dy /= infoPtr->nItemHeight;
6816 if (dy != 0) return FALSE;
6823 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6824 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6831 * Sets the background color.
6834 * [I] infoPtr : valid pointer to the listview structure
6835 * [I] clrBk : background color
6841 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6843 TRACE("(clrBk=%x)\n", clrBk);
6845 if(infoPtr->clrBk != clrBk) {
6846 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6847 infoPtr->clrBk = clrBk;
6848 if (clrBk == CLR_NONE)
6849 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6851 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6852 LISTVIEW_InvalidateList(infoPtr);
6858 /* LISTVIEW_SetBkImage */
6860 /*** Helper for {Insert,Set}ColumnT *only* */
6861 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
6862 const LVCOLUMNW *lpColumn, BOOL isW)
6864 if (lpColumn->mask & LVCF_FMT)
6866 /* format member is valid */
6867 lphdi->mask |= HDI_FORMAT;
6869 /* set text alignment (leftmost column must be left-aligned) */
6870 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6871 lphdi->fmt |= HDF_LEFT;
6872 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6873 lphdi->fmt |= HDF_RIGHT;
6874 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6875 lphdi->fmt |= HDF_CENTER;
6877 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6878 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6880 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6882 lphdi->fmt |= HDF_IMAGE;
6883 lphdi->iImage = I_IMAGECALLBACK;
6887 if (lpColumn->mask & LVCF_WIDTH)
6889 lphdi->mask |= HDI_WIDTH;
6890 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6892 /* make it fill the remainder of the controls width */
6896 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6898 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6899 lphdi->cxy += rcHeader.right - rcHeader.left;
6902 /* retrieve the layout of the header */
6903 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6904 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
6906 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6909 lphdi->cxy = lpColumn->cx;
6912 if (lpColumn->mask & LVCF_TEXT)
6914 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6915 lphdi->fmt |= HDF_STRING;
6916 lphdi->pszText = lpColumn->pszText;
6917 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6920 if (lpColumn->mask & LVCF_IMAGE)
6922 lphdi->mask |= HDI_IMAGE;
6923 lphdi->iImage = lpColumn->iImage;
6926 if (lpColumn->mask & LVCF_ORDER)
6928 lphdi->mask |= HDI_ORDER;
6929 lphdi->iOrder = lpColumn->iOrder;
6936 * Inserts a new column.
6939 * [I] infoPtr : valid pointer to the listview structure
6940 * [I] nColumn : column index
6941 * [I] lpColumn : column information
6942 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6945 * SUCCESS : new column index
6948 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6949 const LVCOLUMNW *lpColumn, BOOL isW)
6951 COLUMN_INFO *lpColumnInfo;
6954 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6956 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6958 if (!lpColumn || nColumn < 0) return -1;
6959 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6961 ZeroMemory(&hdi, sizeof(HDITEMW));
6962 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6965 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
6966 * (can be seen in SPY) otherwise column never gets added.
6968 if (!(lpColumn->mask & LVCF_WIDTH)) {
6969 hdi.mask |= HDI_WIDTH;
6974 * when the iSubItem is available Windows copies it to the header lParam. It seems
6975 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
6977 if (lpColumn->mask & LVCF_SUBITEM)
6979 hdi.mask |= HDI_LPARAM;
6980 hdi.lParam = lpColumn->iSubItem;
6983 /* create header if not present */
6984 LISTVIEW_CreateHeader(infoPtr);
6985 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle) &&
6986 (LVS_REPORT == uView) && (WS_VISIBLE & infoPtr->dwStyle))
6988 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
6991 /* insert item in header control */
6992 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6993 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6994 (WPARAM)nColumn, (LPARAM)&hdi);
6995 if (nNewColumn == -1) return -1;
6996 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6998 /* create our own column info */
6999 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
7000 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
7002 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
7003 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
7005 /* now we have to actually adjust the data */
7006 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
7008 SUBITEM_INFO *lpSubItem;
7012 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
7014 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
7015 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
7017 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
7018 if (lpSubItem->iSubItem >= nNewColumn)
7019 lpSubItem->iSubItem++;
7024 /* make space for the new column */
7025 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7026 LISTVIEW_UpdateItemSize(infoPtr);
7031 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
7034 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
7042 * Sets the attributes of a header item.
7045 * [I] infoPtr : valid pointer to the listview structure
7046 * [I] nColumn : column index
7047 * [I] lpColumn : column attributes
7048 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
7054 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
7055 const LVCOLUMNW *lpColumn, BOOL isW)
7057 HDITEMW hdi, hdiget;
7060 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
7062 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7064 ZeroMemory(&hdi, sizeof(HDITEMW));
7065 if (lpColumn->mask & LVCF_FMT)
7067 hdi.mask |= HDI_FORMAT;
7068 hdiget.mask = HDI_FORMAT;
7069 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
7070 hdi.fmt = hdiget.fmt & HDF_STRING;
7072 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
7074 /* set header item attributes */
7075 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
7076 if (!bResult) return FALSE;
7078 if (lpColumn->mask & LVCF_FMT)
7080 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
7081 int oldFmt = lpColumnInfo->fmt;
7083 lpColumnInfo->fmt = lpColumn->fmt;
7084 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
7086 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7087 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
7096 * Sets the column order array
7099 * [I] infoPtr : valid pointer to the listview structure
7100 * [I] iCount : number of elements in column order array
7101 * [I] lpiArray : pointer to column order array
7107 static BOOL LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
7109 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7120 * Sets the width of a column
7123 * [I] infoPtr : valid pointer to the listview structure
7124 * [I] nColumn : column index
7125 * [I] cx : column width
7131 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
7133 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7134 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
7138 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
7140 /* set column width only if in report or list mode */
7141 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
7143 /* take care of invalid cx values */
7144 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
7145 else if (uView == LVS_LIST && cx < 1) return FALSE;
7147 /* resize all columns if in LVS_LIST mode */
7148 if(uView == LVS_LIST)
7150 infoPtr->nItemWidth = cx;
7151 LISTVIEW_InvalidateList(infoPtr);
7155 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7157 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
7162 lvItem.mask = LVIF_TEXT;
7164 lvItem.iSubItem = nColumn;
7165 lvItem.pszText = szDispText;
7166 lvItem.cchTextMax = DISP_TEXT_SIZE;
7167 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7169 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
7170 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
7171 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
7173 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
7174 max_cx += infoPtr->iconSize.cx;
7175 max_cx += TRAILING_LABEL_PADDING;
7178 /* autosize based on listview items width */
7179 if(cx == LVSCW_AUTOSIZE)
7181 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7183 /* if iCol is the last column make it fill the remainder of the controls width */
7184 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
7189 LISTVIEW_GetOrigin(infoPtr, &Origin);
7190 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
7192 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
7196 /* Despite what the MS docs say, if this is not the last
7197 column, then MS resizes the column to the width of the
7198 largest text string in the column, including headers
7199 and items. This is different from LVSCW_AUTOSIZE in that
7200 LVSCW_AUTOSIZE ignores the header string length. */
7203 /* retrieve header text */
7204 hdi.mask = HDI_TEXT;
7205 hdi.cchTextMax = DISP_TEXT_SIZE;
7206 hdi.pszText = szDispText;
7207 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
7209 HDC hdc = GetDC(infoPtr->hwndSelf);
7210 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
7213 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
7214 cx = size.cx + TRAILING_HEADER_PADDING;
7215 /* FIXME: Take into account the header image, if one is present */
7216 SelectObject(hdc, old_font);
7217 ReleaseDC(infoPtr->hwndSelf, hdc);
7219 cx = max (cx, max_cx);
7223 if (cx < 0) return FALSE;
7225 /* call header to update the column change */
7226 hdi.mask = HDI_WIDTH;
7228 TRACE("hdi.cxy=%d\n", hdi.cxy);
7229 return Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7233 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
7236 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
7239 HBITMAP hbm_im, hbm_mask, hbm_orig;
7241 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
7242 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
7245 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
7246 ILC_COLOR | ILC_MASK, 2, 2);
7247 hdc_wnd = GetDC(infoPtr->hwndSelf);
7248 hdc = CreateCompatibleDC(hdc_wnd);
7249 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
7250 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
7251 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
7253 rc.left = rc.top = 0;
7254 rc.right = GetSystemMetrics(SM_CXSMICON);
7255 rc.bottom = GetSystemMetrics(SM_CYSMICON);
7257 hbm_orig = SelectObject(hdc, hbm_mask);
7258 FillRect(hdc, &rc, hbr_white);
7259 InflateRect(&rc, -2, -2);
7260 FillRect(hdc, &rc, hbr_black);
7262 SelectObject(hdc, hbm_im);
7263 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
7264 SelectObject(hdc, hbm_orig);
7265 ImageList_Add(himl, hbm_im, hbm_mask);
7267 SelectObject(hdc, hbm_im);
7268 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
7269 SelectObject(hdc, hbm_orig);
7270 ImageList_Add(himl, hbm_im, hbm_mask);
7272 DeleteObject(hbm_mask);
7273 DeleteObject(hbm_im);
7281 * Sets the extended listview style.
7284 * [I] infoPtr : valid pointer to the listview structure
7286 * [I] dwStyle : style
7289 * SUCCESS : previous style
7292 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwExStyle)
7294 DWORD dwOldExStyle = infoPtr->dwLvExStyle;
7298 infoPtr->dwLvExStyle = (dwOldExStyle & ~dwMask) | (dwExStyle & dwMask);
7300 infoPtr->dwLvExStyle = dwExStyle;
7302 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_CHECKBOXES)
7304 HIMAGELIST himl = 0;
7305 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7308 item.mask = LVIF_STATE;
7309 item.stateMask = LVIS_STATEIMAGEMASK;
7310 item.state = INDEXTOSTATEIMAGEMASK(1);
7311 LISTVIEW_SetItemState(infoPtr, -1, &item);
7313 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
7315 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
7318 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_HEADERDRAGDROP)
7322 /* if not already created */
7323 LISTVIEW_CreateHeader(infoPtr);
7325 dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
7326 if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
7327 dwStyle |= HDS_DRAGDROP;
7329 dwStyle &= ~HDS_DRAGDROP;
7330 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
7333 /* GRIDLINES adds decoration at top so changes sizes */
7334 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_GRIDLINES)
7336 LISTVIEW_UpdateSize(infoPtr);
7340 LISTVIEW_InvalidateList(infoPtr);
7341 return dwOldExStyle;
7346 * Sets the new hot cursor used during hot tracking and hover selection.
7349 * [I] infoPtr : valid pointer to the listview structure
7350 * [I] hCursor : the new hot cursor handle
7353 * Returns the previous hot cursor
7355 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
7357 HCURSOR oldCursor = infoPtr->hHotCursor;
7359 infoPtr->hHotCursor = hCursor;
7367 * Sets the hot item index.
7370 * [I] infoPtr : valid pointer to the listview structure
7371 * [I] iIndex : index
7374 * SUCCESS : previous hot item index
7375 * FAILURE : -1 (no hot item)
7377 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
7379 INT iOldIndex = infoPtr->nHotItem;
7381 infoPtr->nHotItem = iIndex;
7389 * Sets the amount of time the cursor must hover over an item before it is selected.
7392 * [I] infoPtr : valid pointer to the listview structure
7393 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7396 * Returns the previous hover time
7398 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
7400 DWORD oldHoverTime = infoPtr->dwHoverTime;
7402 infoPtr->dwHoverTime = dwHoverTime;
7404 return oldHoverTime;
7409 * Sets spacing for icons of LVS_ICON style.
7412 * [I] infoPtr : valid pointer to the listview structure
7413 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7414 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7417 * MAKELONG(oldcx, oldcy)
7419 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
7421 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7422 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7424 TRACE("requested=(%d,%d)\n", cx, cy);
7426 /* this is supported only for LVS_ICON style */
7427 if (uView != LVS_ICON) return oldspacing;
7429 /* set to defaults, if instructed to */
7430 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
7431 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
7433 /* if 0 then compute width
7434 * FIXME: Should scan each item and determine max width of
7435 * icon or label, then make that the width */
7437 cx = infoPtr->iconSpacing.cx;
7439 /* if 0 then compute height */
7441 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
7442 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
7445 infoPtr->iconSpacing.cx = cx;
7446 infoPtr->iconSpacing.cy = cy;
7448 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7449 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
7450 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7451 infoPtr->ntmHeight);
7453 /* these depend on the iconSpacing */
7454 LISTVIEW_UpdateItemSize(infoPtr);
7459 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
7463 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
7470 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
7471 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7480 * [I] infoPtr : valid pointer to the listview structure
7481 * [I] nType : image list type
7482 * [I] himl : image list handle
7485 * SUCCESS : old image list
7488 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7490 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7491 INT oldHeight = infoPtr->nItemHeight;
7492 HIMAGELIST himlOld = 0;
7494 TRACE("(nType=%d, himl=%p\n", nType, himl);
7499 himlOld = infoPtr->himlNormal;
7500 infoPtr->himlNormal = himl;
7501 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7502 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7506 himlOld = infoPtr->himlSmall;
7507 infoPtr->himlSmall = himl;
7508 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7512 himlOld = infoPtr->himlState;
7513 infoPtr->himlState = himl;
7514 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7515 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7519 ERR("Unknown icon type=%d\n", nType);
7523 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7524 if (infoPtr->nItemHeight != oldHeight)
7525 LISTVIEW_UpdateScroll(infoPtr);
7532 * Preallocates memory (does *not* set the actual count of items !)
7535 * [I] infoPtr : valid pointer to the listview structure
7536 * [I] nItems : item count (projected number of items to allocate)
7537 * [I] dwFlags : update flags
7543 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7545 TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
7547 if (infoPtr->dwStyle & LVS_OWNERDATA)
7549 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7550 INT nOldCount = infoPtr->nItemCount;
7552 if (nItems < nOldCount)
7554 RANGE range = { nItems, nOldCount };
7555 ranges_del(infoPtr->selectionRanges, range);
7556 if (infoPtr->nFocusedItem >= nItems)
7558 LISTVIEW_SetItemFocus(infoPtr, -1);
7559 SetRectEmpty(&infoPtr->rcFocus);
7563 infoPtr->nItemCount = nItems;
7564 LISTVIEW_UpdateScroll(infoPtr);
7566 /* the flags are valid only in ownerdata report and list modes */
7567 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7569 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7570 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7572 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7573 LISTVIEW_InvalidateList(infoPtr);
7580 LISTVIEW_GetOrigin(infoPtr, &Origin);
7581 nFrom = min(nOldCount, nItems);
7582 nTo = max(nOldCount, nItems);
7584 if (uView == LVS_REPORT)
7587 rcErase.top = nFrom * infoPtr->nItemHeight;
7588 rcErase.right = infoPtr->nItemWidth;
7589 rcErase.bottom = nTo * infoPtr->nItemHeight;
7590 OffsetRect(&rcErase, Origin.x, Origin.y);
7591 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7592 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7596 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7598 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7599 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7600 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7601 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7602 OffsetRect(&rcErase, Origin.x, Origin.y);
7603 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7604 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7606 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7608 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7609 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7610 OffsetRect(&rcErase, Origin.x, Origin.y);
7611 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7612 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7618 /* According to MSDN for non-LVS_OWNERDATA this is just
7619 * a performance issue. The control allocates its internal
7620 * data structures for the number of items specified. It
7621 * cuts down on the number of memory allocations. Therefore
7622 * we will just issue a WARN here
7624 WARN("for non-ownerdata performance option not implemented.\n");
7632 * Sets the position of an item.
7635 * [I] infoPtr : valid pointer to the listview structure
7636 * [I] nItem : item index
7637 * [I] pt : coordinate
7643 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7645 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7648 TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7650 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7651 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7653 LISTVIEW_GetOrigin(infoPtr, &Origin);
7655 /* This point value seems to be an undocumented feature.
7656 * The best guess is that it means either at the origin,
7657 * or at true beginning of the list. I will assume the origin. */
7658 if ((pt.x == -1) && (pt.y == -1))
7661 if (uView == LVS_ICON)
7663 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7664 pt.y -= ICON_TOP_PADDING;
7669 infoPtr->bAutoarrange = FALSE;
7671 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7676 * Sets the state of one or many items.
7679 * [I] infoPtr : valid pointer to the listview structure
7680 * [I] nItem : item index
7681 * [I] lpLVItem : item or subitem info
7687 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7689 BOOL bResult = TRUE;
7692 lvItem.iItem = nItem;
7693 lvItem.iSubItem = 0;
7694 lvItem.mask = LVIF_STATE;
7695 lvItem.state = lpLVItem->state;
7696 lvItem.stateMask = lpLVItem->stateMask;
7697 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7701 /* apply to all items */
7702 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7703 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7706 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7709 * Update selection mark
7711 * Investigation on windows 2k showed that selection mark was updated
7712 * whenever a new selection was made, but if the selected item was
7713 * unselected it was not updated.
7715 * we are probably still not 100% accurate, but this at least sets the
7716 * proper selection mark when it is needed
7719 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7720 (infoPtr->nSelectionMark == -1))
7723 for (i = 0; i < infoPtr->nItemCount; i++)
7725 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7727 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7729 infoPtr->nSelectionMark = i;
7733 else if (ranges_contain(infoPtr->selectionRanges, i))
7735 infoPtr->nSelectionMark = i;
7746 * Sets the text of an item or subitem.
7749 * [I] hwnd : window handle
7750 * [I] nItem : item index
7751 * [I] lpLVItem : item or subitem info
7752 * [I] isW : TRUE if input is Unicode
7758 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7762 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7764 lvItem.iItem = nItem;
7765 lvItem.iSubItem = lpLVItem->iSubItem;
7766 lvItem.mask = LVIF_TEXT;
7767 lvItem.pszText = lpLVItem->pszText;
7768 lvItem.cchTextMax = lpLVItem->cchTextMax;
7770 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7772 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7777 * Set item index that marks the start of a multiple selection.
7780 * [I] infoPtr : valid pointer to the listview structure
7781 * [I] nIndex : index
7784 * Index number or -1 if there is no selection mark.
7786 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7788 INT nOldIndex = infoPtr->nSelectionMark;
7790 TRACE("(nIndex=%d)\n", nIndex);
7792 infoPtr->nSelectionMark = nIndex;
7799 * Sets the text background color.
7802 * [I] infoPtr : valid pointer to the listview structure
7803 * [I] clrTextBk : text background color
7809 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7811 TRACE("(clrTextBk=%x)\n", clrTextBk);
7813 if (infoPtr->clrTextBk != clrTextBk)
7815 infoPtr->clrTextBk = clrTextBk;
7816 LISTVIEW_InvalidateList(infoPtr);
7824 * Sets the text foreground color.
7827 * [I] infoPtr : valid pointer to the listview structure
7828 * [I] clrText : text color
7834 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7836 TRACE("(clrText=%x)\n", clrText);
7838 if (infoPtr->clrText != clrText)
7840 infoPtr->clrText = clrText;
7841 LISTVIEW_InvalidateList(infoPtr);
7849 * Determines which listview item is located at the specified position.
7852 * [I] infoPtr : valid pointer to the listview structure
7853 * [I] hwndNewToolTip : handle to new ToolTip
7858 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7860 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7861 infoPtr->hwndToolTip = hwndNewToolTip;
7862 return hwndOldToolTip;
7867 * sets the Unicode character format flag for the control
7869 * [I] infoPtr :valid pointer to the listview structure
7870 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
7873 * Old Unicode Format
7875 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL fUnicode)
7877 BOOL rc = infoPtr->notifyFormat;
7878 infoPtr->notifyFormat = (fUnicode)?NFR_UNICODE:NFR_ANSI;
7882 /* LISTVIEW_SetWorkAreas */
7886 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMS
7889 * [I] first : pointer to first ITEM_INFO to compare
7890 * [I] second : pointer to second ITEM_INFO to compare
7891 * [I] lParam : HWND of control
7894 * if first comes before second : negative
7895 * if first comes after second : positive
7896 * if first and second are equivalent : zero
7898 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7900 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7901 ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
7902 ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
7904 /* Forward the call to the client defined callback */
7905 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7910 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMSEX
7913 * [I] first : pointer to first ITEM_INFO to compare
7914 * [I] second : pointer to second ITEM_INFO to compare
7915 * [I] lParam : HWND of control
7918 * if first comes before second : negative
7919 * if first comes after second : positive
7920 * if first and second are equivalent : zero
7922 static INT WINAPI LISTVIEW_CallBackCompareEx(LPVOID first, LPVOID second, LPARAM lParam)
7924 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7925 INT first_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, first );
7926 INT second_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, second );
7928 /* Forward the call to the client defined callback */
7929 return (infoPtr->pfnCompare)( first_idx, second_idx, infoPtr->lParamSort );
7934 * Sorts the listview items.
7937 * [I] infoPtr : valid pointer to the listview structure
7938 * [I] pfnCompare : application-defined value
7939 * [I] lParamSort : pointer to comparison callback
7940 * [I] IsEx : TRUE when LVM_SORTITEMSEX used
7946 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare,
7947 LPARAM lParamSort, BOOL IsEx)
7949 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7952 LPVOID selectionMarkItem = NULL;
7953 LPVOID focusedItem = NULL;
7956 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7958 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7960 if (!pfnCompare) return FALSE;
7961 if (!infoPtr->hdpaItems) return FALSE;
7963 /* if there are 0 or 1 items, there is no need to sort */
7964 if (infoPtr->nItemCount < 2) return TRUE;
7966 /* clear selection */
7967 ranges_clear(infoPtr->selectionRanges);
7969 /* save selection mark and focused item */
7970 if (infoPtr->nSelectionMark >= 0)
7971 selectionMarkItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark);
7972 if (infoPtr->nFocusedItem >= 0)
7973 focusedItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7975 infoPtr->pfnCompare = pfnCompare;
7976 infoPtr->lParamSort = lParamSort;
7978 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompareEx, (LPARAM)infoPtr);
7980 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7982 /* restore selection ranges */
7983 for (i=0; i < infoPtr->nItemCount; i++)
7985 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
7986 lpItem = DPA_GetPtr(hdpaSubItems, 0);
7988 if (lpItem->state & LVIS_SELECTED)
7989 ranges_additem(infoPtr->selectionRanges, i);
7991 /* restore selection mark and focused item */
7992 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7993 infoPtr->nFocusedItem = DPA_GetPtrIndex(infoPtr->hdpaItems, focusedItem);
7995 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7997 /* refresh the display */
7998 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7999 LISTVIEW_InvalidateList(infoPtr);
8006 * Update theme handle after a theme change.
8009 * [I] infoPtr : valid pointer to the listview structure
8013 * FAILURE : something else
8015 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr)
8017 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8018 CloseThemeData(theme);
8019 OpenThemeData(infoPtr->hwndSelf, themeClass);
8025 * Updates an items or rearranges the listview control.
8028 * [I] infoPtr : valid pointer to the listview structure
8029 * [I] nItem : item index
8035 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
8037 TRACE("(nItem=%d)\n", nItem);
8039 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
8041 /* rearrange with default alignment style */
8042 if (is_autoarrange(infoPtr))
8043 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8045 LISTVIEW_InvalidateItem(infoPtr, nItem);
8052 * Draw the track line at the place defined in the infoPtr structure.
8053 * The line is drawn with a XOR pen so drawing the line for the second time
8054 * in the same place erases the line.
8057 * [I] infoPtr : valid pointer to the listview structure
8063 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
8069 if (infoPtr->xTrackLine == -1)
8072 if (!(hdc = GetDC(infoPtr->hwndSelf)))
8074 hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
8075 oldROP = SetROP2(hdc, R2_XORPEN);
8076 MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
8077 LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
8078 SetROP2(hdc, oldROP);
8079 SelectObject(hdc, hOldPen);
8080 ReleaseDC(infoPtr->hwndSelf, hdc);
8086 * Called when an edit control should be displayed. This function is called after
8087 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
8090 * [I] hwnd : Handle to the listview
8091 * [I] uMsg : WM_TIMER (ignored)
8092 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
8093 * [I] dwTimer : The elapsed time (ignored)
8098 static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
8100 DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
8101 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8103 KillTimer(hwnd, idEvent);
8104 editItem->fEnabled = FALSE;
8105 /* check if the item is still selected */
8106 if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
8107 LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
8112 * Creates the listview control - the WM_NCCREATE phase.
8115 * [I] hwnd : window handle
8116 * [I] lpcs : the create parameters
8122 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
8124 LISTVIEW_INFO *infoPtr;
8127 TRACE("(lpcs=%p)\n", lpcs);
8129 /* initialize info pointer */
8130 infoPtr = Alloc(sizeof(LISTVIEW_INFO));
8131 if (!infoPtr) return FALSE;
8133 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
8135 infoPtr->hwndSelf = hwnd;
8136 infoPtr->dwStyle = lpcs->style; /* Note: may be changed in WM_CREATE */
8137 /* determine the type of structures to use */
8138 infoPtr->hwndNotify = lpcs->hwndParent;
8139 /* infoPtr->notifyFormat will be filled in WM_CREATE */
8141 /* initialize color information */
8142 infoPtr->clrBk = CLR_NONE;
8143 infoPtr->clrText = CLR_DEFAULT;
8144 infoPtr->clrTextBk = CLR_DEFAULT;
8145 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
8147 /* set default values */
8148 infoPtr->nFocusedItem = -1;
8149 infoPtr->nSelectionMark = -1;
8150 infoPtr->nHotItem = -1;
8151 infoPtr->bRedraw = TRUE;
8152 infoPtr->bNoItemMetrics = TRUE;
8153 infoPtr->bDoChangeNotify = TRUE;
8154 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
8155 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
8156 infoPtr->nEditLabelItem = -1;
8157 infoPtr->dwHoverTime = -1; /* default system hover time */
8158 infoPtr->nMeasureItemHeight = 0;
8159 infoPtr->xTrackLine = -1; /* no track line */
8160 infoPtr->itemEdit.fEnabled = FALSE;
8161 infoPtr->iVersion = COMCTL32_VERSION;
8163 /* get default font (icon title) */
8164 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
8165 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
8166 infoPtr->hFont = infoPtr->hDefaultFont;
8167 LISTVIEW_SaveTextMetrics(infoPtr);
8169 /* allocate memory for the data structure */
8170 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
8171 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
8172 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
8173 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
8174 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
8178 DestroyWindow(infoPtr->hwndHeader);
8179 ranges_destroy(infoPtr->selectionRanges);
8180 DPA_Destroy(infoPtr->hdpaItems);
8181 DPA_Destroy(infoPtr->hdpaPosX);
8182 DPA_Destroy(infoPtr->hdpaPosY);
8183 DPA_Destroy(infoPtr->hdpaColumns);
8190 * Creates the listview control - the WM_CREATE phase. Most of the data is
8191 * already set up in LISTVIEW_NCCreate
8194 * [I] hwnd : window handle
8195 * [I] lpcs : the create parameters
8201 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
8203 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8204 UINT uView = lpcs->style & LVS_TYPEMASK;
8206 TRACE("(lpcs=%p)\n", lpcs);
8208 infoPtr->dwStyle = lpcs->style;
8209 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
8210 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
8212 if ((uView == LVS_REPORT) && (lpcs->style & WS_VISIBLE))
8214 if (LISTVIEW_CreateHeader(infoPtr) < 0) return -1;
8217 infoPtr->hwndHeader = 0;
8219 /* init item size to avoid division by 0 */
8220 LISTVIEW_UpdateItemSize (infoPtr);
8222 if (uView == LVS_REPORT)
8224 if (!(LVS_NOCOLUMNHEADER & lpcs->style) && (WS_VISIBLE & lpcs->style))
8226 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8228 LISTVIEW_UpdateScroll(infoPtr);
8231 OpenThemeData(hwnd, themeClass);
8233 /* initialize the icon sizes */
8234 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
8235 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
8241 * Destroys the listview control.
8244 * [I] infoPtr : valid pointer to the listview structure
8250 static LRESULT LISTVIEW_Destroy(const LISTVIEW_INFO *infoPtr)
8252 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8253 CloseThemeData(theme);
8259 * Enables the listview control.
8262 * [I] infoPtr : valid pointer to the listview structure
8263 * [I] bEnable : specifies whether to enable or disable the window
8269 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr, BOOL bEnable)
8271 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
8272 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8278 * Erases the background of the listview control.
8281 * [I] infoPtr : valid pointer to the listview structure
8282 * [I] hdc : device context handle
8288 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
8292 TRACE("(hdc=%p)\n", hdc);
8294 if (!GetClipBox(hdc, &rc)) return FALSE;
8296 /* for double buffered controls we need to do this during refresh */
8297 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
8299 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
8305 * Helper function for LISTVIEW_[HV]Scroll *only*.
8306 * Performs vertical/horizontal scrolling by a give amount.
8309 * [I] infoPtr : valid pointer to the listview structure
8310 * [I] dx : amount of horizontal scroll
8311 * [I] dy : amount of vertical scroll
8313 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
8315 /* now we can scroll the list */
8316 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
8317 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
8318 /* if we have focus, adjust rect */
8319 OffsetRect(&infoPtr->rcFocus, dx, dy);
8320 UpdateWindow(infoPtr->hwndSelf);
8325 * Performs vertical scrolling.
8328 * [I] infoPtr : valid pointer to the listview structure
8329 * [I] nScrollCode : scroll code
8330 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8331 * [I] hScrollWnd : scrollbar control window handle
8337 * SB_LINEUP/SB_LINEDOWN:
8338 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8339 * for LVS_REPORT is 1 line
8340 * for LVS_LIST cannot occur
8343 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8344 INT nScrollDiff, HWND hScrollWnd)
8346 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8347 INT nOldScrollPos, nNewScrollPos;
8348 SCROLLINFO scrollInfo;
8351 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8352 debugscrollcode(nScrollCode), nScrollDiff);
8354 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8356 scrollInfo.cbSize = sizeof(SCROLLINFO);
8357 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8359 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
8361 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
8363 nOldScrollPos = scrollInfo.nPos;
8364 switch (nScrollCode)
8370 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
8374 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
8378 nScrollDiff = -scrollInfo.nPage;
8382 nScrollDiff = scrollInfo.nPage;
8385 case SB_THUMBPOSITION:
8387 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8394 /* quit right away if pos isn't changing */
8395 if (nScrollDiff == 0) return 0;
8397 /* calculate new position, and handle overflows */
8398 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8399 if (nScrollDiff > 0) {
8400 if (nNewScrollPos < nOldScrollPos ||
8401 nNewScrollPos > scrollInfo.nMax)
8402 nNewScrollPos = scrollInfo.nMax;
8404 if (nNewScrollPos > nOldScrollPos ||
8405 nNewScrollPos < scrollInfo.nMin)
8406 nNewScrollPos = scrollInfo.nMin;
8409 /* set the new position, and reread in case it changed */
8410 scrollInfo.fMask = SIF_POS;
8411 scrollInfo.nPos = nNewScrollPos;
8412 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
8414 /* carry on only if it really changed */
8415 if (nNewScrollPos == nOldScrollPos) return 0;
8417 /* now adjust to client coordinates */
8418 nScrollDiff = nOldScrollPos - nNewScrollPos;
8419 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
8421 /* and scroll the window */
8422 scroll_list(infoPtr, 0, nScrollDiff);
8429 * Performs horizontal scrolling.
8432 * [I] infoPtr : valid pointer to the listview structure
8433 * [I] nScrollCode : scroll code
8434 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8435 * [I] hScrollWnd : scrollbar control window handle
8441 * SB_LINELEFT/SB_LINERIGHT:
8442 * for LVS_ICON, LVS_SMALLICON 1 pixel
8443 * for LVS_REPORT is 1 pixel
8444 * for LVS_LIST is 1 column --> which is a 1 because the
8445 * scroll is based on columns not pixels
8448 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8449 INT nScrollDiff, HWND hScrollWnd)
8451 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8452 INT nOldScrollPos, nNewScrollPos;
8453 SCROLLINFO scrollInfo;
8455 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8456 debugscrollcode(nScrollCode), nScrollDiff);
8458 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8460 scrollInfo.cbSize = sizeof(SCROLLINFO);
8461 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8463 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
8465 nOldScrollPos = scrollInfo.nPos;
8467 switch (nScrollCode)
8481 nScrollDiff = -scrollInfo.nPage;
8485 nScrollDiff = scrollInfo.nPage;
8488 case SB_THUMBPOSITION:
8490 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8497 /* quit right away if pos isn't changing */
8498 if (nScrollDiff == 0) return 0;
8500 /* calculate new position, and handle overflows */
8501 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8502 if (nScrollDiff > 0) {
8503 if (nNewScrollPos < nOldScrollPos ||
8504 nNewScrollPos > scrollInfo.nMax)
8505 nNewScrollPos = scrollInfo.nMax;
8507 if (nNewScrollPos > nOldScrollPos ||
8508 nNewScrollPos < scrollInfo.nMin)
8509 nNewScrollPos = scrollInfo.nMin;
8512 /* set the new position, and reread in case it changed */
8513 scrollInfo.fMask = SIF_POS;
8514 scrollInfo.nPos = nNewScrollPos;
8515 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
8517 /* carry on only if it really changed */
8518 if (nNewScrollPos == nOldScrollPos) return 0;
8520 if(uView == LVS_REPORT)
8521 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
8523 /* now adjust to client coordinates */
8524 nScrollDiff = nOldScrollPos - nNewScrollPos;
8525 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
8527 /* and scroll the window */
8528 scroll_list(infoPtr, nScrollDiff, 0);
8533 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
8535 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8536 INT gcWheelDelta = 0;
8537 INT pulScrollLines = 3;
8538 SCROLLINFO scrollInfo;
8540 TRACE("(wheelDelta=%d)\n", wheelDelta);
8542 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8543 gcWheelDelta -= wheelDelta;
8545 scrollInfo.cbSize = sizeof(SCROLLINFO);
8546 scrollInfo.fMask = SIF_POS;
8553 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8554 * should be fixed in the future.
8556 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
8557 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8561 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8563 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
8564 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8565 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
8570 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8581 * [I] infoPtr : valid pointer to the listview structure
8582 * [I] nVirtualKey : virtual key
8583 * [I] lKeyData : key data
8588 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8590 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8591 HWND hwndSelf = infoPtr->hwndSelf;
8593 NMLVKEYDOWN nmKeyDown;
8595 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
8597 /* send LVN_KEYDOWN notification */
8598 nmKeyDown.wVKey = nVirtualKey;
8599 nmKeyDown.flags = 0;
8600 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8601 if (!IsWindow(hwndSelf))
8604 switch (nVirtualKey)
8607 nItem = infoPtr->nFocusedItem;
8608 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
8609 toggle_checkbox_state(infoPtr, infoPtr->nFocusedItem);
8613 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8615 if (!notify(infoPtr, NM_RETURN)) return 0;
8616 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8621 if (infoPtr->nItemCount > 0)
8626 if (infoPtr->nItemCount > 0)
8627 nItem = infoPtr->nItemCount - 1;
8631 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8635 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8639 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8643 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8647 if (uView == LVS_REPORT)
8649 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8650 if (infoPtr->nFocusedItem == topidx)
8651 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8656 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8657 * LISTVIEW_GetCountPerRow(infoPtr);
8658 if(nItem < 0) nItem = 0;
8662 if (uView == LVS_REPORT)
8664 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8665 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8666 if (infoPtr->nFocusedItem == topidx + cnt - 1)
8667 nItem = infoPtr->nFocusedItem + cnt - 1;
8669 nItem = topidx + cnt - 1;
8672 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8673 * LISTVIEW_GetCountPerRow(infoPtr);
8674 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8678 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8679 LISTVIEW_KeySelection(infoPtr, nItem, nVirtualKey == VK_SPACE);
8689 * [I] infoPtr : valid pointer to the listview structure
8694 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8698 /* if we did not have the focus, there's nothing to do */
8699 if (!infoPtr->bFocus) return 0;
8701 /* send NM_KILLFOCUS notification */
8702 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8704 /* if we have a focus rectangle, get rid of it */
8705 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8707 /* set window focus flag */
8708 infoPtr->bFocus = FALSE;
8710 /* invalidate the selected items before resetting focus flag */
8711 LISTVIEW_InvalidateSelectedItems(infoPtr);
8718 * Processes double click messages (left mouse button).
8721 * [I] infoPtr : valid pointer to the listview structure
8722 * [I] wKey : key flag
8723 * [I] x,y : mouse coordinate
8728 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8730 LVHITTESTINFO htInfo;
8732 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8734 /* Cancel the item edition if any */
8735 if (infoPtr->itemEdit.fEnabled)
8737 KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
8738 infoPtr->itemEdit.fEnabled = FALSE;
8741 /* send NM_RELEASEDCAPTURE notification */
8742 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8747 /* send NM_DBLCLK notification */
8748 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8749 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8751 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8752 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8759 * Processes mouse down messages (left mouse button).
8762 * infoPtr [I ] valid pointer to the listview structure
8763 * wKey [I ] key flag
8764 * x,y [I ] mouse coordinate
8769 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8771 LVHITTESTINFO lvHitTestInfo;
8772 static BOOL bGroupSelect = TRUE;
8773 POINT pt = { x, y };
8776 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8778 /* send NM_RELEASEDCAPTURE notification */
8779 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8781 /* set left button down flag and record the click position */
8782 infoPtr->bLButtonDown = TRUE;
8783 infoPtr->ptClickPos = pt;
8784 infoPtr->bDragging = FALSE;
8786 lvHitTestInfo.pt.x = x;
8787 lvHitTestInfo.pt.y = y;
8789 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8790 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
8791 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8793 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8795 toggle_checkbox_state(infoPtr, nItem);
8799 if (infoPtr->dwStyle & LVS_SINGLESEL)
8801 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8802 infoPtr->nEditLabelItem = nItem;
8804 LISTVIEW_SetSelection(infoPtr, nItem);
8808 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8812 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8813 LISTVIEW_SetItemFocus(infoPtr, nItem);
8814 infoPtr->nSelectionMark = nItem;
8820 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8821 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8823 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8824 infoPtr->nSelectionMark = nItem;
8827 else if (wKey & MK_CONTROL)
8831 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8833 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8834 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8835 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8836 infoPtr->nSelectionMark = nItem;
8838 else if (wKey & MK_SHIFT)
8840 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8844 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8845 infoPtr->nEditLabelItem = nItem;
8847 /* set selection (clears other pre-existing selections) */
8848 LISTVIEW_SetSelection(infoPtr, nItem);
8852 if (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE)
8853 if(lvHitTestInfo.iItem != -1) notify_itemactivate(infoPtr,&lvHitTestInfo);
8857 /* remove all selections */
8858 LISTVIEW_DeselectAll(infoPtr);
8867 * Processes mouse up messages (left mouse button).
8870 * infoPtr [I ] valid pointer to the listview structure
8871 * wKey [I ] key flag
8872 * x,y [I ] mouse coordinate
8877 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8879 LVHITTESTINFO lvHitTestInfo;
8881 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8883 if (!infoPtr->bLButtonDown) return 0;
8885 lvHitTestInfo.pt.x = x;
8886 lvHitTestInfo.pt.y = y;
8888 /* send NM_CLICK notification */
8889 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8890 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8892 /* set left button flag */
8893 infoPtr->bLButtonDown = FALSE;
8895 if (infoPtr->bDragging)
8897 infoPtr->bDragging = FALSE;
8901 /* if we clicked on a selected item, edit the label */
8902 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8904 /* we want to make sure the user doesn't want to do a double click. So we will
8905 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
8907 infoPtr->itemEdit.fEnabled = TRUE;
8908 infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
8909 SetTimer(infoPtr->hwndSelf,
8910 (UINT_PTR)&infoPtr->itemEdit,
8911 GetDoubleClickTime(),
8912 LISTVIEW_DelayedEditItem);
8915 if (!infoPtr->bFocus)
8916 SetFocus(infoPtr->hwndSelf);
8923 * Destroys the listview control (called after WM_DESTROY).
8926 * [I] infoPtr : valid pointer to the listview structure
8931 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8935 /* delete all items */
8936 LISTVIEW_DeleteAllItems(infoPtr, TRUE);
8938 /* destroy data structure */
8939 DPA_Destroy(infoPtr->hdpaItems);
8940 DPA_Destroy(infoPtr->hdpaPosX);
8941 DPA_Destroy(infoPtr->hdpaPosY);
8942 DPA_Destroy(infoPtr->hdpaColumns);
8943 ranges_destroy(infoPtr->selectionRanges);
8945 /* destroy image lists */
8946 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8948 if (infoPtr->himlNormal)
8949 ImageList_Destroy(infoPtr->himlNormal);
8950 if (infoPtr->himlSmall)
8951 ImageList_Destroy(infoPtr->himlSmall);
8952 if (infoPtr->himlState)
8953 ImageList_Destroy(infoPtr->himlState);
8956 /* destroy font, bkgnd brush */
8958 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8959 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8961 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8963 /* free listview info pointer*/
8971 * Handles notifications from header.
8974 * [I] infoPtr : valid pointer to the listview structure
8975 * [I] nCtrlId : control identifier
8976 * [I] lpnmh : notification information
8981 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8983 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8984 HWND hwndSelf = infoPtr->hwndSelf;
8986 TRACE("(lpnmh=%p)\n", lpnmh);
8988 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8990 switch (lpnmh->hdr.code)
8995 COLUMN_INFO *lpColumnInfo;
8999 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
9002 /* remove the old line (if any) */
9003 LISTVIEW_DrawTrackLine(infoPtr);
9005 /* compute & draw the new line */
9006 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
9007 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
9008 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
9009 infoPtr->xTrackLine = x + ptOrigin.x;
9010 LISTVIEW_DrawTrackLine(infoPtr);
9016 /* remove the track line (if any) */
9017 LISTVIEW_DrawTrackLine(infoPtr);
9018 infoPtr->xTrackLine = -1;
9022 FIXME("Changing column order not implemented\n");
9025 case HDN_ITEMCHANGINGW:
9026 case HDN_ITEMCHANGINGA:
9027 return notify_forward_header(infoPtr, lpnmh);
9029 case HDN_ITEMCHANGEDW:
9030 case HDN_ITEMCHANGEDA:
9032 COLUMN_INFO *lpColumnInfo;
9035 notify_forward_header(infoPtr, lpnmh);
9036 if (!IsWindow(hwndSelf))
9039 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
9043 hdi.mask = HDI_WIDTH;
9044 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, &hdi)) return 0;
9048 cxy = lpnmh->pitem->cxy;
9050 /* determine how much we change since the last know position */
9051 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
9052 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
9055 lpColumnInfo->rcHeader.right += dx;
9056 if (lpnmh->iItem + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
9057 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
9060 /* only needs to update the scrolls */
9061 infoPtr->nItemWidth += dx;
9062 LISTVIEW_UpdateScroll(infoPtr);
9064 LISTVIEW_UpdateItemSize(infoPtr);
9065 if (uView == LVS_REPORT && is_redrawing(infoPtr))
9068 RECT rcCol = lpColumnInfo->rcHeader;
9070 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
9071 OffsetRect(&rcCol, ptOrigin.x, 0);
9073 rcCol.top = infoPtr->rcList.top;
9074 rcCol.bottom = infoPtr->rcList.bottom;
9076 /* resizing left-aligned columns leaves most of the left side untouched */
9077 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
9079 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
9082 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
9085 /* when shrinking the last column clear the now unused field */
9086 if (lpnmh->iItem == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
9092 /* deal with right from rightmost column area */
9093 right.left = rcCol.right;
9094 right.top = rcCol.top;
9095 right.bottom = rcCol.bottom;
9096 right.right = infoPtr->rcList.right;
9098 LISTVIEW_InvalidateRect(infoPtr, &right);
9101 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
9107 case HDN_ITEMCLICKW:
9108 case HDN_ITEMCLICKA:
9110 /* Handle sorting by Header Column */
9113 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
9115 nmlv.iSubItem = lpnmh->iItem;
9116 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
9117 notify_forward_header(infoPtr, lpnmh);
9121 case HDN_DIVIDERDBLCLICKW:
9122 case HDN_DIVIDERDBLCLICKA:
9123 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
9132 * Paint non-client area of control.
9135 * [I] infoPtr : valid pointer to the listview structureof the sender
9136 * [I] region : update region
9139 * TRUE - frame was painted
9140 * FALSE - call default window proc
9142 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
9144 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
9148 int cxEdge = GetSystemMetrics (SM_CXEDGE),
9149 cyEdge = GetSystemMetrics (SM_CYEDGE);
9151 if (!theme) return FALSE;
9153 GetWindowRect(infoPtr->hwndSelf, &r);
9155 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
9156 r.right - cxEdge, r.bottom - cyEdge);
9157 if (region != (HRGN)1)
9158 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
9159 OffsetRect(&r, -r.left, -r.top);
9161 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
9162 OffsetRect(&r, -r.left, -r.top);
9164 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
9165 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
9166 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
9167 ReleaseDC(infoPtr->hwndSelf, dc);
9169 /* Call default proc to get the scrollbars etc. painted */
9170 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
9177 * Determines the type of structure to use.
9180 * [I] infoPtr : valid pointer to the listview structureof the sender
9181 * [I] hwndFrom : listview window handle
9182 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
9187 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
9189 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
9191 if (nCommand == NF_REQUERY)
9192 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
9194 return infoPtr->notifyFormat;
9199 * Paints/Repaints the listview control.
9202 * [I] infoPtr : valid pointer to the listview structure
9203 * [I] hdc : device context handle
9208 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
9210 TRACE("(hdc=%p)\n", hdc);
9212 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
9214 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9216 infoPtr->bNoItemMetrics = FALSE;
9217 LISTVIEW_UpdateItemSize(infoPtr);
9218 if (uView == LVS_ICON || uView == LVS_SMALLICON)
9219 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9220 LISTVIEW_UpdateScroll(infoPtr);
9223 if (infoPtr->hwndHeader) UpdateWindow(infoPtr->hwndHeader);
9226 LISTVIEW_Refresh(infoPtr, hdc, NULL);
9231 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
9233 LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
9234 EndPaint(infoPtr->hwndSelf, &ps);
9243 * Paints/Repaints the listview control.
9246 * [I] infoPtr : valid pointer to the listview structure
9247 * [I] hdc : device context handle
9248 * [I] options : drawing options
9253 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
9255 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
9257 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
9260 if (options & PRF_ERASEBKGND)
9261 LISTVIEW_EraseBkgnd(infoPtr, hdc);
9263 if (options & PRF_CLIENT)
9264 LISTVIEW_Paint(infoPtr, hdc);
9272 * Processes double click messages (right mouse button).
9275 * [I] infoPtr : valid pointer to the listview structure
9276 * [I] wKey : key flag
9277 * [I] x,y : mouse coordinate
9282 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9284 LVHITTESTINFO lvHitTestInfo;
9286 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9288 /* send NM_RELEASEDCAPTURE notification */
9289 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9291 /* send NM_RDBLCLK notification */
9292 lvHitTestInfo.pt.x = x;
9293 lvHitTestInfo.pt.y = y;
9294 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9295 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
9302 * Processes mouse down messages (right mouse button).
9305 * [I] infoPtr : valid pointer to the listview structure
9306 * [I] wKey : key flag
9307 * [I] x,y : mouse coordinate
9312 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9314 LVHITTESTINFO lvHitTestInfo;
9317 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9319 /* send NM_RELEASEDCAPTURE notification */
9320 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9322 /* make sure the listview control window has the focus */
9323 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
9325 /* set right button down flag */
9326 infoPtr->bRButtonDown = TRUE;
9328 /* determine the index of the selected item */
9329 lvHitTestInfo.pt.x = x;
9330 lvHitTestInfo.pt.y = y;
9331 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
9333 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9335 LISTVIEW_SetItemFocus(infoPtr, nItem);
9336 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9337 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9338 LISTVIEW_SetSelection(infoPtr, nItem);
9342 LISTVIEW_DeselectAll(infoPtr);
9350 * Processes mouse up messages (right mouse button).
9353 * [I] infoPtr : valid pointer to the listview structure
9354 * [I] wKey : key flag
9355 * [I] x,y : mouse coordinate
9360 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9362 LVHITTESTINFO lvHitTestInfo;
9365 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9367 if (!infoPtr->bRButtonDown) return 0;
9369 /* set button flag */
9370 infoPtr->bRButtonDown = FALSE;
9372 /* Send NM_RCLICK notification */
9373 lvHitTestInfo.pt.x = x;
9374 lvHitTestInfo.pt.y = y;
9375 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9376 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
9378 /* Change to screen coordinate for WM_CONTEXTMENU */
9379 pt = lvHitTestInfo.pt;
9380 ClientToScreen(infoPtr->hwndSelf, &pt);
9382 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9383 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
9384 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
9395 * [I] infoPtr : valid pointer to the listview structure
9396 * [I] hwnd : window handle of window containing the cursor
9397 * [I] nHittest : hit-test code
9398 * [I] wMouseMsg : ideintifier of the mouse message
9401 * TRUE if cursor is set
9404 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
9406 LVHITTESTINFO lvHitTestInfo;
9408 if(!(LISTVIEW_isHotTracking(infoPtr))) return FALSE;
9410 if(!infoPtr->hHotCursor) return FALSE;
9412 GetCursorPos(&lvHitTestInfo.pt);
9413 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
9415 SetCursor(infoPtr->hHotCursor);
9425 * [I] infoPtr : valid pointer to the listview structure
9426 * [I] hwndLoseFocus : handle of previously focused window
9431 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
9433 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
9435 /* if we have the focus already, there's nothing to do */
9436 if (infoPtr->bFocus) return 0;
9438 /* send NM_SETFOCUS notification */
9439 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
9441 /* set window focus flag */
9442 infoPtr->bFocus = TRUE;
9444 /* put the focus rect back on */
9445 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
9447 /* redraw all visible selected items */
9448 LISTVIEW_InvalidateSelectedItems(infoPtr);
9458 * [I] infoPtr : valid pointer to the listview structure
9459 * [I] fRedraw : font handle
9460 * [I] fRedraw : redraw flag
9465 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
9467 HFONT oldFont = infoPtr->hFont;
9469 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
9471 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9472 if (infoPtr->hFont == oldFont) return 0;
9474 LISTVIEW_SaveTextMetrics(infoPtr);
9476 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
9478 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
9479 LISTVIEW_UpdateSize(infoPtr);
9480 LISTVIEW_UpdateScroll(infoPtr);
9483 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
9490 * Message handling for WM_SETREDRAW.
9491 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9494 * [I] infoPtr : valid pointer to the listview structure
9495 * [I] bRedraw: state of redraw flag
9498 * DefWinProc return value
9500 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
9502 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
9504 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9505 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
9507 infoPtr->bRedraw = bRedraw;
9509 if(!bRedraw) return 0;
9511 if (is_autoarrange(infoPtr))
9512 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9513 LISTVIEW_UpdateScroll(infoPtr);
9515 /* despite what the WM_SETREDRAW docs says, apps expect us
9516 * to invalidate the listview here... stupid! */
9517 LISTVIEW_InvalidateList(infoPtr);
9524 * Resizes the listview control. This function processes WM_SIZE
9525 * messages. At this time, the width and height are not used.
9528 * [I] infoPtr : valid pointer to the listview structure
9529 * [I] Width : new width
9530 * [I] Height : new height
9535 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
9537 RECT rcOld = infoPtr->rcList;
9539 TRACE("(width=%d, height=%d)\n", Width, Height);
9541 LISTVIEW_UpdateSize(infoPtr);
9542 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
9544 /* do not bother with display related stuff if we're not redrawing */
9545 if (!is_redrawing(infoPtr)) return 0;
9547 if (is_autoarrange(infoPtr))
9548 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9550 LISTVIEW_UpdateScroll(infoPtr);
9552 /* refresh all only for lists whose height changed significantly */
9553 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
9554 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
9555 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
9556 LISTVIEW_InvalidateList(infoPtr);
9563 * Sets the size information.
9566 * [I] infoPtr : valid pointer to the listview structure
9571 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
9573 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9575 TRACE("uView=%d, rcList(old)=%s\n", uView, wine_dbgstr_rect(&infoPtr->rcList));
9577 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
9579 if (uView == LVS_LIST)
9581 /* Apparently the "LIST" style is supposed to have the same
9582 * number of items in a column even if there is no scroll bar.
9583 * Since if a scroll bar already exists then the bottom is already
9584 * reduced, only reduce if the scroll bar does not currently exist.
9585 * The "2" is there to mimic the native control. I think it may be
9586 * related to either padding or edges. (GLA 7/2002)
9588 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
9589 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
9590 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
9592 else if (uView == LVS_REPORT)
9597 hl.prc = &infoPtr->rcList;
9599 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9600 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp.flags, wp.x, wp.y, wp.cx, wp.cy);
9601 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy,
9602 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9603 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9604 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy);
9606 infoPtr->rcList.top = max(wp.cy, 0);
9607 infoPtr->rcList.top += (infoPtr->dwLvExStyle & LVS_EX_GRIDLINES) ? 2 : 0;
9610 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
9615 * Processes WM_STYLECHANGED messages.
9618 * [I] infoPtr : valid pointer to the listview structure
9619 * [I] wStyleType : window style type (normal or extended)
9620 * [I] lpss : window style information
9625 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9626 const STYLESTRUCT *lpss)
9628 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9629 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9632 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9633 wStyleType, lpss->styleOld, lpss->styleNew);
9635 if (wStyleType != GWL_STYLE) return 0;
9637 /* FIXME: if LVS_NOSORTHEADER changed, update header */
9638 /* or LVS_SORT{AS,DES}CENDING */
9640 infoPtr->dwStyle = lpss->styleNew;
9642 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9643 ((lpss->styleNew & WS_HSCROLL) == 0))
9644 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9646 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9647 ((lpss->styleNew & WS_VSCROLL) == 0))
9648 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9650 if (uNewView != uOldView)
9652 SIZE oldIconSize = infoPtr->iconSize;
9655 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9656 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9658 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9659 SetRectEmpty(&infoPtr->rcFocus);
9661 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9662 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
9664 if (uNewView == LVS_ICON)
9666 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
9668 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9669 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9670 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
9673 else if (uNewView == LVS_REPORT)
9678 LISTVIEW_CreateHeader( infoPtr );
9680 hl.prc = &infoPtr->rcList;
9682 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9683 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
9684 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9685 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9688 LISTVIEW_UpdateItemSize(infoPtr);
9691 if (uNewView == LVS_REPORT)
9693 if ((lpss->styleOld ^ lpss->styleNew) & LVS_NOCOLUMNHEADER)
9695 if (lpss->styleNew & LVS_NOCOLUMNHEADER)
9697 /* Turn off the header control */
9698 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
9699 TRACE("Hide header control, was 0x%08x\n", style);
9700 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style | HDS_HIDDEN);
9702 /* Turn on the header control */
9703 if ((style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE)) & HDS_HIDDEN)
9705 TRACE("Show header control, was 0x%08x\n", style);
9706 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, (style & ~HDS_HIDDEN) | WS_VISIBLE);
9712 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9713 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9714 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9716 /* update the size of the client area */
9717 LISTVIEW_UpdateSize(infoPtr);
9719 /* add scrollbars if needed */
9720 LISTVIEW_UpdateScroll(infoPtr);
9722 /* invalidate client area + erase background */
9723 LISTVIEW_InvalidateList(infoPtr);
9730 * Processes WM_STYLECHANGING messages.
9733 * [I] infoPtr : valid pointer to the listview structure
9734 * [I] wStyleType : window style type (normal or extended)
9735 * [I0] lpss : window style information
9740 static INT LISTVIEW_StyleChanging(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9743 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9744 wStyleType, lpss->styleOld, lpss->styleNew);
9746 /* don't forward LVS_OWNERDATA only if not already set to */
9747 if ((lpss->styleNew ^ lpss->styleOld) & LVS_OWNERDATA)
9749 if (lpss->styleOld & LVS_OWNERDATA)
9750 lpss->styleNew |= LVS_OWNERDATA;
9752 lpss->styleNew &= ~LVS_OWNERDATA;
9760 * Processes WM_SHOWWINDOW messages.
9763 * [I] infoPtr : valid pointer to the listview structure
9764 * [I] bShown : window is being shown (FALSE when hidden)
9765 * [I] iStatus : window show status
9770 static LRESULT LISTVIEW_ShowWindow(LISTVIEW_INFO *infoPtr, BOOL bShown, INT iStatus)
9772 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9774 /* header delayed creation */
9775 if ((uView == LVS_REPORT) && bShown)
9777 LISTVIEW_CreateHeader(infoPtr);
9779 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle))
9780 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9788 * Processes CCM_GETVERSION messages.
9791 * [I] infoPtr : valid pointer to the listview structure
9796 static inline LRESULT LISTVIEW_GetVersion(LISTVIEW_INFO *infoPtr)
9798 return infoPtr->iVersion;
9803 * Processes CCM_SETVERSION messages.
9806 * [I] infoPtr : valid pointer to the listview structure
9807 * [I] iVersion : version to be set
9810 * -1 when requested version is greater then DLL version;
9811 * previous version otherwise
9813 static LRESULT LISTVIEW_SetVersion(LISTVIEW_INFO *infoPtr, DWORD iVersion)
9815 INT iOldVersion = infoPtr->iVersion;
9817 if (iVersion > COMCTL32_VERSION)
9820 infoPtr->iVersion = iVersion;
9822 TRACE("new version %d\n", iVersion);
9829 * Window procedure of the listview control.
9832 static LRESULT WINAPI
9833 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9835 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9837 TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg, wParam, lParam);
9839 if (!infoPtr && (uMsg != WM_NCCREATE))
9840 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9844 case LVM_APPROXIMATEVIEWRECT:
9845 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9846 LOWORD(lParam), HIWORD(lParam));
9848 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9850 /* case LVM_CANCELEDITLABEL: */
9852 case LVM_CREATEDRAGIMAGE:
9853 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9855 case LVM_DELETEALLITEMS:
9856 return LISTVIEW_DeleteAllItems(infoPtr, FALSE);
9858 case LVM_DELETECOLUMN:
9859 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9861 case LVM_DELETEITEM:
9862 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9864 case LVM_EDITLABELW:
9865 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9867 case LVM_EDITLABELA:
9868 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9870 /* case LVM_ENABLEGROUPVIEW: */
9872 case LVM_ENSUREVISIBLE:
9873 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9876 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9879 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9881 case LVM_GETBKCOLOR:
9882 return infoPtr->clrBk;
9884 /* case LVM_GETBKIMAGE: */
9886 case LVM_GETCALLBACKMASK:
9887 return infoPtr->uCallbackMask;
9889 case LVM_GETCOLUMNA:
9890 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9892 case LVM_GETCOLUMNW:
9893 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9895 case LVM_GETCOLUMNORDERARRAY:
9896 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9898 case LVM_GETCOLUMNWIDTH:
9899 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9901 case LVM_GETCOUNTPERPAGE:
9902 return LISTVIEW_GetCountPerPage(infoPtr);
9904 case LVM_GETEDITCONTROL:
9905 return (LRESULT)infoPtr->hwndEdit;
9907 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9908 return infoPtr->dwLvExStyle;
9910 /* case LVM_GETGROUPINFO: */
9912 /* case LVM_GETGROUPMETRICS: */
9915 return (LRESULT)infoPtr->hwndHeader;
9917 case LVM_GETHOTCURSOR:
9918 return (LRESULT)infoPtr->hHotCursor;
9920 case LVM_GETHOTITEM:
9921 return infoPtr->nHotItem;
9923 case LVM_GETHOVERTIME:
9924 return infoPtr->dwHoverTime;
9926 case LVM_GETIMAGELIST:
9927 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9929 /* case LVM_GETINSERTMARK: */
9931 /* case LVM_GETINSERTMARKCOLOR: */
9933 /* case LVM_GETINSERTMARKRECT: */
9935 case LVM_GETISEARCHSTRINGA:
9936 case LVM_GETISEARCHSTRINGW:
9937 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9941 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9944 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9946 case LVM_GETITEMCOUNT:
9947 return infoPtr->nItemCount;
9949 case LVM_GETITEMPOSITION:
9950 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9952 case LVM_GETITEMRECT:
9953 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9955 case LVM_GETITEMSPACING:
9956 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9958 case LVM_GETITEMSTATE:
9959 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9961 case LVM_GETITEMTEXTA:
9962 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9964 case LVM_GETITEMTEXTW:
9965 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9967 case LVM_GETNEXTITEM:
9968 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9970 case LVM_GETNUMBEROFWORKAREAS:
9971 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9975 if (!lParam) return FALSE;
9976 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT ||
9977 (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST) return FALSE;
9978 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9981 /* case LVM_GETOUTLINECOLOR: */
9983 /* case LVM_GETSELECTEDCOLUMN: */
9985 case LVM_GETSELECTEDCOUNT:
9986 return LISTVIEW_GetSelectedCount(infoPtr);
9988 case LVM_GETSELECTIONMARK:
9989 return infoPtr->nSelectionMark;
9991 case LVM_GETSTRINGWIDTHA:
9992 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9994 case LVM_GETSTRINGWIDTHW:
9995 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9997 case LVM_GETSUBITEMRECT:
9998 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
10000 case LVM_GETTEXTBKCOLOR:
10001 return infoPtr->clrTextBk;
10003 case LVM_GETTEXTCOLOR:
10004 return infoPtr->clrText;
10006 /* case LVM_GETTILEINFO: */
10008 /* case LVM_GETTILEVIEWINFO: */
10010 case LVM_GETTOOLTIPS:
10011 if( !infoPtr->hwndToolTip )
10012 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
10013 return (LRESULT)infoPtr->hwndToolTip;
10015 case LVM_GETTOPINDEX:
10016 return LISTVIEW_GetTopIndex(infoPtr);
10018 case LVM_GETUNICODEFORMAT:
10019 return (infoPtr->notifyFormat == NFR_UNICODE);
10021 /* case LVM_GETVIEW: */
10023 case LVM_GETVIEWRECT:
10024 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
10026 case LVM_GETWORKAREAS:
10027 FIXME("LVM_GETWORKAREAS: unimplemented\n");
10030 /* case LVM_HASGROUP: */
10033 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
10035 case LVM_INSERTCOLUMNA:
10036 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10038 case LVM_INSERTCOLUMNW:
10039 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10041 /* case LVM_INSERTGROUP: */
10043 /* case LVM_INSERTGROUPSORTED: */
10045 case LVM_INSERTITEMA:
10046 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
10048 case LVM_INSERTITEMW:
10049 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
10051 /* case LVM_INSERTMARKHITTEST: */
10053 /* case LVM_ISGROUPVIEWENABLED: */
10055 /* case LVM_MAPIDTOINDEX: */
10057 /* case LVM_MAPINDEXTOID: */
10059 /* case LVM_MOVEGROUP: */
10061 /* case LVM_MOVEITEMTOGROUP: */
10063 case LVM_REDRAWITEMS:
10064 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
10066 /* case LVM_REMOVEALLGROUPS: */
10068 /* case LVM_REMOVEGROUP: */
10071 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
10073 case LVM_SETBKCOLOR:
10074 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
10076 /* case LVM_SETBKIMAGE: */
10078 case LVM_SETCALLBACKMASK:
10079 infoPtr->uCallbackMask = (UINT)wParam;
10082 case LVM_SETCOLUMNA:
10083 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
10085 case LVM_SETCOLUMNW:
10086 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
10088 case LVM_SETCOLUMNORDERARRAY:
10089 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
10091 case LVM_SETCOLUMNWIDTH:
10092 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
10094 case LVM_SETEXTENDEDLISTVIEWSTYLE:
10095 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
10097 /* case LVM_SETGROUPINFO: */
10099 /* case LVM_SETGROUPMETRICS: */
10101 case LVM_SETHOTCURSOR:
10102 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
10104 case LVM_SETHOTITEM:
10105 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
10107 case LVM_SETHOVERTIME:
10108 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
10110 case LVM_SETICONSPACING:
10111 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10113 case LVM_SETIMAGELIST:
10114 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
10116 /* case LVM_SETINFOTIP: */
10118 /* case LVM_SETINSERTMARK: */
10120 /* case LVM_SETINSERTMARKCOLOR: */
10125 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
10126 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, (uMsg == LVM_SETITEMW));
10129 case LVM_SETITEMCOUNT:
10130 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
10132 case LVM_SETITEMPOSITION:
10135 pt.x = (short)LOWORD(lParam);
10136 pt.y = (short)HIWORD(lParam);
10137 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
10140 case LVM_SETITEMPOSITION32:
10141 if (lParam == 0) return FALSE;
10142 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
10144 case LVM_SETITEMSTATE:
10145 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
10147 case LVM_SETITEMTEXTA:
10148 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
10150 case LVM_SETITEMTEXTW:
10151 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
10153 /* case LVM_SETOUTLINECOLOR: */
10155 /* case LVM_SETSELECTEDCOLUMN: */
10157 case LVM_SETSELECTIONMARK:
10158 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
10160 case LVM_SETTEXTBKCOLOR:
10161 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
10163 case LVM_SETTEXTCOLOR:
10164 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
10166 /* case LVM_SETTILEINFO: */
10168 /* case LVM_SETTILEVIEWINFO: */
10170 /* case LVM_SETTILEWIDTH: */
10172 case LVM_SETTOOLTIPS:
10173 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
10175 case LVM_SETUNICODEFORMAT:
10176 return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
10178 /* case LVM_SETVIEW: */
10180 /* case LVM_SETWORKAREAS: */
10182 /* case LVM_SORTGROUPS: */
10184 case LVM_SORTITEMS:
10185 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam, FALSE);
10187 case LVM_SORTITEMSEX:
10188 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam, TRUE);
10190 case LVM_SUBITEMHITTEST:
10191 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
10194 return LISTVIEW_Update(infoPtr, (INT)wParam);
10196 case CCM_GETVERSION:
10197 return LISTVIEW_GetVersion(infoPtr);
10199 case CCM_SETVERSION:
10200 return LISTVIEW_SetVersion(infoPtr, wParam);
10203 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
10206 return LISTVIEW_Command(infoPtr, wParam, lParam);
10209 return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
10212 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
10215 return LISTVIEW_Destroy(infoPtr);
10218 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
10220 case WM_ERASEBKGND:
10221 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
10223 case WM_GETDLGCODE:
10224 return DLGC_WANTCHARS | DLGC_WANTARROWS;
10227 return (LRESULT)infoPtr->hFont;
10230 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10233 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
10236 return LISTVIEW_KillFocus(infoPtr);
10238 case WM_LBUTTONDBLCLK:
10239 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10241 case WM_LBUTTONDOWN:
10242 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10245 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10248 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10250 case WM_MOUSEHOVER:
10251 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10254 return LISTVIEW_NCDestroy(infoPtr);
10257 if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
10262 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
10263 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
10266 case WM_NOTIFYFORMAT:
10267 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
10269 case WM_PRINTCLIENT:
10270 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
10273 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
10275 case WM_RBUTTONDBLCLK:
10276 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10278 case WM_RBUTTONDOWN:
10279 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10282 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10285 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
10290 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
10293 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
10296 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
10298 case WM_SHOWWINDOW:
10299 LISTVIEW_ShowWindow(infoPtr, (BOOL)wParam, (INT)lParam);
10300 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10303 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10305 case WM_STYLECHANGED:
10306 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10308 case WM_STYLECHANGING:
10309 return LISTVIEW_StyleChanging(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10311 case WM_SYSCOLORCHANGE:
10312 COMCTL32_RefreshSysColors();
10315 /* case WM_TIMER: */
10316 case WM_THEMECHANGED:
10317 return LISTVIEW_ThemeChanged(infoPtr);
10320 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10322 case WM_MOUSEWHEEL:
10323 if (wParam & (MK_SHIFT | MK_CONTROL))
10324 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10325 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
10327 case WM_WINDOWPOSCHANGED:
10328 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
10330 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
10331 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
10332 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
10334 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
10336 MEASUREITEMSTRUCT mis;
10337 mis.CtlType = ODT_LISTVIEW;
10338 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
10342 mis.itemHeight= infoPtr->nItemHeight;
10343 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
10344 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
10345 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
10348 LISTVIEW_UpdateSize(infoPtr);
10349 LISTVIEW_UpdateScroll(infoPtr);
10351 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10353 /* case WM_WININICHANGE: */
10356 if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
10357 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
10360 /* call default window procedure */
10361 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10368 * Registers the window class.
10376 void LISTVIEW_Register(void)
10378 WNDCLASSW wndClass;
10380 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
10381 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
10382 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
10383 wndClass.cbClsExtra = 0;
10384 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
10385 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
10386 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
10387 wndClass.lpszClassName = WC_LISTVIEWW;
10388 RegisterClassW(&wndClass);
10393 * Unregisters the window class.
10401 void LISTVIEW_Unregister(void)
10403 UnregisterClassW(WC_LISTVIEWW, NULL);
10408 * Handle any WM_COMMAND messages
10411 * [I] infoPtr : valid pointer to the listview structure
10412 * [I] wParam : the first message parameter
10413 * [I] lParam : the second message parameter
10418 static LRESULT LISTVIEW_Command(const LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
10420 switch (HIWORD(wParam))
10425 * Adjust the edit window size
10427 WCHAR buffer[1024];
10428 HDC hdc = GetDC(infoPtr->hwndEdit);
10429 HFONT hFont, hOldFont = 0;
10433 if (!infoPtr->hwndEdit || !hdc) return 0;
10434 GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
10435 GetWindowRect(infoPtr->hwndEdit, &rect);
10437 /* Select font to get the right dimension of the string */
10438 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
10441 hOldFont = SelectObject(hdc, hFont);
10444 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
10446 TEXTMETRICW textMetric;
10448 /* Add Extra spacing for the next character */
10449 GetTextMetricsW(hdc, &textMetric);
10450 sz.cx += (textMetric.tmMaxCharWidth * 2);
10458 rect.bottom - rect.top,
10459 SWP_DRAWFRAME|SWP_NOMOVE);
10462 SelectObject(hdc, hOldFont);
10464 ReleaseDC(infoPtr->hwndEdit, hdc);
10470 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
10479 * Subclassed edit control windproc function
10482 * [I] hwnd : the edit window handle
10483 * [I] uMsg : the message that is to be processed
10484 * [I] wParam : first message parameter
10485 * [I] lParam : second message parameter
10486 * [I] isW : TRUE if input is Unicode
10491 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
10493 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
10494 BOOL cancel = FALSE;
10496 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10497 hwnd, uMsg, wParam, lParam, isW);
10501 case WM_GETDLGCODE:
10502 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
10509 WNDPROC editProc = infoPtr->EditWndProc;
10510 infoPtr->EditWndProc = 0;
10511 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
10512 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
10516 if (VK_ESCAPE == (INT)wParam)
10521 else if (VK_RETURN == (INT)wParam)
10525 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
10528 /* kill the edit */
10529 if (infoPtr->hwndEdit)
10531 LPWSTR buffer = NULL;
10533 infoPtr->hwndEdit = 0;
10536 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
10540 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
10542 if (isW) GetWindowTextW(hwnd, buffer, len+1);
10543 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
10547 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
10552 SendMessageW(hwnd, WM_CLOSE, 0, 0);
10558 * Subclassed edit control Unicode windproc function
10561 * [I] hwnd : the edit window handle
10562 * [I] uMsg : the message that is to be processed
10563 * [I] wParam : first message parameter
10564 * [I] lParam : second message parameter
10568 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10570 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
10575 * Subclassed edit control ANSI windproc function
10578 * [I] hwnd : the edit window handle
10579 * [I] uMsg : the message that is to be processed
10580 * [I] wParam : first message parameter
10581 * [I] lParam : second message parameter
10585 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10587 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
10592 * Creates a subclassed edit control
10595 * [I] infoPtr : valid pointer to the listview structure
10596 * [I] text : initial text for the edit
10597 * [I] style : the window style
10598 * [I] isW : TRUE if input is Unicode
10602 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
10603 INT x, INT y, INT width, INT height, BOOL isW)
10605 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
10610 TEXTMETRICW textMetric;
10611 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
10613 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
10615 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
10616 hdc = GetDC(infoPtr->hwndSelf);
10618 /* Select the font to get appropriate metric dimensions */
10619 if(infoPtr->hFont != 0)
10620 hOldFont = SelectObject(hdc, infoPtr->hFont);
10622 /*Get String Length in pixels */
10623 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
10625 /*Add Extra spacing for the next character */
10626 GetTextMetricsW(hdc, &textMetric);
10627 sz.cx += (textMetric.tmMaxCharWidth * 2);
10629 if(infoPtr->hFont != 0)
10630 SelectObject(hdc, hOldFont);
10632 ReleaseDC(infoPtr->hwndSelf, hdc);
10634 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10636 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10638 if (!hedit) return 0;
10640 infoPtr->EditWndProc = (WNDPROC)
10641 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
10642 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
10644 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);