4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2001 CodeWeavers Inc.
8 * Copyright 2002 Dimitrie O. Paun
9 * Copyright 2009 Nikolay Sivov
10 * Copyright 2009 Owen Rudge for CodeWeavers
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
28 * This code was audited for completeness against the documented features
29 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
31 * Unless otherwise noted, we believe this code to be complete, as per
32 * the specification mentioned above.
33 * If you discover missing features, or bugs, please note them below.
37 * Default Message Processing
38 * -- WM_CREATE: create the icon and small icon image lists at this point only if
39 * the LVS_SHAREIMAGELISTS style is not specified.
40 * -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
41 * or small icon and the LVS_AUTOARRANGE style is specified.
46 * -- Hot item handling, mouse hovering
47 * -- Workareas support
52 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
53 * -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
54 * -- LVA_SNAPTOGRID not implemented
55 * -- LISTVIEW_ApproximateViewRect partially implemented
56 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
57 * -- LISTVIEW_SetIconSpacing is incomplete
58 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
61 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
62 * linear in the number of items in the list, and this is
63 * unacceptable for large lists.
64 * -- if list is sorted by item text LISTVIEW_InsertItemT could use
65 * binary search to calculate item index (e.g. DPA_Search()).
66 * This requires sorted state to be reliably tracked in item modifiers.
67 * -- we should keep an ordered array of coordinates in iconic mode
68 * this would allow to frame items (iterator_frameditems),
69 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
76 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
82 * -- LVS_NOSCROLL (see Q137520)
86 * -- LVS_EX_BORDERSELECT
90 * -- LVS_EX_MULTIWORKAREAS
92 * -- LVS_EX_SIMPLESELECT
93 * -- LVS_EX_TWOCLICKACTIVATE
94 * -- LVS_EX_UNDERLINECOLD
95 * -- LVS_EX_UNDERLINEHOT
98 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
106 * -- LVM_ENABLEGROUPVIEW
107 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
108 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
109 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
110 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
111 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
112 * -- LVM_GETINSERTMARKRECT
113 * -- LVM_GETNUMBEROFWORKAREAS
114 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
115 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
116 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
117 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
118 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
119 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
120 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
121 * -- LVM_INSERTGROUPSORTED
122 * -- LVM_INSERTMARKHITTEST
123 * -- LVM_ISGROUPVIEWENABLED
125 * -- LVM_MOVEITEMTOGROUP
127 * -- LVM_SETTILEWIDTH
131 * -- ListView_GetHoverTime, ListView_SetHoverTime
132 * -- ListView_GetISearchString
133 * -- ListView_GetNumberOfWorkAreas
134 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
139 * Known differences in message stream from native control (not known if
140 * these differences cause problems):
141 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
142 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
143 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
144 * processing for "USEDOUBLECLICKTIME".
148 #include "wine/port.h"
163 #include "commctrl.h"
164 #include "comctl32.h"
167 #include "wine/debug.h"
168 #include "wine/unicode.h"
170 WINE_DEFAULT_DEBUG_CHANNEL(listview);
172 /* make sure you set this to 0 for production use! */
173 #define DEBUG_RANGES 1
175 typedef struct tagCOLUMN_INFO
177 RECT rcHeader; /* tracks the header's rectangle */
178 INT fmt; /* same as LVCOLUMN.fmt */
182 typedef struct tagITEMHDR
186 } ITEMHDR, *LPITEMHDR;
188 typedef struct tagSUBITEM_INFO
194 typedef struct tagITEM_ID ITEM_ID;
196 typedef struct tagITEM_INFO
207 UINT id; /* item id */
208 HDPA item; /* link to item data */
211 typedef struct tagRANGE
217 typedef struct tagRANGES
222 typedef struct tagITERATOR
231 typedef struct tagDELAYED_ITEM_EDIT
237 typedef struct tagLISTVIEW_INFO
241 RECT rcList; /* This rectangle is really the window
242 * client rectangle possibly reduced by the
243 * horizontal scroll bar and/or header - see
244 * LISTVIEW_UpdateSize. This rectangle offset
245 * by the LISTVIEW_GetOrigin value is in
246 * client coordinates */
248 /* notification window */
251 BOOL bDoChangeNotify; /* send change notification messages? */
258 INT nItemCount; /* the number of items in the list */
259 HDPA hdpaItems; /* array ITEM_INFO pointers */
260 HDPA hdpaItemIds; /* array of ITEM_ID pointers */
261 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
262 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
263 RANGES selectionRanges;
264 INT nSelectionMark; /* item to start next multiselection from */
266 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
269 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
270 BOOL colRectsDirty; /* trigger column rectangles requery from header */
273 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
278 PFNLVCOMPARE pfnCompare; /* sorting callback pointer */
282 DWORD dwStyle; /* the cached window GWL_STYLE */
283 DWORD dwLvExStyle; /* extended listview style */
284 DWORD uView; /* current view available through LVM_[G,S]ETVIEW */
290 DELAYED_ITEM_EDIT itemEdit; /* Pointer to this structure will be the timer ID */
293 HIMAGELIST himlNormal;
294 HIMAGELIST himlSmall;
295 HIMAGELIST himlState;
299 POINT currIconPos; /* this is the position next icon will be placed */
303 INT xTrackLine; /* The x coefficient of the track line or -1 if none */
305 /* marquee selection */
306 BOOL bMarqueeSelect; /* marquee selection/highlight underway */
308 RECT marqueeRect; /* absolute coordinates of marquee selection */
309 RECT marqueeDrawRect; /* relative coordinates for drawing marquee */
310 POINT marqueeOrigin; /* absolute coordinates of marquee click origin */
313 BOOL bFocus; /* control has focus */
315 RECT rcFocus; /* focus bounds */
326 INT ntmHeight; /* Some cached metrics of the font used */
327 INT ntmMaxCharWidth; /* by the listview to draw items */
330 /* mouse operation */
334 POINT ptClickPos; /* point where the user clicked */
335 INT nLButtonDownItem; /* tracks item to reset multiselection on WM_LBUTTONUP */
339 /* keyboard operation */
340 DWORD lastKeyPressTimestamp;
342 INT nSearchParamLength;
343 WCHAR szSearchParam[ MAX_PATH ];
346 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
347 BOOL bIsDrawing; /* Drawing in progress */
348 INT nMeasureItemHeight; /* WM_MEASUREITEM result */
349 BOOL bRedraw; /* WM_SETREDRAW switch */
352 DWORD iVersion; /* CCM_[G,S]ETVERSION */
358 /* How many we debug buffer to allocate */
359 #define DEBUG_BUFFERS 20
360 /* The size of a single debug buffer */
361 #define DEBUG_BUFFER_SIZE 256
363 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
364 #define SB_INTERNAL -1
366 /* maximum size of a label */
367 #define DISP_TEXT_SIZE 512
369 /* padding for items in list and small icon display modes */
370 #define WIDTH_PADDING 12
372 /* padding for items in list, report and small icon display modes */
373 #define HEIGHT_PADDING 1
375 /* offset of items in report display mode */
376 #define REPORT_MARGINX 2
378 /* padding for icon in large icon display mode
379 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
380 * that HITTEST will see.
381 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
382 * ICON_TOP_PADDING - sum of the two above.
383 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
384 * LABEL_HOR_PADDING - between text and sides of box
385 * LABEL_VERT_PADDING - between bottom of text and end of box
387 * ICON_LR_PADDING - additional width above icon size.
388 * ICON_LR_HALF - half of the above value
390 #define ICON_TOP_PADDING_NOTHITABLE 2
391 #define ICON_TOP_PADDING_HITABLE 2
392 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
393 #define ICON_BOTTOM_PADDING 4
394 #define LABEL_HOR_PADDING 5
395 #define LABEL_VERT_PADDING 7
396 #define ICON_LR_PADDING 16
397 #define ICON_LR_HALF (ICON_LR_PADDING/2)
399 /* default label width for items in list and small icon display modes */
400 #define DEFAULT_LABEL_WIDTH 40
401 /* maximum select rectangle width for empty text item in LV_VIEW_DETAILS */
402 #define MAX_EMPTYTEXT_SELECT_WIDTH 80
404 /* default column width for items in list display mode */
405 #define DEFAULT_COLUMN_WIDTH 96
407 /* Size of "line" scroll for V & H scrolls */
408 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
410 /* Padding between image and label */
411 #define IMAGE_PADDING 2
413 /* Padding behind the label */
414 #define TRAILING_LABEL_PADDING 12
415 #define TRAILING_HEADER_PADDING 11
417 /* Border for the icon caption */
418 #define CAPTION_BORDER 2
420 /* Standard DrawText flags */
421 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
422 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
423 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
425 /* Image index from state */
426 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
428 /* The time in milliseconds to reset the search in the list */
429 #define KEY_DELAY 450
431 /* Dump the LISTVIEW_INFO structure to the debug channel */
432 #define LISTVIEW_DUMP(iP) do { \
433 TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
434 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
435 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
436 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
437 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
438 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
439 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
440 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
441 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
442 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
445 static const WCHAR themeClass[] = {'L','i','s','t','V','i','e','w',0};
448 * forward declarations
450 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *, LPLVITEMW, BOOL);
451 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *, INT, LPRECT);
452 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *, INT, LPPOINT);
453 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *, INT, LPPOINT);
454 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *, INT, LPRECT);
455 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *, LPPOINT);
456 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *, LPRECT);
457 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
458 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
459 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *, LPCWSTR, BOOL);
460 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT, BOOL);
461 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *, INT, UINT);
462 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
463 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
464 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
465 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
466 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, BOOL);
467 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
468 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
469 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *, BOOL, BOOL);
470 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *, INT, INT);
472 /******** Text handling functions *************************************/
474 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
475 * text string. The string may be ANSI or Unicode, in which case
476 * the boolean isW tells us the type of the string.
478 * The name of the function tell what type of strings it expects:
479 * W: Unicode, T: ANSI/Unicode - function of isW
482 static inline BOOL is_textW(LPCWSTR text)
484 return text != NULL && text != LPSTR_TEXTCALLBACKW;
487 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
489 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
490 return is_textW(text);
493 static inline int textlenT(LPCWSTR text, BOOL isW)
495 return !is_textT(text, isW) ? 0 :
496 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
499 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
502 if (isSrcW) lstrcpynW(dest, src, max);
503 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
505 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
506 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
509 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
511 LPWSTR wstr = (LPWSTR)text;
513 if (!isW && is_textT(text, isW))
515 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
516 wstr = Alloc(len * sizeof(WCHAR));
517 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
519 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
523 static inline void textfreeT(LPWSTR wstr, BOOL isW)
525 if (!isW && is_textT(wstr, isW)) Free (wstr);
529 * dest is a pointer to a Unicode string
530 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
532 static BOOL textsetptrT(LPWSTR *dest, LPCWSTR src, BOOL isW)
536 if (src == LPSTR_TEXTCALLBACKW)
538 if (is_textW(*dest)) Free(*dest);
539 *dest = LPSTR_TEXTCALLBACKW;
543 LPWSTR pszText = textdupTtoW(src, isW);
544 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
545 bResult = Str_SetPtrW(dest, pszText);
546 textfreeT(pszText, isW);
552 * compares a Unicode to a Unicode/ANSI text string
554 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
556 if (!aw) return bt ? -1 : 0;
557 if (!bt) return aw ? 1 : 0;
558 if (aw == LPSTR_TEXTCALLBACKW)
559 return bt == LPSTR_TEXTCALLBACKW ? 1 : -1;
560 if (bt != LPSTR_TEXTCALLBACKW)
562 LPWSTR bw = textdupTtoW(bt, isW);
563 int r = bw ? lstrcmpW(aw, bw) : 1;
571 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
575 n = min(min(n, lstrlenW(s1)), lstrlenW(s2));
576 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
577 return res ? res - sizeof(WCHAR) : res;
580 /******** Debugging functions *****************************************/
582 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
584 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
585 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
588 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
590 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
591 n = min(textlenT(text, isW), n);
592 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
595 static char* debug_getbuf(void)
597 static int index = 0;
598 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
599 return buffers[index++ % DEBUG_BUFFERS];
602 static inline const char* debugrange(const RANGE *lprng)
604 if (!lprng) return "(null)";
605 return wine_dbg_sprintf("[%d, %d]", lprng->lower, lprng->upper);
608 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
610 char* buf = debug_getbuf(), *text = buf;
611 int len, size = DEBUG_BUFFER_SIZE;
613 if (pScrollInfo == NULL) return "(null)";
614 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
615 if (len == -1) goto end; buf += len; size -= len;
616 if (pScrollInfo->fMask & SIF_RANGE)
617 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
619 if (len == -1) goto end; buf += len; size -= len;
620 if (pScrollInfo->fMask & SIF_PAGE)
621 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
623 if (len == -1) goto end; buf += len; size -= len;
624 if (pScrollInfo->fMask & SIF_POS)
625 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
627 if (len == -1) goto end; buf += len; size -= len;
628 if (pScrollInfo->fMask & SIF_TRACKPOS)
629 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
631 if (len == -1) goto end; buf += len; size -= len;
634 buf = text + strlen(text);
636 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
640 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
642 if (!plvnm) return "(null)";
643 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
644 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
645 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
646 plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam);
649 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
651 char* buf = debug_getbuf(), *text = buf;
652 int len, size = DEBUG_BUFFER_SIZE;
654 if (lpLVItem == NULL) return "(null)";
655 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
656 if (len == -1) goto end; buf += len; size -= len;
657 if (lpLVItem->mask & LVIF_STATE)
658 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
660 if (len == -1) goto end; buf += len; size -= len;
661 if (lpLVItem->mask & LVIF_TEXT)
662 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
664 if (len == -1) goto end; buf += len; size -= len;
665 if (lpLVItem->mask & LVIF_IMAGE)
666 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
668 if (len == -1) goto end; buf += len; size -= len;
669 if (lpLVItem->mask & LVIF_PARAM)
670 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
672 if (len == -1) goto end; buf += len; size -= len;
673 if (lpLVItem->mask & LVIF_INDENT)
674 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
676 if (len == -1) goto end; buf += len; size -= len;
679 buf = text + strlen(text);
681 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
685 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
687 char* buf = debug_getbuf(), *text = buf;
688 int len, size = DEBUG_BUFFER_SIZE;
690 if (lpColumn == NULL) return "(null)";
691 len = snprintf(buf, size, "{");
692 if (len == -1) goto end; buf += len; size -= len;
693 if (lpColumn->mask & LVCF_SUBITEM)
694 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
696 if (len == -1) goto end; buf += len; size -= len;
697 if (lpColumn->mask & LVCF_FMT)
698 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
700 if (len == -1) goto end; buf += len; size -= len;
701 if (lpColumn->mask & LVCF_WIDTH)
702 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
704 if (len == -1) goto end; buf += len; size -= len;
705 if (lpColumn->mask & LVCF_TEXT)
706 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
708 if (len == -1) goto end; buf += len; size -= len;
709 if (lpColumn->mask & LVCF_IMAGE)
710 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
712 if (len == -1) goto end; buf += len; size -= len;
713 if (lpColumn->mask & LVCF_ORDER)
714 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
716 if (len == -1) goto end; buf += len; size -= len;
719 buf = text + strlen(text);
721 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
725 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
727 if (!lpht) return "(null)";
729 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
730 wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
733 /* Return the corresponding text for a given scroll value */
734 static inline LPCSTR debugscrollcode(int nScrollCode)
738 case SB_LINELEFT: return "SB_LINELEFT";
739 case SB_LINERIGHT: return "SB_LINERIGHT";
740 case SB_PAGELEFT: return "SB_PAGELEFT";
741 case SB_PAGERIGHT: return "SB_PAGERIGHT";
742 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
743 case SB_THUMBTRACK: return "SB_THUMBTRACK";
744 case SB_ENDSCROLL: return "SB_ENDSCROLL";
745 case SB_INTERNAL: return "SB_INTERNAL";
746 default: return "unknown";
751 /******** Notification functions ************************************/
753 static int get_ansi_notification(UINT unicodeNotificationCode)
755 switch (unicodeNotificationCode)
757 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
758 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
759 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
760 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
761 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
762 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
763 /* header forwards */
764 case HDN_TRACKW: return HDN_TRACKA;
765 case HDN_ENDTRACKW: return HDN_ENDTRACKA;
766 case HDN_BEGINDRAG: return HDN_BEGINDRAG;
767 case HDN_ENDDRAG: return HDN_ENDDRAG;
768 case HDN_ITEMCHANGINGW: return HDN_ITEMCHANGINGA;
769 case HDN_ITEMCHANGEDW: return HDN_ITEMCHANGEDA;
770 case HDN_ITEMCLICKW: return HDN_ITEMCLICKA;
771 case HDN_DIVIDERDBLCLICKW: return HDN_DIVIDERDBLCLICKA;
773 ERR("unknown notification %x\n", unicodeNotificationCode);
778 /* forwards header notifications to listview parent */
779 static LRESULT notify_forward_header(const LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
783 HD_TEXTFILTERA textfilter;
784 LPSTR text = NULL, filter = NULL;
787 /* on unicode format exit earlier */
788 if (infoPtr->notifyFormat == NFR_UNICODE)
789 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
790 (WPARAM)lpnmh->hdr.idFrom, (LPARAM)lpnmh);
792 /* header always supplies unicode notifications,
793 all we have to do is to convert strings to ANSI */
794 nmhA = *(NMHEADERA*)lpnmh;
797 hditema = *(HDITEMA*)lpnmh->pitem;
798 nmhA.pitem = &hditema;
799 /* convert item text */
800 if (lpnmh->pitem->mask & HDI_TEXT)
802 hditema.pszText = NULL;
803 Str_SetPtrWtoA(&hditema.pszText, lpnmh->pitem->pszText);
804 text = hditema.pszText;
806 /* convert filter text */
807 if ((lpnmh->pitem->mask & HDI_FILTER) && (lpnmh->pitem->type == HDFT_ISSTRING) &&
808 lpnmh->pitem->pvFilter)
810 hditema.pvFilter = &textfilter;
811 textfilter = *(HD_TEXTFILTERA*)(lpnmh->pitem->pvFilter);
812 textfilter.pszText = NULL;
813 Str_SetPtrWtoA(&textfilter.pszText, ((HD_TEXTFILTERW*)lpnmh->pitem->pvFilter)->pszText);
814 filter = textfilter.pszText;
817 nmhA.hdr.code = get_ansi_notification(lpnmh->hdr.code);
819 ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
820 (WPARAM)nmhA.hdr.idFrom, (LPARAM)&nmhA);
829 static LRESULT notify_hdr(const LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
833 TRACE("(code=%d)\n", code);
835 pnmh->hwndFrom = infoPtr->hwndSelf;
836 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
838 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, pnmh->idFrom, (LPARAM)pnmh);
840 TRACE(" <= %ld\n", result);
845 static inline BOOL notify(const LISTVIEW_INFO *infoPtr, INT code)
848 HWND hwnd = infoPtr->hwndSelf;
849 notify_hdr(infoPtr, code, &nmh);
850 return IsWindow(hwnd);
853 static inline void notify_itemactivate(const LISTVIEW_INFO *infoPtr, const LVHITTESTINFO *htInfo)
864 item.mask = LVIF_PARAM|LVIF_STATE;
865 item.iItem = htInfo->iItem;
867 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
868 nmia.lParam = item.lParam;
869 nmia.uOldState = item.state;
870 nmia.uNewState = item.state | LVIS_ACTIVATING;
871 nmia.uChanged = LVIF_STATE;
874 nmia.iItem = htInfo->iItem;
875 nmia.iSubItem = htInfo->iSubItem;
876 nmia.ptAction = htInfo->pt;
878 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
879 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
880 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
882 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
885 static inline LRESULT notify_listview(const LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
887 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
888 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
891 static BOOL notify_click(const LISTVIEW_INFO *infoPtr, INT code, const LVHITTESTINFO *lvht)
895 HWND hwnd = infoPtr->hwndSelf;
897 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
898 ZeroMemory(&nmia, sizeof(nmia));
899 nmia.iItem = lvht->iItem;
900 nmia.iSubItem = lvht->iSubItem;
901 nmia.ptAction = lvht->pt;
902 item.mask = LVIF_PARAM;
903 item.iItem = lvht->iItem;
905 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmia.lParam = item.lParam;
906 notify_hdr(infoPtr, code, (LPNMHDR)&nmia);
907 return IsWindow(hwnd);
910 static BOOL notify_deleteitem(const LISTVIEW_INFO *infoPtr, INT nItem)
914 HWND hwnd = infoPtr->hwndSelf;
916 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
918 item.mask = LVIF_PARAM;
921 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
922 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
923 return IsWindow(hwnd);
927 Send notification. depends on dispinfoW having same
928 structure as dispinfoA.
929 infoPtr : listview struct
930 notificationCode : *Unicode* notification code
931 pdi : dispinfo structure (can be unicode or ansi)
932 isW : TRUE if dispinfo is Unicode
934 static BOOL notify_dispinfoT(const LISTVIEW_INFO *infoPtr, UINT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
936 BOOL bResult = FALSE;
937 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
938 INT cchTempBufMax = 0, savCchTextMax = 0;
940 LPWSTR pszTempBuf = NULL, savPszText = NULL;
942 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
944 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
945 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
948 if (convertToAnsi || convertToUnicode)
950 if (notificationCode != LVN_GETDISPINFOW)
952 cchTempBufMax = convertToUnicode ?
953 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
954 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
958 cchTempBufMax = pdi->item.cchTextMax;
959 *pdi->item.pszText = 0; /* make sure we don't process garbage */
962 pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
963 if (!pszTempBuf) return FALSE;
965 if (convertToUnicode)
966 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
967 pszTempBuf, cchTempBufMax);
969 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
970 cchTempBufMax, NULL, NULL);
972 savCchTextMax = pdi->item.cchTextMax;
973 savPszText = pdi->item.pszText;
974 pdi->item.pszText = pszTempBuf;
975 pdi->item.cchTextMax = cchTempBufMax;
978 if (infoPtr->notifyFormat == NFR_ANSI)
979 realNotifCode = get_ansi_notification(notificationCode);
981 realNotifCode = notificationCode;
982 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
983 bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
985 if (convertToUnicode || convertToAnsi)
987 if (convertToUnicode) /* note : pointer can be changed by app ! */
988 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
989 savCchTextMax, NULL, NULL);
991 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
992 savPszText, savCchTextMax);
993 pdi->item.pszText = savPszText; /* restores our buffer */
994 pdi->item.cchTextMax = savCchTextMax;
1000 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, const LISTVIEW_INFO *infoPtr, HDC hdc,
1001 const RECT *rcBounds, const LVITEMW *lplvItem)
1003 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
1004 lpnmlvcd->nmcd.hdc = hdc;
1005 lpnmlvcd->nmcd.rc = *rcBounds;
1006 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
1007 lpnmlvcd->clrText = infoPtr->clrText;
1008 if (!lplvItem) return;
1009 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
1010 lpnmlvcd->iSubItem = lplvItem->iSubItem;
1011 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
1012 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
1013 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
1014 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
1017 static inline DWORD notify_customdraw (const LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
1019 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
1022 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
1023 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
1024 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
1025 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
1026 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
1027 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
1031 static void prepaint_setup (const LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd, BOOL SubItem)
1033 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
1034 lpnmlvcd->clrTextBk = comctl32_color.clrWindow;
1035 if (lpnmlvcd->clrText == CLR_DEFAULT)
1036 lpnmlvcd->clrText = comctl32_color.clrWindowText;
1038 /* apparently, for selected items, we have to override the returned values */
1041 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
1043 if (infoPtr->bFocus)
1045 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
1046 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
1048 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
1050 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
1051 lpnmlvcd->clrText = comctl32_color.clrBtnText;
1056 /* Set the text attributes */
1057 if (lpnmlvcd->clrTextBk != CLR_NONE)
1059 SetBkMode(hdc, OPAQUE);
1060 SetBkColor(hdc,lpnmlvcd->clrTextBk);
1063 SetBkMode(hdc, TRANSPARENT);
1064 SetTextColor(hdc, lpnmlvcd->clrText);
1067 static inline DWORD notify_postpaint (const LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
1069 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
1072 /* returns TRUE when repaint needed, FALSE otherwise */
1073 static BOOL notify_measureitem(LISTVIEW_INFO *infoPtr)
1075 MEASUREITEMSTRUCT mis;
1076 mis.CtlType = ODT_LISTVIEW;
1077 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1081 mis.itemHeight= infoPtr->nItemHeight;
1082 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
1083 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
1085 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
1091 /******** Item iterator functions **********************************/
1093 static RANGES ranges_create(int count);
1094 static void ranges_destroy(RANGES ranges);
1095 static BOOL ranges_add(RANGES ranges, RANGE range);
1096 static BOOL ranges_del(RANGES ranges, RANGE range);
1097 static void ranges_dump(RANGES ranges);
1099 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
1101 RANGE range = { nItem, nItem + 1 };
1103 return ranges_add(ranges, range);
1106 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
1108 RANGE range = { nItem, nItem + 1 };
1110 return ranges_del(ranges, range);
1114 * ITERATOR DOCUMENTATION
1116 * The iterator functions allow for easy, and convenient iteration
1117 * over items of interest in the list. Typically, you create a
1118 * iterator, use it, and destroy it, as such:
1121 * iterator_xxxitems(&i, ...);
1122 * while (iterator_{prev,next}(&i)
1124 * //code which uses i.nItem
1126 * iterator_destroy(&i);
1128 * where xxx is either: framed, or visible.
1129 * Note that it is important that the code destroys the iterator
1130 * after it's done with it, as the creation of the iterator may
1131 * allocate memory, which thus needs to be freed.
1133 * You can iterate both forwards, and backwards through the list,
1134 * by using iterator_next or iterator_prev respectively.
1136 * Lower numbered items are draw on top of higher number items in
1137 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1138 * items may overlap). So, to test items, you should use
1140 * which lists the items top to bottom (in Z-order).
1141 * For drawing items, you should use
1143 * which lists the items bottom to top (in Z-order).
1144 * If you keep iterating over the items after the end-of-items
1145 * marker (-1) is returned, the iterator will start from the
1146 * beginning. Typically, you don't need to test for -1,
1147 * because iterator_{next,prev} will return TRUE if more items
1148 * are to be iterated over, or FALSE otherwise.
1150 * Note: the iterator is defined to be bidirectional. That is,
1151 * any number of prev followed by any number of next, or
1152 * five versa, should leave the iterator at the same item:
1153 * prev * n, next * n = next * n, prev * n
1155 * The iterator has a notion of an out-of-order, special item,
1156 * which sits at the start of the list. This is used in
1157 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1158 * which needs to be first, as it may overlap other items.
1160 * The code is a bit messy because we have:
1161 * - a special item to deal with
1162 * - simple range, or composite range
1164 * If you find bugs, or want to add features, please make sure you
1165 * always check/modify *both* iterator_prev, and iterator_next.
1169 * This function iterates through the items in increasing order,
1170 * but prefixed by the special item, then -1. That is:
1171 * special, 1, 2, 3, ..., n, -1.
1172 * Each item is listed only once.
1174 static inline BOOL iterator_next(ITERATOR* i)
1178 i->nItem = i->nSpecial;
1179 if (i->nItem != -1) return TRUE;
1181 if (i->nItem == i->nSpecial)
1183 if (i->ranges) i->index = 0;
1189 if (i->nItem == i->nSpecial) i->nItem++;
1190 if (i->nItem < i->range.upper) return TRUE;
1195 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1196 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1199 else if (i->nItem >= i->range.upper) goto end;
1201 i->nItem = i->range.lower;
1202 if (i->nItem >= 0) goto testitem;
1209 * This function iterates through the items in decreasing order,
1210 * followed by the special item, then -1. That is:
1211 * n, n-1, ..., 3, 2, 1, special, -1.
1212 * Each item is listed only once.
1214 static inline BOOL iterator_prev(ITERATOR* i)
1221 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1224 if (i->nItem == i->nSpecial)
1232 if (i->nItem == i->nSpecial) i->nItem--;
1233 if (i->nItem >= i->range.lower) return TRUE;
1239 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1242 else if (!start && i->nItem < i->range.lower) goto end;
1244 i->nItem = i->range.upper;
1245 if (i->nItem > 0) goto testitem;
1247 return (i->nItem = i->nSpecial) != -1;
1250 static RANGE iterator_range(const ITERATOR *i)
1254 if (!i->ranges) return i->range;
1256 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1258 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1259 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1261 else range.lower = range.upper = 0;
1267 * Releases resources associated with this ierator.
1269 static inline void iterator_destroy(const ITERATOR *i)
1271 ranges_destroy(i->ranges);
1275 * Create an empty iterator.
1277 static inline BOOL iterator_empty(ITERATOR* i)
1279 ZeroMemory(i, sizeof(*i));
1280 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1285 * Create an iterator over a range.
1287 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1295 * Create an iterator over a bunch of ranges.
1296 * Please note that the iterator will take ownership of the ranges,
1297 * and will free them upon destruction.
1299 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1307 * Creates an iterator over the items which intersect frame.
1308 * Uses absolute coordinates rather than compensating for the current offset.
1310 static BOOL iterator_frameditems_absolute(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *frame)
1312 RECT rcItem, rcTemp;
1314 /* in case we fail, we want to return an empty iterator */
1315 if (!iterator_empty(i)) return FALSE;
1317 TRACE("(frame=%s)\n", wine_dbgstr_rect(frame));
1319 if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)
1323 if (infoPtr->uView == LV_VIEW_ICON && infoPtr->nFocusedItem != -1)
1325 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1326 if (IntersectRect(&rcTemp, &rcItem, frame))
1327 i->nSpecial = infoPtr->nFocusedItem;
1329 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1330 /* to do better here, we need to have PosX, and PosY sorted */
1331 TRACE("building icon ranges:\n");
1332 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1334 rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1335 rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1336 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1337 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1338 if (IntersectRect(&rcTemp, &rcItem, frame))
1339 ranges_additem(i->ranges, nItem);
1343 else if (infoPtr->uView == LV_VIEW_DETAILS)
1347 if (frame->left >= infoPtr->nItemWidth) return TRUE;
1348 if (frame->top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1350 range.lower = max(frame->top / infoPtr->nItemHeight, 0);
1351 range.upper = min((frame->bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1352 if (range.upper <= range.lower) return TRUE;
1353 if (!iterator_rangeitems(i, range)) return FALSE;
1354 TRACE(" report=%s\n", debugrange(&i->range));
1358 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1359 INT nFirstRow = max(frame->top / infoPtr->nItemHeight, 0);
1360 INT nLastRow = min((frame->bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1367 if (infoPtr->nItemWidth)
1369 nFirstCol = max(frame->left / infoPtr->nItemWidth, 0);
1370 nLastCol = min((frame->right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1374 nFirstCol = max(frame->left, 0);
1375 nLastCol = min(frame->right - 1, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1378 lower = nFirstCol * nPerCol + nFirstRow;
1380 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1381 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1383 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1385 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1386 TRACE("building list ranges:\n");
1387 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1389 item_range.lower = nCol * nPerCol + nFirstRow;
1390 if(item_range.lower >= infoPtr->nItemCount) break;
1391 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1392 TRACE(" list=%s\n", debugrange(&item_range));
1393 ranges_add(i->ranges, item_range);
1401 * Creates an iterator over the items which intersect lprc.
1403 static BOOL iterator_frameditems(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *lprc)
1408 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc));
1410 LISTVIEW_GetOrigin(infoPtr, &Origin);
1411 OffsetRect(&frame, -Origin.x, -Origin.y);
1413 return iterator_frameditems_absolute(i, infoPtr, &frame);
1417 * Creates an iterator over the items which intersect the visible region of hdc.
1419 static BOOL iterator_visibleitems(ITERATOR *i, const LISTVIEW_INFO *infoPtr, HDC hdc)
1421 POINT Origin, Position;
1422 RECT rcItem, rcClip;
1425 rgntype = GetClipBox(hdc, &rcClip);
1426 if (rgntype == NULLREGION) return iterator_empty(i);
1427 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1428 if (rgntype == SIMPLEREGION) return TRUE;
1430 /* first deal with the special item */
1431 if (i->nSpecial != -1)
1433 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1434 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1437 /* if we can't deal with the region, we'll just go with the simple range */
1438 LISTVIEW_GetOrigin(infoPtr, &Origin);
1439 TRACE("building visible range:\n");
1440 if (!i->ranges && i->range.lower < i->range.upper)
1442 if (!(i->ranges = ranges_create(50))) return TRUE;
1443 if (!ranges_add(i->ranges, i->range))
1445 ranges_destroy(i->ranges);
1451 /* now delete the invisible items from the list */
1452 while(iterator_next(i))
1454 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1455 rcItem.left = (infoPtr->uView == LV_VIEW_DETAILS) ? Origin.x : Position.x + Origin.x;
1456 rcItem.top = Position.y + Origin.y;
1457 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1458 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1459 if (!RectVisible(hdc, &rcItem))
1460 ranges_delitem(i->ranges, i->nItem);
1462 /* the iterator should restart on the next iterator_next */
1468 /******** Misc helper functions ************************************/
1470 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1471 WPARAM wParam, LPARAM lParam, BOOL isW)
1473 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1474 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1477 static inline BOOL is_autoarrange(const LISTVIEW_INFO *infoPtr)
1479 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1480 (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON);
1483 static void toggle_checkbox_state(LISTVIEW_INFO *infoPtr, INT nItem)
1485 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
1486 if(state == 1 || state == 2)
1490 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
1491 lvitem.stateMask = LVIS_STATEIMAGEMASK;
1492 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
1496 /* this should be called after window style got updated,
1497 it used to reset view state to match current window style */
1498 static inline void map_style_view(LISTVIEW_INFO *infoPtr)
1500 switch (infoPtr->dwStyle & LVS_TYPEMASK)
1503 infoPtr->uView = LV_VIEW_ICON;
1506 infoPtr->uView = LV_VIEW_DETAILS;
1509 infoPtr->uView = LV_VIEW_SMALLICON;
1512 infoPtr->uView = LV_VIEW_LIST;
1516 /* computes next item id value */
1517 static DWORD get_next_itemid(const LISTVIEW_INFO *infoPtr)
1519 INT count = DPA_GetPtrCount(infoPtr->hdpaItemIds);
1523 ITEM_ID *lpID = DPA_GetPtr(infoPtr->hdpaItemIds, count - 1);
1524 return lpID->id + 1;
1529 /******** Internal API functions ************************************/
1531 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(const LISTVIEW_INFO *infoPtr, INT nSubItem)
1533 static COLUMN_INFO mainItem;
1535 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1536 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1538 /* update cached column rectangles */
1539 if (infoPtr->colRectsDirty)
1542 LISTVIEW_INFO *Ptr = (LISTVIEW_INFO*)infoPtr;
1545 for (i = 0; i < DPA_GetPtrCount(infoPtr->hdpaColumns); i++) {
1546 info = DPA_GetPtr(infoPtr->hdpaColumns, i);
1547 SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, i, (LPARAM)&info->rcHeader);
1549 Ptr->colRectsDirty = FALSE;
1552 return DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1555 static INT LISTVIEW_CreateHeader(LISTVIEW_INFO *infoPtr)
1557 DWORD dFlags = WS_CHILD | HDS_HORZ | HDS_FULLDRAG | HDS_DRAGDROP;
1560 if (infoPtr->hwndHeader) return 0;
1562 TRACE("Creating header for list %p\n", infoPtr->hwndSelf);
1564 /* setup creation flags */
1565 dFlags |= (LVS_NOSORTHEADER & infoPtr->dwStyle) ? 0 : HDS_BUTTONS;
1566 dFlags |= (LVS_NOCOLUMNHEADER & infoPtr->dwStyle) ? HDS_HIDDEN : 0;
1568 hInst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
1571 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL, dFlags,
1572 0, 0, 0, 0, infoPtr->hwndSelf, NULL, hInst, NULL);
1573 if (!infoPtr->hwndHeader) return -1;
1575 /* set header unicode format */
1576 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, TRUE, 0);
1578 /* set header font */
1579 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
1581 LISTVIEW_UpdateSize(infoPtr);
1586 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO *infoPtr, INT nSubItem, LPRECT lprc)
1588 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1591 static inline BOOL LISTVIEW_IsHeaderEnabled(const LISTVIEW_INFO *infoPtr)
1593 return (infoPtr->uView == LV_VIEW_DETAILS ||
1594 infoPtr->dwLvExStyle & LVS_EX_HEADERINALLVIEWS) &&
1595 !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER);
1598 static inline BOOL LISTVIEW_GetItemW(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1600 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1603 /* used to handle collapse main item column case */
1604 static inline BOOL LISTVIEW_DrawFocusRect(const LISTVIEW_INFO *infoPtr, HDC hdc)
1606 return (infoPtr->rcFocus.left < infoPtr->rcFocus.right) ?
1607 DrawFocusRect(hdc, &infoPtr->rcFocus) : FALSE;
1610 /* Listview invalidation functions: use _only_ these functions to invalidate */
1612 static inline BOOL is_redrawing(const LISTVIEW_INFO *infoPtr)
1614 return infoPtr->bRedraw;
1617 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO *infoPtr, const RECT* rect)
1619 if(!is_redrawing(infoPtr)) return;
1620 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect));
1621 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1624 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO *infoPtr, INT nItem)
1628 if(!is_redrawing(infoPtr)) return;
1629 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1630 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1633 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1635 POINT Origin, Position;
1638 if(!is_redrawing(infoPtr)) return;
1639 assert (infoPtr->uView == LV_VIEW_DETAILS);
1640 LISTVIEW_GetOrigin(infoPtr, &Origin);
1641 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1642 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1644 rcBox.bottom = infoPtr->nItemHeight;
1645 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1646 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1649 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO *infoPtr)
1651 LISTVIEW_InvalidateRect(infoPtr, NULL);
1654 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO *infoPtr, INT nColumn)
1658 if(!is_redrawing(infoPtr)) return;
1659 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1660 rcCol.top = infoPtr->rcList.top;
1661 rcCol.bottom = infoPtr->rcList.bottom;
1662 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1667 * Retrieves the number of items that can fit vertically in the client area.
1670 * [I] infoPtr : valid pointer to the listview structure
1673 * Number of items per row.
1675 static inline INT LISTVIEW_GetCountPerRow(const LISTVIEW_INFO *infoPtr)
1677 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1679 return max(nListWidth/(infoPtr->nItemWidth ? infoPtr->nItemWidth : 1), 1);
1684 * Retrieves the number of items that can fit horizontally in the client
1688 * [I] infoPtr : valid pointer to the listview structure
1691 * Number of items per column.
1693 static inline INT LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO *infoPtr)
1695 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1697 return max(nListHeight / infoPtr->nItemHeight, 1);
1701 /*************************************************************************
1702 * LISTVIEW_ProcessLetterKeys
1704 * Processes keyboard messages generated by pressing the letter keys
1706 * What this does is perform a case insensitive search from the
1707 * current position with the following quirks:
1708 * - If two chars or more are pressed in quick succession we search
1709 * for the corresponding string (e.g. 'abc').
1710 * - If there is a delay we wipe away the current search string and
1711 * restart with just that char.
1712 * - If the user keeps pressing the same character, whether slowly or
1713 * fast, so that the search string is entirely composed of this
1714 * character ('aaaaa' for instance), then we search for first item
1715 * that starting with that character.
1716 * - If the user types the above character in quick succession, then
1717 * we must also search for the corresponding string ('aaaaa'), and
1718 * go to that string if there is a match.
1721 * [I] hwnd : handle to the window
1722 * [I] charCode : the character code, the actual character
1723 * [I] keyData : key data
1731 * - The current implementation has a list of characters it will
1732 * accept and it ignores everything else. In particular it will
1733 * ignore accentuated characters which seems to match what
1734 * Windows does. But I'm not sure it makes sense to follow
1736 * - We don't sound a beep when the search fails.
1740 * TREEVIEW_ProcessLetterKeys
1742 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1747 WCHAR buffer[MAX_PATH];
1748 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1750 /* simple parameter checking */
1751 if (!charCode || !keyData) return 0;
1753 /* only allow the valid WM_CHARs through */
1754 if (!isalnumW(charCode) &&
1755 charCode != '.' && charCode != '`' && charCode != '!' &&
1756 charCode != '@' && charCode != '#' && charCode != '$' &&
1757 charCode != '%' && charCode != '^' && charCode != '&' &&
1758 charCode != '*' && charCode != '(' && charCode != ')' &&
1759 charCode != '-' && charCode != '_' && charCode != '+' &&
1760 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1761 charCode != '}' && charCode != '[' && charCode != '{' &&
1762 charCode != '/' && charCode != '?' && charCode != '>' &&
1763 charCode != '<' && charCode != ',' && charCode != '~')
1766 /* if there's one item or less, there is no where to go */
1767 if (infoPtr->nItemCount <= 1) return 0;
1769 /* update the search parameters */
1770 infoPtr->lastKeyPressTimestamp = GetTickCount();
1771 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1772 if (infoPtr->nSearchParamLength < MAX_PATH-1)
1773 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1774 if (infoPtr->charCode != charCode)
1775 infoPtr->charCode = charCode = 0;
1777 infoPtr->charCode=charCode;
1778 infoPtr->szSearchParam[0]=charCode;
1779 infoPtr->nSearchParamLength=1;
1780 /* Redundant with the 1 char string */
1784 /* and search from the current position */
1786 if (infoPtr->nFocusedItem >= 0) {
1787 endidx=infoPtr->nFocusedItem;
1789 /* if looking for single character match,
1790 * then we must always move forward
1792 if (infoPtr->nSearchParamLength == 1)
1795 endidx=infoPtr->nItemCount;
1799 /* Let application handle this for virtual listview */
1800 if (infoPtr->dwStyle & LVS_OWNERDATA)
1805 ZeroMemory(&lvfi, sizeof(lvfi));
1806 lvfi.flags = (LVFI_WRAP | LVFI_PARTIAL);
1807 infoPtr->szSearchParam[infoPtr->nSearchParamLength] = '\0';
1808 lvfi.psz = infoPtr->szSearchParam;
1812 nItem = notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
1815 LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1821 if (idx == infoPtr->nItemCount) {
1822 if (endidx == infoPtr->nItemCount || endidx == 0)
1828 item.mask = LVIF_TEXT;
1831 item.pszText = buffer;
1832 item.cchTextMax = MAX_PATH;
1833 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1835 /* check for a match */
1836 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1839 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1840 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1841 /* This would work but we must keep looking for a longer match */
1845 } while (idx != endidx);
1848 LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1853 /*************************************************************************
1854 * LISTVIEW_UpdateHeaderSize [Internal]
1856 * Function to resize the header control
1859 * [I] hwnd : handle to a window
1860 * [I] nNewScrollPos : scroll pos to set
1865 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1870 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1872 if (!infoPtr->hwndHeader) return;
1874 GetWindowRect(infoPtr->hwndHeader, &winRect);
1875 point[0].x = winRect.left;
1876 point[0].y = winRect.top;
1877 point[1].x = winRect.right;
1878 point[1].y = winRect.bottom;
1880 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1881 point[0].x = -nNewScrollPos;
1882 point[1].x += nNewScrollPos;
1884 SetWindowPos(infoPtr->hwndHeader,0,
1885 point[0].x,point[0].y,point[1].x,point[1].y,
1886 (infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW |
1887 SWP_NOZORDER | SWP_NOACTIVATE);
1892 * Update the scrollbars. This functions should be called whenever
1893 * the content, size or view changes.
1896 * [I] infoPtr : valid pointer to the listview structure
1901 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *infoPtr)
1903 SCROLLINFO horzInfo, vertInfo;
1906 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1908 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1909 horzInfo.cbSize = sizeof(SCROLLINFO);
1910 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1912 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1913 if (infoPtr->uView == LV_VIEW_LIST)
1915 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1916 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1918 /* scroll by at least one column per page */
1919 if(horzInfo.nPage < infoPtr->nItemWidth)
1920 horzInfo.nPage = infoPtr->nItemWidth;
1922 if (infoPtr->nItemWidth)
1923 horzInfo.nPage /= infoPtr->nItemWidth;
1925 else if (infoPtr->uView == LV_VIEW_DETAILS)
1927 horzInfo.nMax = infoPtr->nItemWidth;
1929 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
1933 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1936 if (LISTVIEW_IsHeaderEnabled(infoPtr))
1938 if (DPA_GetPtrCount(infoPtr->hdpaColumns))
1943 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
1944 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
1946 LISTVIEW_GetHeaderRect(infoPtr, index, &rcHeader);
1947 horzInfo.nMax = rcHeader.right;
1948 TRACE("horzInfo.nMax=%d\n", horzInfo.nMax);
1952 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1953 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1954 dx = GetScrollPos(infoPtr->hwndSelf, SB_HORZ);
1955 dx -= SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1956 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1958 /* Setting the horizontal scroll can change the listview size
1959 * (and potentially everything else) so we need to recompute
1960 * everything again for the vertical scroll
1963 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1964 vertInfo.cbSize = sizeof(SCROLLINFO);
1965 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1967 if (infoPtr->uView == LV_VIEW_DETAILS)
1969 vertInfo.nMax = infoPtr->nItemCount;
1971 /* scroll by at least one page */
1972 if(vertInfo.nPage < infoPtr->nItemHeight)
1973 vertInfo.nPage = infoPtr->nItemHeight;
1975 if (infoPtr->nItemHeight > 0)
1976 vertInfo.nPage /= infoPtr->nItemHeight;
1978 else if (infoPtr->uView != LV_VIEW_LIST) /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
1982 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1985 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1986 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1987 dy = GetScrollPos(infoPtr->hwndSelf, SB_VERT);
1988 dy -= SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1989 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1991 /* Change of the range may have changed the scroll pos. If so move the content */
1992 if (dx != 0 || dy != 0)
1995 listRect = infoPtr->rcList;
1996 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &listRect, &listRect, 0, 0,
1997 SW_ERASE | SW_INVALIDATE);
2000 /* Update the Header Control */
2001 if (infoPtr->hwndHeader)
2003 horzInfo.fMask = SIF_POS;
2004 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
2005 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
2012 * Shows/hides the focus rectangle.
2015 * [I] infoPtr : valid pointer to the listview structure
2016 * [I] fShow : TRUE to show the focus, FALSE to hide it.
2021 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO *infoPtr, BOOL fShow)
2025 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
2027 if (infoPtr->nFocusedItem < 0) return;
2029 /* we need some gymnastics in ICON mode to handle large items */
2030 if (infoPtr->uView == LV_VIEW_ICON)
2034 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
2035 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
2037 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
2042 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
2044 /* for some reason, owner draw should work only in report mode */
2045 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
2050 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2051 HFONT hOldFont = SelectObject(hdc, hFont);
2053 item.iItem = infoPtr->nFocusedItem;
2055 item.mask = LVIF_PARAM;
2056 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
2058 ZeroMemory(&dis, sizeof(dis));
2059 dis.CtlType = ODT_LISTVIEW;
2060 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
2061 dis.itemID = item.iItem;
2062 dis.itemAction = ODA_FOCUS;
2063 if (fShow) dis.itemState |= ODS_FOCUS;
2064 dis.hwndItem = infoPtr->hwndSelf;
2066 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
2067 dis.itemData = item.lParam;
2069 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
2071 SelectObject(hdc, hOldFont);
2075 LISTVIEW_DrawFocusRect(infoPtr, hdc);
2078 ReleaseDC(infoPtr->hwndSelf, hdc);
2082 * Invalidates all visible selected items.
2084 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO *infoPtr)
2088 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
2089 while(iterator_next(&i))
2091 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
2092 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
2094 iterator_destroy(&i);
2099 * DESCRIPTION: [INTERNAL]
2100 * Computes an item's (left,top) corner, relative to rcView.
2101 * That is, the position has NOT been made relative to the Origin.
2102 * This is deliberate, to avoid computing the Origin over, and
2103 * over again, when this function is called in a loop. Instead,
2104 * one can factor the computation of the Origin before the loop,
2105 * and offset the value returned by this function, on every iteration.
2108 * [I] infoPtr : valid pointer to the listview structure
2109 * [I] nItem : item number
2110 * [O] lpptOrig : item top, left corner
2115 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
2117 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
2119 if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
2121 lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2122 lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2124 else if (infoPtr->uView == LV_VIEW_LIST)
2126 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
2127 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
2128 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
2130 else /* LV_VIEW_DETAILS */
2132 lpptPosition->x = REPORT_MARGINX;
2133 /* item is always at zero indexed column */
2134 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2135 lpptPosition->x += LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left;
2136 lpptPosition->y = nItem * infoPtr->nItemHeight;
2141 * DESCRIPTION: [INTERNAL]
2142 * Compute the rectangles of an item. This is to localize all
2143 * the computations in one place. If you are not interested in some
2144 * of these values, simply pass in a NULL -- the function is smart
2145 * enough to compute only what's necessary. The function computes
2146 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
2147 * one, the BOX rectangle. This rectangle is very cheap to compute,
2148 * and is guaranteed to contain all the other rectangles. Computing
2149 * the ICON rect is also cheap, but all the others are potentially
2150 * expensive. This gives an easy and effective optimization when
2151 * searching (like point inclusion, or rectangle intersection):
2152 * first test against the BOX, and if TRUE, test against the desired
2154 * If the function does not have all the necessary information
2155 * to computed the requested rectangles, will crash with a
2156 * failed assertion. This is done so we catch all programming
2157 * errors, given that the function is called only from our code.
2159 * We have the following 'special' meanings for a few fields:
2160 * * If LVIS_FOCUSED is set, we assume the item has the focus
2161 * This is important in ICON mode, where it might get a larger
2162 * then usual rectangle
2164 * Please note that subitem support works only in REPORT mode.
2167 * [I] infoPtr : valid pointer to the listview structure
2168 * [I] lpLVItem : item to compute the measures for
2169 * [O] lprcBox : ptr to Box rectangle
2170 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
2171 * [0] lprcSelectBox : ptr to select box rectangle
2172 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
2173 * [O] lprcIcon : ptr to Icon rectangle
2174 * Same as LVM_GETITEMRECT with LVIR_ICON
2175 * [O] lprcStateIcon: ptr to State Icon rectangle
2176 * [O] lprcLabel : ptr to Label rectangle
2177 * Same as LVM_GETITEMRECT with LVIR_LABEL
2182 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
2183 LPRECT lprcBox, LPRECT lprcSelectBox,
2184 LPRECT lprcIcon, LPRECT lprcStateIcon, LPRECT lprcLabel)
2186 BOOL doSelectBox = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
2187 RECT Box, SelectBox, Icon, Label;
2188 COLUMN_INFO *lpColumnInfo = NULL;
2189 SIZE labelSize = { 0, 0 };
2191 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
2193 /* Be smart and try to figure out the minimum we have to do */
2194 if (lpLVItem->iSubItem) assert(infoPtr->uView == LV_VIEW_DETAILS);
2195 if (infoPtr->uView == LV_VIEW_ICON && (lprcBox || lprcLabel))
2197 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
2198 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
2200 if (lprcSelectBox) doSelectBox = TRUE;
2201 if (lprcLabel) doLabel = TRUE;
2202 if (doLabel || lprcIcon || lprcStateIcon) doIcon = TRUE;
2209 /************************************************************/
2210 /* compute the box rectangle (it should be cheap to do) */
2211 /************************************************************/
2212 if (lpLVItem->iSubItem || infoPtr->uView == LV_VIEW_DETAILS)
2213 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
2215 if (lpLVItem->iSubItem)
2217 Box = lpColumnInfo->rcHeader;
2222 Box.right = infoPtr->nItemWidth;
2225 Box.bottom = infoPtr->nItemHeight;
2227 /******************************************************************/
2228 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
2229 /******************************************************************/
2232 LONG state_width = 0;
2234 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
2235 state_width = infoPtr->iconStateSize.cx;
2237 if (infoPtr->uView == LV_VIEW_ICON)
2239 Icon.left = Box.left + state_width;
2240 if (infoPtr->himlNormal)
2241 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx - state_width) / 2;
2242 Icon.top = Box.top + ICON_TOP_PADDING;
2243 Icon.right = Icon.left;
2244 Icon.bottom = Icon.top;
2245 if (infoPtr->himlNormal)
2247 Icon.right += infoPtr->iconSize.cx;
2248 Icon.bottom += infoPtr->iconSize.cy;
2251 else /* LV_VIEW_SMALLICON, LV_VIEW_LIST or LV_VIEW_DETAILS */
2253 Icon.left = Box.left + state_width;
2255 if (infoPtr->uView == LV_VIEW_DETAILS && lpLVItem->iSubItem == 0)
2257 /* we need the indent in report mode */
2258 assert(lpLVItem->mask & LVIF_INDENT);
2259 Icon.left += infoPtr->iconSize.cx * lpLVItem->iIndent + REPORT_MARGINX;
2263 Icon.right = Icon.left;
2264 if (infoPtr->himlSmall &&
2265 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
2266 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
2267 Icon.right += infoPtr->iconSize.cx;
2268 Icon.bottom = Icon.top + infoPtr->iconSize.cy;
2270 if(lprcIcon) *lprcIcon = Icon;
2271 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon));
2273 /* TODO: is this correct? */
2276 lprcStateIcon->left = Icon.left - state_width;
2277 lprcStateIcon->right = Icon.left;
2278 lprcStateIcon->top = Icon.top;
2279 lprcStateIcon->bottom = lprcStateIcon->top + infoPtr->iconSize.cy;
2280 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon));
2283 else Icon.right = 0;
2285 /************************************************************/
2286 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2287 /************************************************************/
2290 /* calculate how far to the right can the label stretch */
2291 Label.right = Box.right;
2292 if (infoPtr->uView == LV_VIEW_DETAILS)
2294 if (lpLVItem->iSubItem == 0)
2296 /* we need a zero based rect here */
2297 Label = lpColumnInfo->rcHeader;
2298 OffsetRect(&Label, -Label.left, 0);
2302 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && infoPtr->uView == LV_VIEW_DETAILS))
2304 labelSize.cx = infoPtr->nItemWidth;
2305 labelSize.cy = infoPtr->nItemHeight;
2309 /* we need the text in non owner draw mode */
2310 assert(lpLVItem->mask & LVIF_TEXT);
2311 if (is_textT(lpLVItem->pszText, TRUE))
2313 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2314 HDC hdc = GetDC(infoPtr->hwndSelf);
2315 HFONT hOldFont = SelectObject(hdc, hFont);
2319 /* compute rough rectangle where the label will go */
2320 SetRectEmpty(&rcText);
2321 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2322 rcText.bottom = infoPtr->nItemHeight;
2323 if (infoPtr->uView == LV_VIEW_ICON)
2324 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2326 /* now figure out the flags */
2327 if (infoPtr->uView == LV_VIEW_ICON)
2328 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2330 uFormat = LV_SL_DT_FLAGS;
2332 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2334 if (rcText.right != rcText.left)
2335 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2337 labelSize.cy = rcText.bottom - rcText.top;
2339 SelectObject(hdc, hOldFont);
2340 ReleaseDC(infoPtr->hwndSelf, hdc);
2344 if (infoPtr->uView == LV_VIEW_ICON)
2346 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2347 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2348 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2349 Label.right = Label.left + labelSize.cx;
2350 Label.bottom = Label.top + infoPtr->nItemHeight;
2351 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2353 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2354 labelSize.cy /= infoPtr->ntmHeight;
2355 labelSize.cy = max(labelSize.cy, 1);
2356 labelSize.cy *= infoPtr->ntmHeight;
2358 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2360 else if (infoPtr->uView == LV_VIEW_DETAILS)
2362 Label.left = Icon.right;
2363 Label.top = Box.top;
2364 Label.right = lpLVItem->iSubItem ? lpColumnInfo->rcHeader.right :
2365 lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
2366 Label.bottom = Label.top + infoPtr->nItemHeight;
2368 else /* LV_VIEW_SMALLICON or LV_VIEW_LIST */
2370 Label.left = Icon.right;
2371 Label.top = Box.top;
2372 Label.right = min(Label.left + labelSize.cx, Label.right);
2373 Label.bottom = Label.top + infoPtr->nItemHeight;
2376 if (lprcLabel) *lprcLabel = Label;
2377 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label));
2380 /************************************************************/
2381 /* compute SELECT bounding box */
2382 /************************************************************/
2385 if (infoPtr->uView == LV_VIEW_DETAILS)
2387 SelectBox.left = Icon.left;
2388 SelectBox.top = Box.top;
2389 SelectBox.bottom = Box.bottom;
2392 SelectBox.right = min(Label.left + labelSize.cx, Label.right);
2394 SelectBox.right = min(Label.left + MAX_EMPTYTEXT_SELECT_WIDTH, Label.right);
2398 UnionRect(&SelectBox, &Icon, &Label);
2400 if (lprcSelectBox) *lprcSelectBox = SelectBox;
2401 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox));
2404 /* Fix the Box if necessary */
2407 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2408 else *lprcBox = Box;
2410 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box));
2414 * DESCRIPTION: [INTERNAL]
2417 * [I] infoPtr : valid pointer to the listview structure
2418 * [I] nItem : item number
2419 * [O] lprcBox : ptr to Box rectangle
2424 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2426 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2427 POINT Position, Origin;
2430 LISTVIEW_GetOrigin(infoPtr, &Origin);
2431 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2433 /* Be smart and try to figure out the minimum we have to do */
2435 if (infoPtr->uView == LV_VIEW_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2436 lvItem.mask |= LVIF_TEXT;
2437 lvItem.iItem = nItem;
2438 lvItem.iSubItem = 0;
2439 lvItem.pszText = szDispText;
2440 lvItem.cchTextMax = DISP_TEXT_SIZE;
2441 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2442 if (infoPtr->uView == LV_VIEW_ICON)
2444 lvItem.mask |= LVIF_STATE;
2445 lvItem.stateMask = LVIS_FOCUSED;
2446 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2448 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0, 0);
2450 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT &&
2451 SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 0, 0))
2453 OffsetRect(lprcBox, Origin.x, Position.y + Origin.y);
2456 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2459 /* LISTVIEW_MapIdToIndex helper */
2460 static INT CALLBACK MapIdSearchCompare(LPVOID p1, LPVOID p2, LPARAM lParam)
2462 ITEM_ID *id1 = (ITEM_ID*)p1;
2463 ITEM_ID *id2 = (ITEM_ID*)p2;
2465 if (id1->id == id2->id) return 0;
2467 return (id1->id < id2->id) ? -1 : 1;
2472 * Returns the item index for id specified.
2475 * [I] infoPtr : valid pointer to the listview structure
2476 * [I] iID : item id to get index for
2479 * Item index, or -1 on failure.
2481 static INT LISTVIEW_MapIdToIndex(const LISTVIEW_INFO *infoPtr, UINT iID)
2486 TRACE("iID=%d\n", iID);
2488 if (infoPtr->dwStyle & LVS_OWNERDATA) return -1;
2489 if (infoPtr->nItemCount == 0) return -1;
2492 index = DPA_Search(infoPtr->hdpaItemIds, &ID, -1, &MapIdSearchCompare, 0, DPAS_SORTED);
2496 ITEM_ID *lpID = DPA_GetPtr(infoPtr->hdpaItemIds, index);
2497 return DPA_GetPtrIndex(infoPtr->hdpaItems, lpID->item);
2505 * Returns the item id for index given.
2508 * [I] infoPtr : valid pointer to the listview structure
2509 * [I] iItem : item index to get id for
2514 static DWORD LISTVIEW_MapIndexToId(const LISTVIEW_INFO *infoPtr, INT iItem)
2519 TRACE("iItem=%d\n", iItem);
2521 if (infoPtr->dwStyle & LVS_OWNERDATA) return -1;
2522 if (iItem < 0 || iItem >= infoPtr->nItemCount) return -1;
2524 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, iItem);
2525 lpItem = DPA_GetPtr(hdpaSubItems, 0);
2527 return lpItem->id->id;
2532 * Returns the current icon position, and advances it along the top.
2533 * The returned position is not offset by Origin.
2536 * [I] infoPtr : valid pointer to the listview structure
2537 * [O] lpPos : will get the current icon position
2542 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2544 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2546 *lpPos = infoPtr->currIconPos;
2548 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2549 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2551 infoPtr->currIconPos.x = 0;
2552 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2558 * Returns the current icon position, and advances it down the left edge.
2559 * The returned position is not offset by Origin.
2562 * [I] infoPtr : valid pointer to the listview structure
2563 * [O] lpPos : will get the current icon position
2568 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2570 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2572 *lpPos = infoPtr->currIconPos;
2574 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2575 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2577 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2578 infoPtr->currIconPos.y = 0;
2584 * Moves an icon to the specified position.
2585 * It takes care of invalidating the item, etc.
2588 * [I] infoPtr : valid pointer to the listview structure
2589 * [I] nItem : the item to move
2590 * [I] lpPos : the new icon position
2591 * [I] isNew : flags the item as being new
2597 static BOOL LISTVIEW_MoveIconTo(const LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2603 old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2604 old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2606 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2607 LISTVIEW_InvalidateItem(infoPtr, nItem);
2610 /* Allocating a POINTER for every item is too resource intensive,
2611 * so we'll keep the (x,y) in different arrays */
2612 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2613 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2615 LISTVIEW_InvalidateItem(infoPtr, nItem);
2622 * Arranges listview items in icon display mode.
2625 * [I] infoPtr : valid pointer to the listview structure
2626 * [I] nAlignCode : alignment code
2632 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2634 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2638 if (infoPtr->uView != LV_VIEW_ICON && infoPtr->uView != LV_VIEW_SMALLICON) return FALSE;
2640 TRACE("nAlignCode=%d\n", nAlignCode);
2642 if (nAlignCode == LVA_DEFAULT)
2644 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2645 else nAlignCode = LVA_ALIGNTOP;
2650 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2651 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2652 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2653 default: return FALSE;
2656 infoPtr->bAutoarrange = TRUE;
2657 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2658 for (i = 0; i < infoPtr->nItemCount; i++)
2660 next_pos(infoPtr, &pos);
2661 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2669 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2670 * For LVS_REPORT always returns empty rectangle.
2673 * [I] infoPtr : valid pointer to the listview structure
2674 * [O] lprcView : bounding rectangle
2680 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2684 SetRectEmpty(lprcView);
2686 switch (infoPtr->uView)
2689 case LV_VIEW_SMALLICON:
2690 for (i = 0; i < infoPtr->nItemCount; i++)
2692 x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2693 y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2694 lprcView->right = max(lprcView->right, x);
2695 lprcView->bottom = max(lprcView->bottom, y);
2697 if (infoPtr->nItemCount > 0)
2699 lprcView->right += infoPtr->nItemWidth;
2700 lprcView->bottom += infoPtr->nItemHeight;
2705 y = LISTVIEW_GetCountPerColumn(infoPtr);
2706 x = infoPtr->nItemCount / y;
2707 if (infoPtr->nItemCount % y) x++;
2708 lprcView->right = x * infoPtr->nItemWidth;
2709 lprcView->bottom = y * infoPtr->nItemHeight;
2716 * Retrieves the bounding rectangle of all the items.
2719 * [I] infoPtr : valid pointer to the listview structure
2720 * [O] lprcView : bounding rectangle
2726 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2730 TRACE("(lprcView=%p)\n", lprcView);
2732 if (!lprcView) return FALSE;
2734 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2736 if (infoPtr->uView != LV_VIEW_DETAILS)
2738 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2739 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2742 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView));
2749 * Retrieves the subitem pointer associated with the subitem index.
2752 * [I] hdpaSubItems : DPA handle for a specific item
2753 * [I] nSubItem : index of subitem
2756 * SUCCESS : subitem pointer
2759 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2761 SUBITEM_INFO *lpSubItem;
2764 /* we should binary search here if need be */
2765 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2767 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
2768 if (lpSubItem->iSubItem == nSubItem)
2778 * Calculates the desired item width.
2781 * [I] infoPtr : valid pointer to the listview structure
2784 * The desired item width.
2786 static INT LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO *infoPtr)
2790 TRACE("uView=%d\n", infoPtr->uView);
2792 if (infoPtr->uView == LV_VIEW_ICON)
2793 nItemWidth = infoPtr->iconSpacing.cx;
2794 else if (infoPtr->uView == LV_VIEW_DETAILS)
2796 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2801 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
2802 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
2804 LISTVIEW_GetHeaderRect(infoPtr, index, &rcHeader);
2805 nItemWidth = rcHeader.right;
2808 else /* LV_VIEW_SMALLICON, or LV_VIEW_LIST */
2810 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2815 lvItem.mask = LVIF_TEXT;
2816 lvItem.iSubItem = 0;
2818 for (i = 0; i < infoPtr->nItemCount; i++)
2821 lvItem.pszText = szDispText;
2822 lvItem.cchTextMax = DISP_TEXT_SIZE;
2823 if (LISTVIEW_GetItemW(infoPtr, &lvItem))
2824 nItemWidth = max(LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE),
2827 empty = nItemWidth == 0;
2829 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2830 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2833 nItemWidth = max(nItemWidth, DEFAULT_COLUMN_WIDTH);
2835 nItemWidth += WIDTH_PADDING;
2843 * Calculates the desired item height.
2846 * [I] infoPtr : valid pointer to the listview structure
2849 * The desired item height.
2851 static INT LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO *infoPtr)
2855 TRACE("uView=%d\n", infoPtr->uView);
2857 if (infoPtr->uView == LV_VIEW_ICON)
2858 nItemHeight = infoPtr->iconSpacing.cy;
2861 nItemHeight = infoPtr->ntmHeight;
2862 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
2864 if (infoPtr->himlState)
2865 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2866 if (infoPtr->himlSmall)
2867 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2868 if (infoPtr->himlState || infoPtr->himlSmall)
2869 nItemHeight += HEIGHT_PADDING;
2870 if (infoPtr->nMeasureItemHeight > 0)
2871 nItemHeight = infoPtr->nMeasureItemHeight;
2874 return max(nItemHeight, 1);
2879 * Updates the width, and height of an item.
2882 * [I] infoPtr : valid pointer to the listview structure
2887 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2889 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2890 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2896 * Retrieves and saves important text metrics info for the current
2900 * [I] infoPtr : valid pointer to the listview structure
2903 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2905 HDC hdc = GetDC(infoPtr->hwndSelf);
2906 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2907 HFONT hOldFont = SelectObject(hdc, hFont);
2911 if (GetTextMetricsW(hdc, &tm))
2913 infoPtr->ntmHeight = tm.tmHeight;
2914 infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
2917 if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
2918 infoPtr->nEllipsisWidth = sz.cx;
2920 SelectObject(hdc, hOldFont);
2921 ReleaseDC(infoPtr->hwndSelf, hdc);
2923 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2928 * A compare function for ranges
2931 * [I] range1 : pointer to range 1;
2932 * [I] range2 : pointer to range 2;
2936 * > 0 : if range 1 > range 2
2937 * < 0 : if range 2 > range 1
2938 * = 0 : if range intersects range 2
2940 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2944 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2946 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2951 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1), debugrange(range2), cmp);
2957 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2959 #define ranges_check(ranges, desc) do { } while(0)
2962 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2967 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2969 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2970 ranges_dump(ranges);
2971 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2973 prev = DPA_GetPtr(ranges->hdpa, 0);
2974 assert (prev->lower >= 0 && prev->lower < prev->upper);
2975 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2977 curr = DPA_GetPtr(ranges->hdpa, i);
2978 assert (prev->upper <= curr->lower);
2979 assert (curr->lower < curr->upper);
2983 TRACE("--- Done checking---\n");
2986 static RANGES ranges_create(int count)
2988 RANGES ranges = Alloc(sizeof(struct tagRANGES));
2989 if (!ranges) return NULL;
2990 ranges->hdpa = DPA_Create(count);
2991 if (ranges->hdpa) return ranges;
2996 static void ranges_clear(RANGES ranges)
3000 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
3001 Free(DPA_GetPtr(ranges->hdpa, i));
3002 DPA_DeleteAllPtrs(ranges->hdpa);
3006 static void ranges_destroy(RANGES ranges)
3008 if (!ranges) return;
3009 ranges_clear(ranges);
3010 DPA_Destroy(ranges->hdpa);
3014 static RANGES ranges_clone(RANGES ranges)
3019 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
3021 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
3023 RANGE *newrng = Alloc(sizeof(RANGE));
3024 if (!newrng) goto fail;
3025 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
3026 DPA_SetPtr(clone->hdpa, i, newrng);
3031 TRACE ("clone failed\n");
3032 ranges_destroy(clone);
3036 static RANGES ranges_diff(RANGES ranges, RANGES sub)
3040 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
3041 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
3046 static void ranges_dump(RANGES ranges)
3050 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
3051 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
3054 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
3056 RANGE srchrng = { nItem, nItem + 1 };
3058 TRACE("(nItem=%d)\n", nItem);
3059 ranges_check(ranges, "before contain");
3060 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
3063 static INT ranges_itemcount(RANGES ranges)
3067 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
3069 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
3070 count += sel->upper - sel->lower;
3076 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
3078 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
3081 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
3082 if (index == -1) return TRUE;
3084 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
3086 chkrng = DPA_GetPtr(ranges->hdpa, index);
3087 if (chkrng->lower >= nItem)
3088 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
3089 if (chkrng->upper > nItem)
3090 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
3095 static BOOL ranges_add(RANGES ranges, RANGE range)
3100 TRACE("(%s)\n", debugrange(&range));
3101 ranges_check(ranges, "before add");
3103 /* try find overlapping regions first */
3104 srchrgn.lower = range.lower - 1;
3105 srchrgn.upper = range.upper + 1;
3106 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
3112 TRACE("Adding new range\n");
3114 /* create the brand new range to insert */
3115 newrgn = Alloc(sizeof(RANGE));
3116 if(!newrgn) goto fail;
3119 /* figure out where to insert it */
3120 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
3121 TRACE("index=%d\n", index);
3122 if (index == -1) index = 0;
3124 /* and get it over with */
3125 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
3133 RANGE *chkrgn, *mrgrgn;
3134 INT fromindex, mergeindex;
3136 chkrgn = DPA_GetPtr(ranges->hdpa, index);
3137 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
3139 chkrgn->lower = min(range.lower, chkrgn->lower);
3140 chkrgn->upper = max(range.upper, chkrgn->upper);
3142 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
3144 /* merge now common ranges */
3146 srchrgn.lower = chkrgn->lower - 1;
3147 srchrgn.upper = chkrgn->upper + 1;
3151 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
3152 if (mergeindex == -1) break;
3153 if (mergeindex == index)
3155 fromindex = index + 1;
3159 TRACE("Merge with index %i\n", mergeindex);
3161 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
3162 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
3163 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
3165 DPA_DeletePtr(ranges->hdpa, mergeindex);
3166 if (mergeindex < index) index --;
3170 ranges_check(ranges, "after add");
3174 ranges_check(ranges, "failed add");
3178 static BOOL ranges_del(RANGES ranges, RANGE range)
3183 TRACE("(%s)\n", debugrange(&range));
3184 ranges_check(ranges, "before del");
3186 /* we don't use DPAS_SORTED here, since we need *
3187 * to find the first overlapping range */
3188 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
3191 chkrgn = DPA_GetPtr(ranges->hdpa, index);
3193 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
3195 /* case 1: Same range */
3196 if ( (chkrgn->upper == range.upper) &&
3197 (chkrgn->lower == range.lower) )
3199 DPA_DeletePtr(ranges->hdpa, index);
3202 /* case 2: engulf */
3203 else if ( (chkrgn->upper <= range.upper) &&
3204 (chkrgn->lower >= range.lower) )
3206 DPA_DeletePtr(ranges->hdpa, index);
3208 /* case 3: overlap upper */
3209 else if ( (chkrgn->upper <= range.upper) &&
3210 (chkrgn->lower < range.lower) )
3212 chkrgn->upper = range.lower;
3214 /* case 4: overlap lower */
3215 else if ( (chkrgn->upper > range.upper) &&
3216 (chkrgn->lower >= range.lower) )
3218 chkrgn->lower = range.upper;
3221 /* case 5: fully internal */
3224 RANGE tmprgn = *chkrgn, *newrgn;
3226 if (!(newrgn = Alloc(sizeof(RANGE)))) goto fail;
3227 newrgn->lower = chkrgn->lower;
3228 newrgn->upper = range.lower;
3229 chkrgn->lower = range.upper;
3230 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
3239 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
3242 ranges_check(ranges, "after del");
3246 ranges_check(ranges, "failed del");
3252 * Removes all selection ranges
3255 * [I] infoPtr : valid pointer to the listview structure
3256 * [I] toSkip : item range to skip removing the selection
3262 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
3271 lvItem.stateMask = LVIS_SELECTED;
3273 /* need to clone the DPA because callbacks can change it */
3274 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
3275 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
3276 while(iterator_next(&i))
3277 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
3278 /* note that the iterator destructor will free the cloned range */
3279 iterator_destroy(&i);
3284 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
3288 if (!(toSkip = ranges_create(1))) return FALSE;
3289 if (nItem != -1) ranges_additem(toSkip, nItem);
3290 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
3291 ranges_destroy(toSkip);
3295 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
3297 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
3302 * Retrieves the number of items that are marked as selected.
3305 * [I] infoPtr : valid pointer to the listview structure
3308 * Number of items selected.
3310 static INT LISTVIEW_GetSelectedCount(const LISTVIEW_INFO *infoPtr)
3312 INT nSelectedCount = 0;
3314 if (infoPtr->uCallbackMask & LVIS_SELECTED)
3317 for (i = 0; i < infoPtr->nItemCount; i++)
3319 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
3324 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
3326 TRACE("nSelectedCount=%d\n", nSelectedCount);
3327 return nSelectedCount;
3332 * Manages the item focus.
3335 * [I] infoPtr : valid pointer to the listview structure
3336 * [I] nItem : item index
3339 * TRUE : focused item changed
3340 * FALSE : focused item has NOT changed
3342 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
3344 INT oldFocus = infoPtr->nFocusedItem;
3347 if (nItem == infoPtr->nFocusedItem) return FALSE;
3349 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
3350 lvItem.stateMask = LVIS_FOCUSED;
3351 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
3353 return oldFocus != infoPtr->nFocusedItem;
3356 /* Helper function for LISTVIEW_ShiftIndices *only* */
3357 static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
3359 if (nShiftItem < nItem) return nShiftItem;
3361 if (nShiftItem > nItem) return nShiftItem + direction;
3363 if (direction > 0) return nShiftItem + direction;
3365 return min(nShiftItem, infoPtr->nItemCount - 1);
3370 * Updates the various indices after an item has been inserted or deleted.
3373 * [I] infoPtr : valid pointer to the listview structure
3374 * [I] nItem : item index
3375 * [I] direction : Direction of shift, +1 or -1.
3380 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
3385 /* temporarily disable change notification while shifting items */
3386 bOldChange = infoPtr->bDoChangeNotify;
3387 infoPtr->bDoChangeNotify = FALSE;
3389 TRACE("Shifting %iu, %i steps\n", nItem, direction);
3391 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
3393 assert(abs(direction) == 1);
3395 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
3397 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
3398 if (nNewFocus != infoPtr->nFocusedItem)
3399 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
3401 /* But we are not supposed to modify nHotItem! */
3403 infoPtr->bDoChangeNotify = bOldChange;
3409 * Adds a block of selections.
3412 * [I] infoPtr : valid pointer to the listview structure
3413 * [I] nItem : item index
3416 * Whether the window is still valid.
3418 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3420 INT nFirst = min(infoPtr->nSelectionMark, nItem);
3421 INT nLast = max(infoPtr->nSelectionMark, nItem);
3422 HWND hwndSelf = infoPtr->hwndSelf;
3423 NMLVODSTATECHANGE nmlv;
3428 /* Temporarily disable change notification
3429 * If the control is LVS_OWNERDATA, we need to send
3430 * only one LVN_ODSTATECHANGED notification.
3431 * See MSDN documentation for LVN_ITEMCHANGED.
3433 bOldChange = infoPtr->bDoChangeNotify;
3434 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3436 if (nFirst == -1) nFirst = nItem;
3438 item.state = LVIS_SELECTED;
3439 item.stateMask = LVIS_SELECTED;
3441 for (i = nFirst; i <= nLast; i++)
3442 LISTVIEW_SetItemState(infoPtr,i,&item);
3444 ZeroMemory(&nmlv, sizeof(nmlv));
3445 nmlv.iFrom = nFirst;
3448 nmlv.uOldState = item.state;
3450 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3451 if (!IsWindow(hwndSelf))
3453 infoPtr->bDoChangeNotify = bOldChange;
3460 * Sets a single group selection.
3463 * [I] infoPtr : valid pointer to the listview structure
3464 * [I] nItem : item index
3469 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3476 if (!(selection = ranges_create(100))) return;
3478 item.state = LVIS_SELECTED;
3479 item.stateMask = LVIS_SELECTED;
3481 if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
3483 if (infoPtr->nSelectionMark == -1)
3485 infoPtr->nSelectionMark = nItem;
3486 ranges_additem(selection, nItem);
3492 sel.lower = min(infoPtr->nSelectionMark, nItem);
3493 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3494 ranges_add(selection, sel);
3499 RECT rcItem, rcSel, rcSelMark;
3502 rcItem.left = LVIR_BOUNDS;
3503 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3504 rcSelMark.left = LVIR_BOUNDS;
3505 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3506 UnionRect(&rcSel, &rcItem, &rcSelMark);
3507 iterator_frameditems(&i, infoPtr, &rcSel);
3508 while(iterator_next(&i))
3510 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3511 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3513 iterator_destroy(&i);
3516 /* disable per item notifications on LVS_OWNERDATA style
3517 FIXME: single LVN_ODSTATECHANGED should be used */
3518 bOldChange = infoPtr->bDoChangeNotify;
3519 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3521 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3524 iterator_rangesitems(&i, selection);
3525 while(iterator_next(&i))
3526 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3527 /* this will also destroy the selection */
3528 iterator_destroy(&i);
3530 infoPtr->bDoChangeNotify = bOldChange;
3532 LISTVIEW_SetItemFocus(infoPtr, nItem);
3537 * Sets a single selection.
3540 * [I] infoPtr : valid pointer to the listview structure
3541 * [I] nItem : item index
3546 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3550 TRACE("nItem=%d\n", nItem);
3552 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3554 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3555 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3556 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3558 infoPtr->nSelectionMark = nItem;
3563 * Set selection(s) with keyboard.
3566 * [I] infoPtr : valid pointer to the listview structure
3567 * [I] nItem : item index
3568 * [I] space : VK_SPACE code sent
3571 * SUCCESS : TRUE (needs to be repainted)
3572 * FAILURE : FALSE (nothing has changed)
3574 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem, BOOL space)
3576 /* FIXME: pass in the state */
3577 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3578 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3579 BOOL bResult = FALSE;
3581 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3582 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3586 if (infoPtr->dwStyle & LVS_SINGLESEL || (wShift == 0 && wCtrl == 0))
3587 LISTVIEW_SetSelection(infoPtr, nItem);
3591 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3595 lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3596 lvItem.stateMask = LVIS_SELECTED;
3599 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3600 if (lvItem.state & LVIS_SELECTED)
3601 infoPtr->nSelectionMark = nItem;
3603 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3606 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3609 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3613 static BOOL LISTVIEW_GetItemAtPt(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3615 LVHITTESTINFO lvHitTestInfo;
3617 ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3618 lvHitTestInfo.pt.x = pt.x;
3619 lvHitTestInfo.pt.y = pt.y;
3621 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3623 lpLVItem->mask = LVIF_PARAM;
3624 lpLVItem->iItem = lvHitTestInfo.iItem;
3625 lpLVItem->iSubItem = 0;
3627 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3630 static inline BOOL LISTVIEW_isHotTracking(const LISTVIEW_INFO *infoPtr)
3632 return ((infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) ||
3633 (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE) ||
3634 (infoPtr->dwLvExStyle & LVS_EX_TWOCLICKACTIVATE));
3639 * Called when the mouse is being actively tracked and has hovered for a specified
3643 * [I] infoPtr : valid pointer to the listview structure
3644 * [I] fwKeys : key indicator
3645 * [I] x,y : mouse position
3648 * 0 if the message was processed, non-zero if there was an error
3651 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3652 * over the item for a certain period of time.
3655 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3657 if (LISTVIEW_isHotTracking(infoPtr))
3665 if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3666 LISTVIEW_SetSelection(infoPtr, item.iItem);
3672 #define SCROLL_LEFT 0x1
3673 #define SCROLL_RIGHT 0x2
3674 #define SCROLL_UP 0x4
3675 #define SCROLL_DOWN 0x8
3679 * Utility routine to draw and highlight items within a marquee selection rectangle.
3682 * [I] infoPtr : valid pointer to the listview structure
3683 * [I] coords_orig : original co-ordinates of the cursor
3684 * [I] coords_offs : offsetted coordinates of the cursor
3685 * [I] offset : offset amount
3686 * [I] scroll : Bitmask of which directions we should scroll, if at all
3691 static void LISTVIEW_MarqueeHighlight(LISTVIEW_INFO *infoPtr, LPPOINT coords_orig, LPPOINT coords_offs, LPPOINT offset, INT scroll)
3693 BOOL controlDown = FALSE;
3698 if (coords_offs->x > infoPtr->marqueeOrigin.x)
3700 rect.left = infoPtr->marqueeOrigin.x;
3701 rect.right = coords_offs->x;
3705 rect.left = coords_offs->x;
3706 rect.right = infoPtr->marqueeOrigin.x;
3709 if (coords_offs->y > infoPtr->marqueeOrigin.y)
3711 rect.top = infoPtr->marqueeOrigin.y;
3712 rect.bottom = coords_offs->y;
3716 rect.top = coords_offs->y;
3717 rect.bottom = infoPtr->marqueeOrigin.y;
3720 /* Cancel out the old marquee rectangle and draw the new one */
3721 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeDrawRect);
3723 /* Scroll by the appropriate distance if applicable - speed up scrolling as
3724 the cursor is further away */
3726 if ((scroll & SCROLL_LEFT) && (coords_orig->x <= 0))
3727 LISTVIEW_Scroll(infoPtr, coords_orig->x, 0);
3729 if ((scroll & SCROLL_RIGHT) && (coords_orig->x >= infoPtr->rcList.right))
3730 LISTVIEW_Scroll(infoPtr, (coords_orig->x - infoPtr->rcList.right), 0);
3732 if ((scroll & SCROLL_UP) && (coords_orig->y <= 0))
3733 LISTVIEW_Scroll(infoPtr, 0, coords_orig->y);
3735 if ((scroll & SCROLL_DOWN) && (coords_orig->y >= infoPtr->rcList.bottom))
3736 LISTVIEW_Scroll(infoPtr, 0, (coords_orig->y - infoPtr->rcList.bottom));
3738 /* Invert the items in the old marquee rectangle */
3739 iterator_frameditems_absolute(&i, infoPtr, &infoPtr->marqueeRect);
3741 while (iterator_next(&i))
3745 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED) == LVIS_SELECTED)
3748 item.state = LVIS_SELECTED;
3750 item.stateMask = LVIS_SELECTED;
3752 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3756 iterator_destroy(&i);
3758 CopyRect(&infoPtr->marqueeRect, &rect);
3760 CopyRect(&infoPtr->marqueeDrawRect, &rect);
3761 OffsetRect(&infoPtr->marqueeDrawRect, offset->x, offset->y);
3763 /* Iterate over the items within our marquee rectangle */
3764 iterator_frameditems_absolute(&i, infoPtr, &infoPtr->marqueeRect);
3766 if (GetKeyState(VK_CONTROL) & 0x8000)
3769 while (iterator_next(&i))
3773 /* If CTRL is pressed, invert. If not, always select the item. */
3774 if ((controlDown) && (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED)))
3777 item.state = LVIS_SELECTED;
3779 item.stateMask = LVIS_SELECTED;
3781 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3785 iterator_destroy(&i);
3786 LISTVIEW_InvalidateRect(infoPtr, &rect);
3791 * Called when we are in a marquee selection that involves scrolling the listview (ie,
3792 * the cursor is outside the bounds of the client area). This is a TIMERPROC.
3795 * [I] hwnd : Handle to the listview
3796 * [I] uMsg : WM_TIMER (ignored)
3797 * [I] idEvent : The timer ID interpreted as a pointer to a LISTVIEW_INFO struct
3798 * [I] dwTimer : The elapsed time (ignored)
3803 static VOID CALLBACK LISTVIEW_ScrollTimer(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
3805 LISTVIEW_INFO *infoPtr;
3806 SCROLLINFO scrollInfo;
3812 infoPtr = (LISTVIEW_INFO *) idEvent;
3817 /* Get the current cursor position and convert to client coordinates */
3818 GetCursorPos(&coords_orig);
3819 ScreenToClient(hWnd, &coords_orig);
3821 /* Ensure coordinates are within client bounds */
3822 coords_offs.x = max(min(coords_orig.x, infoPtr->rcList.right), 0);
3823 coords_offs.y = max(min(coords_orig.y, infoPtr->rcList.bottom), 0);
3826 LISTVIEW_GetOrigin(infoPtr, &offset);
3828 /* Offset coordinates by the appropriate amount */
3829 coords_offs.x -= offset.x;
3830 coords_offs.y -= offset.y;
3832 scrollInfo.cbSize = sizeof(SCROLLINFO);
3833 scrollInfo.fMask = SIF_ALL;
3835 /* Work out in which directions we can scroll */
3836 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3838 if (scrollInfo.nPos != scrollInfo.nMin)
3839 scroll |= SCROLL_UP;
3841 if (((scrollInfo.nPage + scrollInfo.nPos) - 1) != scrollInfo.nMax)
3842 scroll |= SCROLL_DOWN;
3845 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3847 if (scrollInfo.nPos != scrollInfo.nMin)
3848 scroll |= SCROLL_LEFT;
3850 if (((scrollInfo.nPage + scrollInfo.nPos) - 1) != scrollInfo.nMax)
3851 scroll |= SCROLL_RIGHT;
3854 if (((coords_orig.x <= 0) && (scroll & SCROLL_LEFT)) ||
3855 ((coords_orig.y <= 0) && (scroll & SCROLL_UP)) ||
3856 ((coords_orig.x >= infoPtr->rcList.right) && (scroll & SCROLL_RIGHT)) ||
3857 ((coords_orig.y >= infoPtr->rcList.bottom) && (scroll & SCROLL_DOWN)))
3859 LISTVIEW_MarqueeHighlight(infoPtr, &coords_orig, &coords_offs, &offset, scroll);
3865 * Called whenever WM_MOUSEMOVE is received.
3868 * [I] infoPtr : valid pointer to the listview structure
3869 * [I] fwKeys : key indicator
3870 * [I] x,y : mouse position
3873 * 0 if the message is processed, non-zero if there was an error
3875 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3877 TRACKMOUSEEVENT trackinfo;
3879 if (!(fwKeys & MK_LBUTTON))
3880 infoPtr->bLButtonDown = FALSE;
3882 if (infoPtr->bLButtonDown)
3886 LVHITTESTINFO lvHitTestInfo;
3887 WORD wDragWidth = GetSystemMetrics(SM_CXDRAG);
3888 WORD wDragHeight= GetSystemMetrics(SM_CYDRAG);
3890 if (infoPtr->bMarqueeSelect)
3900 LISTVIEW_GetOrigin(infoPtr, &offset);
3902 /* Ensure coordinates are within client bounds */
3903 coords_offs.x = max(min(x, infoPtr->rcList.right), 0);
3904 coords_offs.y = max(min(y, infoPtr->rcList.bottom), 0);
3906 /* Offset coordinates by the appropriate amount */
3907 coords_offs.x -= offset.x;
3908 coords_offs.y -= offset.y;
3910 /* Enable the timer if we're going outside our bounds, in case the user doesn't
3911 move the mouse again */
3913 if ((x <= 0) || (y <= 0) || (x >= infoPtr->rcList.right) ||
3914 (y >= infoPtr->rcList.bottom))
3916 if (!infoPtr->bScrolling)
3918 infoPtr->bScrolling = TRUE;
3919 SetTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr, 1, LISTVIEW_ScrollTimer);
3924 infoPtr->bScrolling = FALSE;
3925 KillTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr);
3928 LISTVIEW_MarqueeHighlight(infoPtr, &coords_orig, &coords_offs, &offset, 0);
3932 rect.left = infoPtr->ptClickPos.x - wDragWidth;
3933 rect.right = infoPtr->ptClickPos.x + wDragWidth;
3934 rect.top = infoPtr->ptClickPos.y - wDragHeight;
3935 rect.bottom = infoPtr->ptClickPos.y + wDragHeight;
3940 lvHitTestInfo.pt = tmp;
3941 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3943 /* reset item marker */
3944 if (infoPtr->nLButtonDownItem != lvHitTestInfo.iItem)
3945 infoPtr->nLButtonDownItem = -1;
3947 if (!PtInRect(&rect, tmp))
3949 /* this path covers the following:
3950 1. WM_LBUTTONDOWN over selected item (sets focus on it)
3951 2. change focus with keys
3952 3. move mouse over item from step 1 selects it and moves focus on it */
3953 if (infoPtr->nLButtonDownItem != -1 &&
3954 !LISTVIEW_GetItemState(infoPtr, infoPtr->nLButtonDownItem, LVIS_SELECTED))
3958 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3959 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3961 LISTVIEW_SetItemState(infoPtr, infoPtr->nLButtonDownItem, &lvItem);
3962 infoPtr->nLButtonDownItem = -1;
3965 if (!infoPtr->bDragging)
3967 lvHitTestInfo.pt = infoPtr->ptClickPos;
3968 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3970 /* If the click is outside the range of an item, begin a
3971 highlight. If not, begin an item drag. */
3972 if (lvHitTestInfo.iItem == -1)
3976 /* If we're allowing multiple selections, send notification.
3977 If return value is non-zero, cancel. */
3978 if (!(infoPtr->dwStyle & LVS_SINGLESEL) && (notify_hdr(infoPtr, LVN_MARQUEEBEGIN, &hdr) == 0))
3980 /* Store the absolute coordinates of the click */
3982 LISTVIEW_GetOrigin(infoPtr, &offset);
3984 infoPtr->marqueeOrigin.x = infoPtr->ptClickPos.x - offset.x;
3985 infoPtr->marqueeOrigin.y = infoPtr->ptClickPos.y - offset.y;
3987 /* Begin selection and capture mouse */
3988 infoPtr->bMarqueeSelect = TRUE;
3989 SetCapture(infoPtr->hwndSelf);
3996 ZeroMemory(&nmlv, sizeof(nmlv));
3997 nmlv.iItem = lvHitTestInfo.iItem;
3998 nmlv.ptAction = infoPtr->ptClickPos;
4000 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
4001 infoPtr->bDragging = TRUE;
4009 /* see if we are supposed to be tracking mouse hovering */
4010 if (LISTVIEW_isHotTracking(infoPtr)) {
4011 /* fill in the trackinfo struct */
4012 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
4013 trackinfo.dwFlags = TME_QUERY;
4014 trackinfo.hwndTrack = infoPtr->hwndSelf;
4015 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
4017 /* see if we are already tracking this hwnd */
4018 _TrackMouseEvent(&trackinfo);
4020 if(!(trackinfo.dwFlags & TME_HOVER)) {
4021 trackinfo.dwFlags = TME_HOVER;
4023 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
4024 _TrackMouseEvent(&trackinfo);
4033 * Tests whether the item is assignable to a list with style lStyle
4035 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
4037 if ( (lpLVItem->mask & LVIF_TEXT) &&
4038 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
4039 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
4047 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
4050 * [I] infoPtr : valid pointer to the listview structure
4051 * [I] lpLVItem : valid pointer to new item attributes
4052 * [I] isNew : the item being set is being inserted
4053 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4054 * [O] bChanged : will be set to TRUE if the item really changed
4060 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
4066 /* stateMask is ignored for LVM_INSERTITEM */
4067 UINT stateMask = isNew ? ~0 : lpLVItem->stateMask;
4071 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
4073 if (lpLVItem->mask == 0) return TRUE;
4075 if (infoPtr->dwStyle & LVS_OWNERDATA)
4077 /* a virtual listview only stores selection and focus */
4078 if (lpLVItem->mask & ~LVIF_STATE)
4084 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4085 lpItem = DPA_GetPtr(hdpaSubItems, 0);
4089 /* we need to get the lParam and state of the item */
4090 item.iItem = lpLVItem->iItem;
4091 item.iSubItem = lpLVItem->iSubItem;
4092 item.mask = LVIF_STATE | LVIF_PARAM;
4093 item.stateMask = (infoPtr->dwStyle & LVS_OWNERDATA) ? LVIS_FOCUSED | LVIS_SELECTED : ~0;
4097 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
4099 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
4100 /* determine what fields will change */
4101 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & stateMask & ~infoPtr->uCallbackMask))
4102 uChanged |= LVIF_STATE;
4104 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
4105 uChanged |= LVIF_IMAGE;
4107 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
4108 uChanged |= LVIF_PARAM;
4110 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
4111 uChanged |= LVIF_INDENT;
4113 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
4114 uChanged |= LVIF_TEXT;
4116 TRACE("uChanged=0x%x\n", uChanged);
4117 if (!uChanged) return TRUE;
4120 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4121 nmlv.iItem = lpLVItem->iItem;
4122 nmlv.uNewState = (item.state & ~stateMask) | (lpLVItem->state & stateMask);
4123 nmlv.uOldState = item.state;
4124 nmlv.uChanged = uChanged;
4125 nmlv.lParam = item.lParam;
4127 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
4128 /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
4130 if(lpItem && !isNew && infoPtr->bDoChangeNotify)
4132 HWND hwndSelf = infoPtr->hwndSelf;
4134 if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
4136 if (!IsWindow(hwndSelf))
4140 /* copy information */
4141 if (lpLVItem->mask & LVIF_TEXT)
4142 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
4144 if (lpLVItem->mask & LVIF_IMAGE)
4145 lpItem->hdr.iImage = lpLVItem->iImage;
4147 if (lpLVItem->mask & LVIF_PARAM)
4148 lpItem->lParam = lpLVItem->lParam;
4150 if (lpLVItem->mask & LVIF_INDENT)
4151 lpItem->iIndent = lpLVItem->iIndent;
4153 if (uChanged & LVIF_STATE)
4155 if (lpItem && (stateMask & ~infoPtr->uCallbackMask))
4157 lpItem->state &= ~stateMask;
4158 lpItem->state |= (lpLVItem->state & stateMask);
4160 if (lpLVItem->state & stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
4162 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
4163 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
4165 else if (stateMask & LVIS_SELECTED)
4167 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
4169 /* if we are asked to change focus, and we manage it, do it */
4170 if (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
4172 if (lpLVItem->state & LVIS_FOCUSED)
4174 if (infoPtr->nFocusedItem != -1)
4176 /* remove current focus */
4177 item.mask = LVIF_STATE;
4179 item.stateMask = LVIS_FOCUSED;
4181 /* recurse with redrawing an item */
4182 LISTVIEW_SetItemState(infoPtr, infoPtr->nFocusedItem, &item);
4185 infoPtr->nFocusedItem = lpLVItem->iItem;
4186 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, infoPtr->uView == LV_VIEW_LIST);
4188 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
4190 infoPtr->nFocusedItem = -1;
4195 /* if we're inserting the item, we're done */
4196 if (isNew) return TRUE;
4198 /* send LVN_ITEMCHANGED notification */
4199 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
4200 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
4207 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
4210 * [I] infoPtr : valid pointer to the listview structure
4211 * [I] lpLVItem : valid pointer to new subitem attributes
4212 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4213 * [O] bChanged : will be set to TRUE if the item really changed
4219 static BOOL set_sub_item(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
4222 SUBITEM_INFO *lpSubItem;
4224 /* we do not support subitems for virtual listviews */
4225 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
4227 /* set subitem only if column is present */
4228 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4230 /* First do some sanity checks */
4231 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
4232 particularly useful. We currently do not actually do anything with
4233 the flag on subitems.
4235 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)) return FALSE;
4236 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))) return TRUE;
4238 /* get the subitem structure, and create it if not there */
4239 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4240 assert (hdpaSubItems);
4242 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4245 SUBITEM_INFO *tmpSubItem;
4248 lpSubItem = Alloc(sizeof(SUBITEM_INFO));
4249 if (!lpSubItem) return FALSE;
4250 /* we could binary search here, if need be...*/
4251 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4253 tmpSubItem = DPA_GetPtr(hdpaSubItems, i);
4254 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
4256 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
4261 lpSubItem->iSubItem = lpLVItem->iSubItem;
4262 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
4266 if (lpLVItem->mask & LVIF_IMAGE)
4267 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
4269 lpSubItem->hdr.iImage = lpLVItem->iImage;
4273 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpSubItem->hdr.pszText, lpLVItem->pszText, isW))
4275 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
4284 * Sets item attributes.
4287 * [I] infoPtr : valid pointer to the listview structure
4288 * [I] lpLVItem : new item attributes
4289 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4295 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem, BOOL isW)
4297 HWND hwndSelf = infoPtr->hwndSelf;
4298 LPWSTR pszText = NULL;
4299 BOOL bResult, bChanged = FALSE;
4301 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4303 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4306 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
4307 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
4309 pszText = lpLVItem->pszText;
4310 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
4313 /* actually set the fields */
4314 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
4316 if (lpLVItem->iSubItem)
4317 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
4319 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
4320 if (!IsWindow(hwndSelf))
4323 /* redraw item, if necessary */
4324 if (bChanged && !infoPtr->bIsDrawing)
4326 /* this little optimization eliminates some nasty flicker */
4327 if ( infoPtr->uView == LV_VIEW_DETAILS && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
4328 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
4329 lpLVItem->iSubItem > 0 && lpLVItem->iSubItem <= DPA_GetPtrCount(infoPtr->hdpaColumns) )
4330 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
4332 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
4337 textfreeT(lpLVItem->pszText, isW);
4338 lpLVItem->pszText = pszText;
4346 * Retrieves the index of the item at coordinate (0, 0) of the client area.
4349 * [I] infoPtr : valid pointer to the listview structure
4354 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *infoPtr)
4357 SCROLLINFO scrollInfo;
4359 scrollInfo.cbSize = sizeof(SCROLLINFO);
4360 scrollInfo.fMask = SIF_POS;
4362 if (infoPtr->uView == LV_VIEW_LIST)
4364 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
4365 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
4367 else if (infoPtr->uView == LV_VIEW_DETAILS)
4369 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
4370 nItem = scrollInfo.nPos;
4374 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
4375 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
4378 TRACE("nItem=%d\n", nItem);
4386 * Erases the background of the given rectangle
4389 * [I] infoPtr : valid pointer to the listview structure
4390 * [I] hdc : device context handle
4391 * [I] lprcBox : clipping rectangle
4397 static inline BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
4399 if (!infoPtr->hBkBrush) return FALSE;
4401 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
4403 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
4411 * [I] infoPtr : valid pointer to the listview structure
4412 * [I] hdc : device context handle
4413 * [I] nItem : item index
4414 * [I] nSubItem : subitem index
4415 * [I] pos : item position in client coordinates
4416 * [I] cdmode : custom draw mode
4422 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
4425 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4426 static WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
4427 DWORD cdsubitemmode = CDRF_DODEFAULT;
4429 RECT rcSelect, rcBox, rcIcon, rcLabel, rcStateIcon;
4430 NMLVCUSTOMDRAW nmlvcd;
4435 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
4437 /* get information needed for drawing the item */
4438 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
4439 if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
4440 if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
4441 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK | LVIS_CUT;
4442 lvItem.iItem = nItem;
4443 lvItem.iSubItem = nSubItem;
4446 lvItem.cchTextMax = DISP_TEXT_SIZE;
4447 lvItem.pszText = szDispText;
4448 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
4449 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
4450 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
4451 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
4452 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
4454 /* now check if we need to update the focus rectangle */
4455 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
4457 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
4458 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
4459 OffsetRect(&rcBox, pos.x, pos.y);
4460 OffsetRect(&rcSelect, pos.x, pos.y);
4461 OffsetRect(&rcIcon, pos.x, pos.y);
4462 OffsetRect(&rcStateIcon, pos.x, pos.y);
4463 OffsetRect(&rcLabel, pos.x, pos.y);
4464 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
4465 wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect),
4466 wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
4468 /* fill in the custom draw structure */
4469 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
4471 hOldFont = GetCurrentObject(hdc, OBJ_FONT);
4472 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
4473 if (cdmode & CDRF_SKIPDEFAULT) goto postpaint;
4474 if (cdmode & CDRF_NOTIFYITEMDRAW)
4475 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4476 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
4477 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
4478 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
4479 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
4481 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
4482 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
4484 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
4485 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4486 else if ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) == FALSE)
4487 prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE);
4489 /* in full row select, subitems, will just use main item's colors */
4490 if (nSubItem && infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
4491 nmlvcd.clrTextBk = CLR_NONE;
4493 /* FIXME: temporary hack */
4494 rcSelect.left = rcLabel.left;
4496 /* draw the selection background, if we're drawing the main item */
4499 /* in icon mode, the label rect is really what we want to draw the
4501 if (infoPtr->uView == LV_VIEW_ICON)
4504 if (infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
4506 /* we have to update left focus bound too if item isn't in leftmost column
4507 and reduce right box bound */
4508 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
4512 if ((leftmost = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 0, 0)))
4514 INT Originx = pos.x - LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left;
4515 INT index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
4516 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
4518 rcBox.right = LISTVIEW_GetColumnInfo(infoPtr, index)->rcHeader.right + Originx;
4519 rcSelect.left = LISTVIEW_GetColumnInfo(infoPtr, leftmost)->rcHeader.left + Originx;
4523 rcSelect.right = rcBox.right;
4526 if (nmlvcd.clrTextBk != CLR_NONE)
4527 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, NULL, 0, NULL);
4528 /* store new focus rectangle */
4529 if (infoPtr->nFocusedItem == nItem) infoPtr->rcFocus = rcSelect;
4533 if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && (nSubItem == 0))
4535 UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
4538 TRACE("uStateImage=%d\n", uStateImage);
4539 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
4540 rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
4545 himl = (infoPtr->uView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
4546 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
4550 TRACE("iImage=%d\n", lvItem.iImage);
4552 if (lvItem.state & (LVIS_SELECTED | LVIS_CUT) && infoPtr->bFocus)
4553 style = ILD_SELECTED;
4557 ImageList_DrawEx(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
4558 rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk,
4559 lvItem.state & LVIS_CUT ? RGB(255, 255, 255) : CLR_DEFAULT,
4563 /* Don't bother painting item being edited */
4564 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
4566 /* figure out the text drawing flags */
4567 uFormat = (infoPtr->uView == LV_VIEW_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
4568 if (infoPtr->uView == LV_VIEW_ICON)
4569 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
4572 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
4574 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
4575 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
4576 default: uFormat |= DT_LEFT;
4579 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
4581 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
4582 else rcLabel.left += LABEL_HOR_PADDING;
4584 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
4586 /* for GRIDLINES reduce the bottom so the text formats correctly */
4587 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
4590 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
4593 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
4594 notify_postpaint(infoPtr, &nmlvcd);
4595 if (cdsubitemmode & CDRF_NEWFONT)
4596 SelectObject(hdc, hOldFont);
4602 * Draws listview items when in owner draw mode.
4605 * [I] infoPtr : valid pointer to the listview structure
4606 * [I] hdc : device context handle
4611 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4613 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
4614 DWORD cditemmode = CDRF_DODEFAULT;
4615 NMLVCUSTOMDRAW nmlvcd;
4616 POINT Origin, Position;
4622 ZeroMemory(&dis, sizeof(dis));
4624 /* Get scroll info once before loop */
4625 LISTVIEW_GetOrigin(infoPtr, &Origin);
4627 /* iterate through the invalidated rows */
4628 while(iterator_next(i))
4630 item.iItem = i->nItem;
4632 item.mask = LVIF_PARAM | LVIF_STATE;
4633 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4634 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
4636 dis.CtlType = ODT_LISTVIEW;
4638 dis.itemID = item.iItem;
4639 dis.itemAction = ODA_DRAWENTIRE;
4641 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
4642 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
4643 dis.hwndItem = infoPtr->hwndSelf;
4645 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
4646 dis.rcItem.left = Position.x + Origin.x;
4647 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
4648 dis.rcItem.top = Position.y + Origin.y;
4649 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
4650 dis.itemData = item.lParam;
4652 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
4655 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
4656 * structure for the rest. of the paint cycle
4658 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
4659 if (cdmode & CDRF_NOTIFYITEMDRAW)
4660 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4662 if (!(cditemmode & CDRF_SKIPDEFAULT))
4664 prepaint_setup (infoPtr, hdc, &nmlvcd, FALSE);
4665 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
4668 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
4669 notify_postpaint(infoPtr, &nmlvcd);
4675 * Draws listview items when in report display mode.
4678 * [I] infoPtr : valid pointer to the listview structure
4679 * [I] hdc : device context handle
4680 * [I] cdmode : custom draw mode
4685 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4688 RECT rcClip, rcItem;
4689 POINT Origin, Position;
4696 /* figure out what to draw */
4697 rgntype = GetClipBox(hdc, &rcClip);
4698 if (rgntype == NULLREGION) return;
4700 /* Get scroll info once before loop */
4701 LISTVIEW_GetOrigin(infoPtr, &Origin);
4703 colRanges = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns));
4705 /* narrow down the columns we need to paint */
4706 for(col = 0; col < DPA_GetPtrCount(infoPtr->hdpaColumns); col++)
4708 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, col, 0);
4710 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
4711 if ((rcItem.right + Origin.x >= rcClip.left) && (rcItem.left + Origin.x < rcClip.right))
4712 ranges_additem(colRanges, index);
4714 iterator_rangesitems(&j, colRanges);
4716 /* in full row select, we _have_ to draw the main item */
4717 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
4720 /* iterate through the invalidated rows */
4721 while(iterator_next(i))
4723 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4724 Position.y += Origin.y;
4726 /* iterate through the invalidated columns */
4727 while(iterator_next(&j))
4729 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4730 Position.x = (j.nItem == 0) ? rcItem.left + Origin.x : Origin.x;
4732 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
4735 rcItem.bottom = infoPtr->nItemHeight;
4736 OffsetRect(&rcItem, Origin.x, Position.y);
4737 if (!RectVisible(hdc, &rcItem)) continue;
4740 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
4743 iterator_destroy(&j);
4748 * Draws the gridlines if necessary when in report display mode.
4751 * [I] infoPtr : valid pointer to the listview structure
4752 * [I] hdc : device context handle
4757 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO *infoPtr, HDC hdc)
4763 RECT rcClip, rcItem = {0};
4771 /* figure out what to draw */
4772 rgntype = GetClipBox(hdc, &rcClip);
4773 if (rgntype == NULLREGION) return;
4775 /* Get scroll info once before loop */
4776 LISTVIEW_GetOrigin(infoPtr, &Origin);
4778 colRanges = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns));
4780 /* narrow down the columns we need to paint */
4781 for(col = 0; col < DPA_GetPtrCount(infoPtr->hdpaColumns); col++)
4783 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, col, 0);
4785 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
4786 if ((rcItem.right + Origin.x >= rcClip.left) && (rcItem.left + Origin.x < rcClip.right))
4787 ranges_additem(colRanges, index);
4790 /* is right most vertical line visible? */
4791 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
4793 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
4794 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
4795 rmost = (rcItem.right + Origin.x < rcClip.right);
4798 if ((hPen = CreatePen( PS_SOLID, 1, comctl32_color.clr3dFace )))
4800 hOldPen = SelectObject ( hdc, hPen );
4802 /* draw the vertical lines for the columns */
4803 iterator_rangesitems(&j, colRanges);
4804 while(iterator_next(&j))
4806 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4807 if (rcItem.left == 0) continue; /* skip leftmost column */
4808 rcItem.left += Origin.x;
4809 rcItem.right += Origin.x;
4810 rcItem.top = infoPtr->rcList.top;
4811 rcItem.bottom = infoPtr->rcList.bottom;
4812 TRACE("vert col=%d, rcItem=%s\n", j.nItem, wine_dbgstr_rect(&rcItem));
4813 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4814 LineTo (hdc, rcItem.left, rcItem.bottom);
4816 iterator_destroy(&j);
4817 /* draw rightmost grid line if visible */
4820 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
4821 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
4822 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
4824 rcItem.right += Origin.x;
4826 MoveToEx (hdc, rcItem.right, infoPtr->rcList.top, NULL);
4827 LineTo (hdc, rcItem.right, infoPtr->rcList.bottom);
4830 /* draw the horizontial lines for the rows */
4831 itemheight = LISTVIEW_CalculateItemHeight(infoPtr);
4832 rcItem.left = infoPtr->rcList.left;
4833 rcItem.right = infoPtr->rcList.right;
4834 rcItem.bottom = rcItem.top = Origin.y - 1;
4835 MoveToEx(hdc, rcItem.left, rcItem.top, NULL);
4836 LineTo(hdc, rcItem.right, rcItem.top);
4837 for(y=itemheight-1+Origin.y; y<=infoPtr->rcList.bottom; y+=itemheight)
4839 rcItem.bottom = rcItem.top = y;
4840 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem));
4841 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4842 LineTo (hdc, rcItem.right, rcItem.top);
4845 SelectObject( hdc, hOldPen );
4846 DeleteObject( hPen );
4852 * Draws listview items when in list display mode.
4855 * [I] infoPtr : valid pointer to the listview structure
4856 * [I] hdc : device context handle
4857 * [I] cdmode : custom draw mode
4862 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4864 POINT Origin, Position;
4866 /* Get scroll info once before loop */
4867 LISTVIEW_GetOrigin(infoPtr, &Origin);
4869 while(iterator_prev(i))
4871 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4872 Position.x += Origin.x;
4873 Position.y += Origin.y;
4875 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
4882 * Draws listview items.
4885 * [I] infoPtr : valid pointer to the listview structure
4886 * [I] hdc : device context handle
4887 * [I] prcErase : rect to be erased before refresh (may be NULL)
4892 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcErase)
4894 COLORREF oldTextColor = 0, oldBkColor = 0, oldClrTextBk, oldClrText;
4895 NMLVCUSTOMDRAW nmlvcd;
4902 HBITMAP hbmp = NULL;
4905 LISTVIEW_DUMP(infoPtr);
4907 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4908 TRACE("double buffering\n");
4910 hdc = CreateCompatibleDC(hdcOrig);
4912 ERR("Failed to create DC for backbuffer\n");
4915 hbmp = CreateCompatibleBitmap(hdcOrig, infoPtr->rcList.right,
4916 infoPtr->rcList.bottom);
4918 ERR("Failed to create bitmap for backbuffer\n");
4923 SelectObject(hdc, hbmp);
4924 SelectObject(hdc, infoPtr->hFont);
4926 /* Save dc values we're gonna trash while drawing
4927 * FIXME: Should be done in LISTVIEW_DrawItem() */
4928 hOldFont = SelectObject(hdc, infoPtr->hFont);
4929 oldBkMode = GetBkMode(hdc);
4930 oldBkColor = GetBkColor(hdc);
4931 oldTextColor = GetTextColor(hdc);
4934 infoPtr->bIsDrawing = TRUE;
4937 LISTVIEW_FillBkgnd(infoPtr, hdc, prcErase);
4938 } else if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4939 /* If no erasing was done (usually because RedrawWindow was called
4940 * with RDW_INVALIDATE only) we need to copy the old contents into
4941 * the backbuffer before continuing. */
4942 BitBlt(hdc, infoPtr->rcList.left, infoPtr->rcList.top,
4943 infoPtr->rcList.right - infoPtr->rcList.left,
4944 infoPtr->rcList.bottom - infoPtr->rcList.top,
4945 hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4948 /* FIXME: Shouldn't need to do this */
4949 oldClrTextBk = infoPtr->clrTextBk;
4950 oldClrText = infoPtr->clrText;
4952 infoPtr->cditemmode = CDRF_DODEFAULT;
4954 GetClientRect(infoPtr->hwndSelf, &rcClient);
4955 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4956 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4957 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4958 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4960 /* Use these colors to draw the items */
4961 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4962 infoPtr->clrText = nmlvcd.clrText;
4964 /* nothing to draw */
4965 if(infoPtr->nItemCount == 0) goto enddraw;
4967 /* figure out what we need to draw */
4968 iterator_visibleitems(&i, infoPtr, hdc);
4969 range = iterator_range(&i);
4971 /* send cache hint notification */
4972 if (infoPtr->dwStyle & LVS_OWNERDATA)
4976 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4977 nmlv.iFrom = range.lower;
4978 nmlv.iTo = range.upper - 1;
4979 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4982 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
4983 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4986 if (infoPtr->uView == LV_VIEW_DETAILS)
4987 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4988 else /* LV_VIEW_LIST, LV_VIEW_ICON or LV_VIEW_SMALLICON */
4989 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4991 /* if we have a focus rect and it's visible, draw it */
4992 if (infoPtr->bFocus && range.lower <= infoPtr->nFocusedItem &&
4993 (range.upper - 1) >= infoPtr->nFocusedItem)
4994 LISTVIEW_DrawFocusRect(infoPtr, hdc);
4996 iterator_destroy(&i);
4999 /* For LVS_EX_GRIDLINES go and draw lines */
5000 /* This includes the case where there were *no* items */
5001 if ((infoPtr->uView == LV_VIEW_DETAILS) && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
5002 LISTVIEW_RefreshReportGrid(infoPtr, hdc);
5004 /* Draw marquee rectangle if appropriate */
5005 if (infoPtr->bMarqueeSelect)
5006 DrawFocusRect(hdc, &infoPtr->marqueeDrawRect);
5008 if (cdmode & CDRF_NOTIFYPOSTPAINT)
5009 notify_postpaint(infoPtr, &nmlvcd);
5011 infoPtr->clrTextBk = oldClrTextBk;
5012 infoPtr->clrText = oldClrText;
5015 BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top,
5016 infoPtr->rcList.right - infoPtr->rcList.left,
5017 infoPtr->rcList.bottom - infoPtr->rcList.top,
5018 hdc, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
5023 SelectObject(hdc, hOldFont);
5024 SetBkMode(hdc, oldBkMode);
5025 SetBkColor(hdc, oldBkColor);
5026 SetTextColor(hdc, oldTextColor);
5029 infoPtr->bIsDrawing = FALSE;
5035 * Calculates the approximate width and height of a given number of items.
5038 * [I] infoPtr : valid pointer to the listview structure
5039 * [I] nItemCount : number of items
5040 * [I] wWidth : width
5041 * [I] wHeight : height
5044 * Returns a DWORD. The width in the low word and the height in high word.
5046 static DWORD LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO *infoPtr, INT nItemCount,
5047 WORD wWidth, WORD wHeight)
5049 INT nItemCountPerColumn = 1;
5050 INT nColumnCount = 0;
5051 DWORD dwViewRect = 0;
5053 if (nItemCount == -1)
5054 nItemCount = infoPtr->nItemCount;
5056 if (infoPtr->uView == LV_VIEW_LIST)
5058 if (wHeight == 0xFFFF)
5060 /* use current height */
5061 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
5064 if (wHeight < infoPtr->nItemHeight)
5065 wHeight = infoPtr->nItemHeight;
5069 if (infoPtr->nItemHeight > 0)
5071 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
5072 if (nItemCountPerColumn == 0)
5073 nItemCountPerColumn = 1;
5075 if (nItemCount % nItemCountPerColumn != 0)
5076 nColumnCount = nItemCount / nItemCountPerColumn;
5078 nColumnCount = nItemCount / nItemCountPerColumn + 1;
5082 /* Microsoft padding magic */
5083 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
5084 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
5086 dwViewRect = MAKELONG(wWidth, wHeight);
5088 else if (infoPtr->uView == LV_VIEW_DETAILS)
5092 if (infoPtr->nItemCount > 0)
5094 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
5095 wWidth = rcBox.right - rcBox.left;
5096 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
5100 /* use current height and width */
5101 if (wHeight == 0xffff)
5102 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
5103 if (wWidth == 0xffff)
5104 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
5107 dwViewRect = MAKELONG(wWidth, wHeight);
5109 else if (infoPtr->uView == LV_VIEW_ICON)
5115 nItemWidth = infoPtr->iconSpacing.cx;
5116 nItemHeight = infoPtr->iconSpacing.cy;
5118 if (nItemCount == -1)
5119 nItemCount = infoPtr->nItemCount;
5121 if (wWidth == 0xffff)
5122 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
5124 if (wWidth < nItemWidth)
5125 wWidth = nItemWidth;
5127 cols = wWidth / nItemWidth;
5128 if (cols > nItemCount)
5135 rows = nItemCount / cols;
5136 if (nItemCount % cols)
5142 wHeight = (nItemHeight * rows)+2;
5143 wWidth = (nItemWidth * cols)+2;
5145 dwViewRect = MAKELONG(wWidth, wHeight);
5147 else if (infoPtr->uView == LV_VIEW_SMALLICON)
5148 FIXME("uView == LV_VIEW_SMALLICON: not implemented\n");
5155 * Cancel edit label with saving item text.
5158 * [I] infoPtr : valid pointer to the listview structure
5161 * Always returns TRUE.
5163 static LRESULT LISTVIEW_CancelEditLabel(LISTVIEW_INFO *infoPtr)
5165 if (infoPtr->hwndEdit)
5167 /* handle value will be lost after LISTVIEW_EndEditLabelT */
5168 HWND edit = infoPtr->hwndEdit;
5170 LISTVIEW_EndEditLabelT(infoPtr, TRUE, IsWindowUnicode(infoPtr->hwndEdit));
5171 SendMessageW(edit, WM_CLOSE, 0, 0);
5179 * Create a drag image list for the specified item.
5182 * [I] infoPtr : valid pointer to the listview structure
5183 * [I] iItem : index of item
5184 * [O] lppt : Upper-left corner of the image
5187 * Returns a handle to the image list if successful, NULL otherwise.
5189 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
5195 HBITMAP hbmp, hOldbmp;
5196 HIMAGELIST dragList = 0;
5197 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
5199 if (iItem < 0 || iItem >= infoPtr->nItemCount)
5202 rcItem.left = LVIR_BOUNDS;
5203 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
5206 lppt->x = rcItem.left;
5207 lppt->y = rcItem.top;
5209 size.cx = rcItem.right - rcItem.left;
5210 size.cy = rcItem.bottom - rcItem.top;
5212 hdcOrig = GetDC(infoPtr->hwndSelf);
5213 hdc = CreateCompatibleDC(hdcOrig);
5214 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
5215 hOldbmp = SelectObject(hdc, hbmp);
5217 rcItem.left = rcItem.top = 0;
5218 rcItem.right = size.cx;
5219 rcItem.bottom = size.cy;
5220 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
5223 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
5225 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
5226 SelectObject(hdc, hOldbmp);
5227 ImageList_Add(dragList, hbmp, 0);
5230 SelectObject(hdc, hOldbmp);
5234 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
5236 TRACE("ret=%p\n", dragList);
5244 * Removes all listview items and subitems.
5247 * [I] infoPtr : valid pointer to the listview structure
5253 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr, BOOL destroy)
5256 HDPA hdpaSubItems = NULL;
5265 /* we do it directly, to avoid notifications */
5266 ranges_clear(infoPtr->selectionRanges);
5267 infoPtr->nSelectionMark = -1;
5268 infoPtr->nFocusedItem = -1;
5269 SetRectEmpty(&infoPtr->rcFocus);
5270 /* But we are supposed to leave nHotItem as is! */
5273 /* send LVN_DELETEALLITEMS notification */
5274 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
5276 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
5278 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
5280 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
5282 /* send LVN_DELETEITEM notification, if not suppressed
5283 and if it is not a virtual listview */
5284 if (!bSuppress) notify_deleteitem(infoPtr, i);
5285 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
5286 lpItem = DPA_GetPtr(hdpaSubItems, 0);
5287 /* free id struct */
5288 j = DPA_GetPtrIndex(infoPtr->hdpaItemIds, lpItem->id);
5289 lpID = DPA_GetPtr(infoPtr->hdpaItemIds, j);
5290 DPA_DeletePtr(infoPtr->hdpaItemIds, j);
5292 /* both item and subitem start with ITEMHDR header */
5293 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
5295 hdrItem = DPA_GetPtr(hdpaSubItems, j);
5296 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
5299 DPA_Destroy(hdpaSubItems);
5300 DPA_DeletePtr(infoPtr->hdpaItems, i);
5302 DPA_DeletePtr(infoPtr->hdpaPosX, i);
5303 DPA_DeletePtr(infoPtr->hdpaPosY, i);
5304 infoPtr->nItemCount --;
5309 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
5310 LISTVIEW_UpdateScroll(infoPtr);
5312 LISTVIEW_InvalidateList(infoPtr);
5319 * Scrolls, and updates the columns, when a column is changing width.
5322 * [I] infoPtr : valid pointer to the listview structure
5323 * [I] nColumn : column to scroll
5324 * [I] dx : amount of scroll, in pixels
5329 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
5331 COLUMN_INFO *lpColumnInfo;
5337 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
5338 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
5339 rcCol = lpColumnInfo->rcHeader;
5340 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
5341 rcCol.left = rcCol.right;
5343 /* adjust the other columns */
5344 hdi.mask = HDI_ORDER;
5345 if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdi))
5347 INT nOrder = hdi.iOrder;
5348 for (nCol = 0; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
5350 hdi.mask = HDI_ORDER;
5351 SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nCol, (LPARAM)&hdi);
5352 if (hdi.iOrder >= nOrder) {
5353 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
5354 lpColumnInfo->rcHeader.left += dx;
5355 lpColumnInfo->rcHeader.right += dx;
5360 /* do not update screen if not in report mode */
5361 if (!is_redrawing(infoPtr) || infoPtr->uView != LV_VIEW_DETAILS) return;
5363 /* Need to reset the item width when inserting a new column */
5364 infoPtr->nItemWidth += dx;
5366 LISTVIEW_UpdateScroll(infoPtr);
5367 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
5369 /* scroll to cover the deleted column, and invalidate for redraw */
5370 rcOld = infoPtr->rcList;
5371 rcOld.left = ptOrigin.x + rcCol.left + dx;
5372 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
5377 * Removes a column from the listview control.
5380 * [I] infoPtr : valid pointer to the listview structure
5381 * [I] nColumn : column index
5387 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
5391 TRACE("nColumn=%d\n", nColumn);
5393 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
5394 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5396 /* While the MSDN specifically says that column zero should not be deleted,
5397 what actually happens is that the column itself is deleted but no items or subitems
5401 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
5403 if (!SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nColumn, 0))
5406 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
5407 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
5409 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
5411 SUBITEM_INFO *lpSubItem, *lpDelItem;
5413 INT nItem, nSubItem, i;
5415 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
5417 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
5420 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
5422 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
5423 if (lpSubItem->iSubItem == nColumn)
5426 lpDelItem = lpSubItem;
5428 else if (lpSubItem->iSubItem > nColumn)
5430 lpSubItem->iSubItem--;
5434 /* if we found our subitem, zapp it */
5438 if (is_textW(lpDelItem->hdr.pszText))
5439 Free(lpDelItem->hdr.pszText);
5444 /* free dpa memory */
5445 DPA_DeletePtr(hdpaSubItems, nSubItem);
5450 /* update the other column info */
5451 LISTVIEW_UpdateItemSize(infoPtr);
5452 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
5453 LISTVIEW_InvalidateList(infoPtr);
5455 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
5462 * Invalidates the listview after an item's insertion or deletion.
5465 * [I] infoPtr : valid pointer to the listview structure
5466 * [I] nItem : item index
5467 * [I] dir : -1 if deleting, 1 if inserting
5472 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
5474 INT nPerCol, nItemCol, nItemRow;
5478 /* if we don't refresh, what's the point of scrolling? */
5479 if (!is_redrawing(infoPtr)) return;
5481 assert (abs(dir) == 1);
5483 /* arrange icons if autoarrange is on */
5484 if (is_autoarrange(infoPtr))
5486 BOOL arrange = TRUE;
5487 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
5488 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
5489 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
5492 /* scrollbars need updating */
5493 LISTVIEW_UpdateScroll(infoPtr);
5495 /* figure out the item's position */
5496 if (infoPtr->uView == LV_VIEW_DETAILS)
5497 nPerCol = infoPtr->nItemCount + 1;
5498 else if (infoPtr->uView == LV_VIEW_LIST)
5499 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
5500 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
5503 nItemCol = nItem / nPerCol;
5504 nItemRow = nItem % nPerCol;
5505 LISTVIEW_GetOrigin(infoPtr, &Origin);
5507 /* move the items below up a slot */
5508 rcScroll.left = nItemCol * infoPtr->nItemWidth;
5509 rcScroll.top = nItemRow * infoPtr->nItemHeight;
5510 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
5511 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
5512 OffsetRect(&rcScroll, Origin.x, Origin.y);
5513 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
5514 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
5516 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
5517 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
5518 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
5521 /* report has only that column, so we're done */
5522 if (infoPtr->uView == LV_VIEW_DETAILS) return;
5524 /* now for LISTs, we have to deal with the columns to the right */
5525 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
5527 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
5528 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
5529 OffsetRect(&rcScroll, Origin.x, Origin.y);
5530 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
5531 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
5532 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
5537 * Removes an item from the listview control.
5540 * [I] infoPtr : valid pointer to the listview structure
5541 * [I] nItem : item index
5547 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
5550 const BOOL is_icon = (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON);
5552 TRACE("(nItem=%d)\n", nItem);
5554 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5556 /* remove selection, and focus */
5558 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
5559 LISTVIEW_SetItemState(infoPtr, nItem, &item);
5561 /* send LVN_DELETEITEM notification. */
5562 if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
5564 /* we need to do this here, because we'll be deleting stuff */
5566 LISTVIEW_InvalidateItem(infoPtr, nItem);
5568 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
5576 hdpaSubItems = DPA_DeletePtr(infoPtr->hdpaItems, nItem);
5577 lpItem = DPA_GetPtr(hdpaSubItems, 0);
5579 /* free id struct */
5580 i = DPA_GetPtrIndex(infoPtr->hdpaItemIds, lpItem->id);
5581 lpID = DPA_GetPtr(infoPtr->hdpaItemIds, i);
5582 DPA_DeletePtr(infoPtr->hdpaItemIds, i);
5584 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
5586 hdrItem = DPA_GetPtr(hdpaSubItems, i);
5587 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
5590 DPA_Destroy(hdpaSubItems);
5595 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5596 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
5599 infoPtr->nItemCount--;
5600 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
5602 /* now is the invalidation fun */
5604 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
5611 * Callback implementation for editlabel control
5614 * [I] infoPtr : valid pointer to the listview structure
5615 * [I] storeText : store edit box text as item text
5616 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
5622 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, BOOL storeText, BOOL isW)
5624 HWND hwndSelf = infoPtr->hwndSelf;
5625 NMLVDISPINFOW dispInfo;
5626 INT editedItem = infoPtr->nEditLabelItem;
5628 WCHAR *pszText = NULL;
5633 DWORD len = isW ? GetWindowTextLengthW(infoPtr->hwndEdit) : GetWindowTextLengthA(infoPtr->hwndEdit);
5637 if ((pszText = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))))
5639 if (isW) GetWindowTextW(infoPtr->hwndEdit, pszText, len+1);
5640 else GetWindowTextA(infoPtr->hwndEdit, (CHAR*)pszText, len+1);
5645 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
5647 infoPtr->nEditLabelItem = -1;
5648 infoPtr->hwndEdit = 0;
5650 ZeroMemory(&dispInfo, sizeof(dispInfo));
5651 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
5652 dispInfo.item.iItem = editedItem;
5653 dispInfo.item.iSubItem = 0;
5654 dispInfo.item.stateMask = ~0;
5655 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item))
5662 bSame = (lstrcmpW(dispInfo.item.pszText, pszText) == 0);
5665 LPWSTR tmp = textdupTtoW(pszText, FALSE);
5666 bSame = (lstrcmpW(dispInfo.item.pszText, tmp) == 0);
5667 textfreeT(tmp, FALSE);
5670 /* add the text from the edit in */
5671 dispInfo.item.mask |= LVIF_TEXT;
5672 dispInfo.item.pszText = bSame ? NULL : pszText;
5673 dispInfo.item.cchTextMax = bSame ? 0 : textlenT(pszText, isW);
5675 /* Do we need to update the Item Text */
5676 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW))
5681 if (!IsWindow(hwndSelf))
5686 if (!pszText) return TRUE;
5693 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
5695 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, editedItem);
5696 ITEM_INFO* lpItem = DPA_GetPtr(hdpaSubItems, 0);
5697 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
5699 LISTVIEW_InvalidateItem(infoPtr, editedItem);
5705 ZeroMemory(&dispInfo, sizeof(dispInfo));
5706 dispInfo.item.mask = LVIF_TEXT;
5707 dispInfo.item.iItem = editedItem;
5708 dispInfo.item.iSubItem = 0;
5709 dispInfo.item.pszText = pszText;
5710 dispInfo.item.cchTextMax = textlenT(pszText, isW);
5711 res = LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
5721 * Begin in place editing of specified list view item
5724 * [I] infoPtr : valid pointer to the listview structure
5725 * [I] nItem : item index
5726 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
5732 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
5734 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
5735 NMLVDISPINFOW dispInfo;
5738 HWND hwndSelf = infoPtr->hwndSelf;
5740 HFONT hOldFont = NULL;
5741 TEXTMETRICW textMetric;
5743 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
5745 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
5747 /* Is the EditBox still there, if so remove it */
5748 if(infoPtr->hwndEdit != 0)
5750 SetFocus(infoPtr->hwndSelf);
5751 infoPtr->hwndEdit = 0;
5754 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5756 infoPtr->nEditLabelItem = nItem;
5758 LISTVIEW_SetSelection(infoPtr, nItem);
5759 LISTVIEW_SetItemFocus(infoPtr, nItem);
5760 LISTVIEW_InvalidateItem(infoPtr, nItem);
5762 rect.left = LVIR_LABEL;
5763 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
5765 ZeroMemory(&dispInfo, sizeof(dispInfo));
5766 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
5767 dispInfo.item.iItem = nItem;
5768 dispInfo.item.iSubItem = 0;
5769 dispInfo.item.stateMask = ~0;
5770 dispInfo.item.pszText = szDispText;
5771 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
5772 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
5774 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE, isW);
5775 if (!infoPtr->hwndEdit) return 0;
5777 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
5779 if (!IsWindow(hwndSelf))
5781 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
5782 infoPtr->hwndEdit = 0;
5786 /* Now position and display edit box */
5787 hdc = GetDC(infoPtr->hwndSelf);
5789 /* Select the font to get appropriate metric dimensions */
5790 if(infoPtr->hFont != 0)
5791 hOldFont = SelectObject(hdc, infoPtr->hFont);
5793 /* Get String Length in pixels */
5794 GetTextExtentPoint32W(hdc, dispInfo.item.pszText, lstrlenW(dispInfo.item.pszText), &sz);
5796 /* Add Extra spacing for the next character */
5797 GetTextMetricsW(hdc, &textMetric);
5798 sz.cx += (textMetric.tmMaxCharWidth * 2);
5800 if(infoPtr->hFont != 0)
5801 SelectObject(hdc, hOldFont);
5803 ReleaseDC(infoPtr->hwndSelf, hdc);
5805 MoveWindow(infoPtr->hwndEdit, rect.left - 2, rect.top - 1, sz.cx,
5806 rect.bottom - rect.top + 2, FALSE);
5807 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
5808 SetFocus(infoPtr->hwndEdit);
5809 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
5810 return infoPtr->hwndEdit;
5816 * Ensures the specified item is visible, scrolling into view if necessary.
5819 * [I] infoPtr : valid pointer to the listview structure
5820 * [I] nItem : item index
5821 * [I] bPartial : partially or entirely visible
5827 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
5829 INT nScrollPosHeight = 0;
5830 INT nScrollPosWidth = 0;
5831 INT nHorzAdjust = 0;
5832 INT nVertAdjust = 0;
5835 RECT rcItem, rcTemp;
5837 rcItem.left = LVIR_BOUNDS;
5838 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
5840 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
5842 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
5844 /* scroll left/right, but in LV_VIEW_DETAILS mode */
5845 if (infoPtr->uView == LV_VIEW_LIST)
5846 nScrollPosWidth = infoPtr->nItemWidth;
5847 else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
5848 nScrollPosWidth = 1;
5850 if (rcItem.left < infoPtr->rcList.left)
5853 if (infoPtr->uView != LV_VIEW_DETAILS) nHorzDiff = rcItem.left - infoPtr->rcList.left;
5858 if (infoPtr->uView != LV_VIEW_DETAILS) nHorzDiff = rcItem.right - infoPtr->rcList.right;
5862 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
5864 /* scroll up/down, but not in LVS_LIST mode */
5865 if (infoPtr->uView == LV_VIEW_DETAILS)
5866 nScrollPosHeight = infoPtr->nItemHeight;
5867 else if ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON))
5868 nScrollPosHeight = 1;
5870 if (rcItem.top < infoPtr->rcList.top)
5873 if (infoPtr->uView != LV_VIEW_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
5878 if (infoPtr->uView != LV_VIEW_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
5882 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
5884 if (nScrollPosWidth)
5886 INT diff = nHorzDiff / nScrollPosWidth;
5887 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
5888 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
5891 if (nScrollPosHeight)
5893 INT diff = nVertDiff / nScrollPosHeight;
5894 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
5895 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
5903 * Searches for an item with specific characteristics.
5906 * [I] hwnd : window handle
5907 * [I] nStart : base item index
5908 * [I] lpFindInfo : item information to look for
5911 * SUCCESS : index of item
5914 static INT LISTVIEW_FindItemW(const LISTVIEW_INFO *infoPtr, INT nStart,
5915 const LVFINDINFOW *lpFindInfo)
5917 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5918 BOOL bWrap = FALSE, bNearest = FALSE;
5919 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
5920 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
5921 POINT Position, Destination;
5924 /* Search in virtual listviews should be done by application, not by
5925 listview control, so we just send LVN_ODFINDITEMW and return the result */
5926 if (infoPtr->dwStyle & LVS_OWNERDATA)
5930 nmlv.iStart = nStart;
5931 nmlv.lvfi = *lpFindInfo;
5932 return notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
5935 if (!lpFindInfo || nItem < 0) return -1;
5938 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL) ||
5939 lpFindInfo->flags & LVFI_SUBSTRING)
5941 lvItem.mask |= LVIF_TEXT;
5942 lvItem.pszText = szDispText;
5943 lvItem.cchTextMax = DISP_TEXT_SIZE;
5946 if (lpFindInfo->flags & LVFI_WRAP)
5949 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
5950 (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON))
5955 LISTVIEW_GetOrigin(infoPtr, &Origin);
5956 Destination.x = lpFindInfo->pt.x - Origin.x;
5957 Destination.y = lpFindInfo->pt.y - Origin.y;
5958 switch(lpFindInfo->vkDirection)
5960 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
5961 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
5962 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
5963 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
5964 case VK_HOME: Destination.x = Destination.y = 0; break;
5965 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5966 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5968 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
5969 Destination.x = rcArea.right;
5970 Destination.y = rcArea.bottom;
5972 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
5976 else Destination.x = Destination.y = 0;
5978 /* if LVFI_PARAM is specified, all other flags are ignored */
5979 if (lpFindInfo->flags & LVFI_PARAM)
5981 lvItem.mask |= LVIF_PARAM;
5983 lvItem.mask &= ~LVIF_TEXT;
5987 for (; nItem < nLast; nItem++)
5989 lvItem.iItem = nItem;
5990 lvItem.iSubItem = 0;
5991 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
5993 if (lvItem.mask & LVIF_PARAM)
5995 if (lpFindInfo->lParam == lvItem.lParam)
6001 if (lvItem.mask & LVIF_TEXT)
6003 if (lpFindInfo->flags & (LVFI_PARTIAL | LVFI_SUBSTRING))
6005 WCHAR *p = strstrW(lvItem.pszText, lpFindInfo->psz);
6006 if (!p || p != lvItem.pszText) continue;
6010 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
6014 if (!bNearest) return nItem;
6016 /* This is very inefficient. To do a good job here,
6017 * we need a sorted array of (x,y) item positions */
6018 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
6020 /* compute the distance^2 to the destination */
6021 xdist = Destination.x - Position.x;
6022 ydist = Destination.y - Position.y;
6023 dist = xdist * xdist + ydist * ydist;
6025 /* remember the distance, and item if it's closer */
6029 nNearestItem = nItem;
6036 nLast = min(nStart + 1, infoPtr->nItemCount);
6041 return nNearestItem;
6046 * Searches for an item with specific characteristics.
6049 * [I] hwnd : window handle
6050 * [I] nStart : base item index
6051 * [I] lpFindInfo : item information to look for
6054 * SUCCESS : index of item
6057 static INT LISTVIEW_FindItemA(const LISTVIEW_INFO *infoPtr, INT nStart,
6058 const LVFINDINFOA *lpFindInfo)
6060 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL) ||
6061 lpFindInfo->flags & LVFI_SUBSTRING;
6066 memcpy(&fiw, lpFindInfo, sizeof(fiw));
6067 if (hasText) fiw.psz = strW = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
6068 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
6069 textfreeT(strW, FALSE);
6075 * Retrieves the background image of the listview control.
6078 * [I] infoPtr : valid pointer to the listview structure
6079 * [O] lpBkImage : background image attributes
6085 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
6087 /* FIXME (listview, "empty stub!\n"); */
6093 * Retrieves column attributes.
6096 * [I] infoPtr : valid pointer to the listview structure
6097 * [I] nColumn : column index
6098 * [IO] lpColumn : column information
6099 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
6100 * otherwise it is in fact a LPLVCOLUMNA
6106 static BOOL LISTVIEW_GetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
6108 COLUMN_INFO *lpColumnInfo;
6111 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6112 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6114 /* initialize memory */
6115 ZeroMemory(&hdi, sizeof(hdi));
6117 if (lpColumn->mask & LVCF_TEXT)
6119 hdi.mask |= HDI_TEXT;
6120 hdi.pszText = lpColumn->pszText;
6121 hdi.cchTextMax = lpColumn->cchTextMax;
6124 if (lpColumn->mask & LVCF_IMAGE)
6125 hdi.mask |= HDI_IMAGE;
6127 if (lpColumn->mask & LVCF_ORDER)
6128 hdi.mask |= HDI_ORDER;
6130 if (lpColumn->mask & LVCF_SUBITEM)
6131 hdi.mask |= HDI_LPARAM;
6133 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
6135 if (lpColumn->mask & LVCF_FMT)
6136 lpColumn->fmt = lpColumnInfo->fmt;
6138 if (lpColumn->mask & LVCF_WIDTH)
6139 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
6141 if (lpColumn->mask & LVCF_IMAGE)
6142 lpColumn->iImage = hdi.iImage;
6144 if (lpColumn->mask & LVCF_ORDER)
6145 lpColumn->iOrder = hdi.iOrder;
6147 if (lpColumn->mask & LVCF_SUBITEM)
6148 lpColumn->iSubItem = hdi.lParam;
6150 if (lpColumn->mask & LVCF_MINWIDTH)
6151 lpColumn->cxMin = lpColumnInfo->cxMin;
6157 static BOOL LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6159 TRACE("iCount=%d, lpiArray=%p\n", iCount, lpiArray);
6164 return SendMessageW(infoPtr->hwndHeader, HDM_GETORDERARRAY, iCount, (LPARAM)lpiArray);
6169 * Retrieves the column width.
6172 * [I] infoPtr : valid pointer to the listview structure
6173 * [I] int : column index
6176 * SUCCESS : column width
6179 static INT LISTVIEW_GetColumnWidth(const LISTVIEW_INFO *infoPtr, INT nColumn)
6181 INT nColumnWidth = 0;
6184 TRACE("nColumn=%d\n", nColumn);
6186 /* we have a 'column' in LIST and REPORT mode only */
6187 switch(infoPtr->uView)
6190 nColumnWidth = infoPtr->nItemWidth;
6192 case LV_VIEW_DETAILS:
6193 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDN_ITEMCHANGED.
6194 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
6195 * HDN_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
6197 * TODO: should we do the same in LVM_GETCOLUMN?
6199 hdItem.mask = HDI_WIDTH;
6200 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdItem))
6202 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr->hwndSelf, nColumn);
6205 nColumnWidth = hdItem.cxy;
6209 TRACE("nColumnWidth=%d\n", nColumnWidth);
6210 return nColumnWidth;
6215 * In list or report display mode, retrieves the number of items that can fit
6216 * vertically in the visible area. In icon or small icon display mode,
6217 * retrieves the total number of visible items.
6220 * [I] infoPtr : valid pointer to the listview structure
6223 * Number of fully visible items.
6225 static INT LISTVIEW_GetCountPerPage(const LISTVIEW_INFO *infoPtr)
6227 switch (infoPtr->uView)
6230 case LV_VIEW_SMALLICON:
6231 return infoPtr->nItemCount;
6232 case LV_VIEW_DETAILS:
6233 return LISTVIEW_GetCountPerColumn(infoPtr);
6235 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
6243 * Retrieves an image list handle.
6246 * [I] infoPtr : valid pointer to the listview structure
6247 * [I] nImageList : image list identifier
6250 * SUCCESS : image list handle
6253 static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImageList)
6257 case LVSIL_NORMAL: return infoPtr->himlNormal;
6258 case LVSIL_SMALL: return infoPtr->himlSmall;
6259 case LVSIL_STATE: return infoPtr->himlState;
6260 case LVSIL_GROUPHEADER:
6261 FIXME("LVSIL_GROUPHEADER not supported\n");
6264 WARN("got unknown imagelist index - %d\n", nImageList);
6269 /* LISTVIEW_GetISearchString */
6273 * Retrieves item attributes.
6276 * [I] hwnd : window handle
6277 * [IO] lpLVItem : item info
6278 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
6279 * if FALSE, then lpLVItem is a LPLVITEMA.
6282 * This is the internal 'GetItem' interface -- it tries to
6283 * be smart and avoid text copies, if possible, by modifying
6284 * lpLVItem->pszText to point to the text string. Please note
6285 * that this is not always possible (e.g. OWNERDATA), so on
6286 * entry you *must* supply valid values for pszText, and cchTextMax.
6287 * The only difference to the documented interface is that upon
6288 * return, you should use *only* the lpLVItem->pszText, rather than
6289 * the buffer pointer you provided on input. Most code already does
6290 * that, so it's not a problem.
6291 * For the two cases when the text must be copied (that is,
6292 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
6298 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
6300 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
6301 NMLVDISPINFOW dispInfo;
6307 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6309 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
6312 if (lpLVItem->mask == 0) return TRUE;
6314 /* make a local copy */
6315 isubitem = lpLVItem->iSubItem;
6317 /* a quick optimization if all we're asked is the focus state
6318 * these queries are worth optimising since they are common,
6319 * and can be answered in constant time, without the heavy accesses */
6320 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
6321 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
6323 lpLVItem->state = 0;
6324 if (infoPtr->nFocusedItem == lpLVItem->iItem)
6325 lpLVItem->state |= LVIS_FOCUSED;
6329 ZeroMemory(&dispInfo, sizeof(dispInfo));
6331 /* if the app stores all the data, handle it separately */
6332 if (infoPtr->dwStyle & LVS_OWNERDATA)
6334 dispInfo.item.state = 0;
6336 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
6337 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) ||
6338 ((lpLVItem->mask & LVIF_STATE) && (infoPtr->uCallbackMask & lpLVItem->stateMask)))
6340 UINT mask = lpLVItem->mask;
6342 /* NOTE: copy only fields which we _know_ are initialized, some apps
6343 * depend on the uninitialized fields being 0 */
6344 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
6345 dispInfo.item.iItem = lpLVItem->iItem;
6346 dispInfo.item.iSubItem = isubitem;
6347 if (lpLVItem->mask & LVIF_TEXT)
6349 if (lpLVItem->mask & LVIF_NORECOMPUTE)
6351 dispInfo.item.mask &= ~(LVIF_TEXT | LVIF_NORECOMPUTE);
6354 dispInfo.item.pszText = lpLVItem->pszText;
6355 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
6358 if (lpLVItem->mask & LVIF_STATE)
6359 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
6360 /* could be zeroed on LVIF_NORECOMPUTE case */
6361 if (dispInfo.item.mask != 0)
6363 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
6364 dispInfo.item.stateMask = lpLVItem->stateMask;
6365 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6367 /* full size structure expected - _WIN32IE >= 0x560 */
6368 *lpLVItem = dispInfo.item;
6370 else if (lpLVItem->mask & LVIF_INDENT)
6372 /* indent member expected - _WIN32IE >= 0x300 */
6373 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
6377 /* minimal structure expected */
6378 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
6380 lpLVItem->mask = mask;
6381 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
6385 /* make sure lParam is zeroed out */
6386 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
6388 /* callback marked pointer required here */
6389 if ((lpLVItem->mask & LVIF_TEXT) && (lpLVItem->mask & LVIF_NORECOMPUTE))
6390 lpLVItem->pszText = LPSTR_TEXTCALLBACKW;
6392 /* we store only a little state, so if we're not asked, we're done */
6393 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
6395 /* if focus is handled by us, report it */
6396 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
6398 lpLVItem->state &= ~LVIS_FOCUSED;
6399 if (infoPtr->nFocusedItem == lpLVItem->iItem)
6400 lpLVItem->state |= LVIS_FOCUSED;
6403 /* and do the same for selection, if we handle it */
6404 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
6406 lpLVItem->state &= ~LVIS_SELECTED;
6407 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
6408 lpLVItem->state |= LVIS_SELECTED;
6414 /* find the item and subitem structures before we proceed */
6415 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
6416 lpItem = DPA_GetPtr(hdpaSubItems, 0);
6421 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
6422 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
6425 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
6430 pItemHdr = &lpItem->hdr;
6432 /* Do we need to query the state from the app? */
6433 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
6435 dispInfo.item.mask |= LVIF_STATE;
6436 dispInfo.item.stateMask = infoPtr->uCallbackMask;
6439 /* Do we need to enquire about the image? */
6440 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
6441 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
6443 dispInfo.item.mask |= LVIF_IMAGE;
6444 dispInfo.item.iImage = I_IMAGECALLBACK;
6447 /* Only items support indentation */
6448 if ((lpLVItem->mask & LVIF_INDENT) && lpItem->iIndent == I_INDENTCALLBACK &&
6451 dispInfo.item.mask |= LVIF_INDENT;
6452 dispInfo.item.iIndent = I_INDENTCALLBACK;
6455 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
6456 if ((lpLVItem->mask & LVIF_TEXT) && !(lpLVItem->mask & LVIF_NORECOMPUTE) &&
6457 !is_textW(pItemHdr->pszText))
6459 dispInfo.item.mask |= LVIF_TEXT;
6460 dispInfo.item.pszText = lpLVItem->pszText;
6461 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
6462 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
6463 *dispInfo.item.pszText = '\0';
6466 /* If we don't have all the requested info, query the application */
6467 if (dispInfo.item.mask != 0)
6469 dispInfo.item.iItem = lpLVItem->iItem;
6470 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
6471 dispInfo.item.lParam = lpItem->lParam;
6472 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
6473 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
6476 /* we should not store values for subitems */
6477 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
6479 /* Now, handle the iImage field */
6480 if (dispInfo.item.mask & LVIF_IMAGE)
6482 lpLVItem->iImage = dispInfo.item.iImage;
6483 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
6484 pItemHdr->iImage = dispInfo.item.iImage;
6486 else if (lpLVItem->mask & LVIF_IMAGE)
6488 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
6489 lpLVItem->iImage = pItemHdr->iImage;
6491 lpLVItem->iImage = 0;
6494 /* The pszText field */
6495 if (dispInfo.item.mask & LVIF_TEXT)
6497 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
6498 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
6500 lpLVItem->pszText = dispInfo.item.pszText;
6502 else if (lpLVItem->mask & LVIF_TEXT)
6504 /* if LVN_GETDISPINFO's disabled with LVIF_NORECOMPUTE return callback placeholder */
6505 if (isW || !is_textW(pItemHdr->pszText)) lpLVItem->pszText = pItemHdr->pszText;
6506 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
6509 /* Next is the lParam field */
6510 if (dispInfo.item.mask & LVIF_PARAM)
6512 lpLVItem->lParam = dispInfo.item.lParam;
6513 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
6514 lpItem->lParam = dispInfo.item.lParam;
6516 else if (lpLVItem->mask & LVIF_PARAM)
6517 lpLVItem->lParam = lpItem->lParam;
6519 /* if this is a subitem, we're done */
6520 if (isubitem) return TRUE;
6522 /* ... the state field (this one is different due to uCallbackmask) */
6523 if (lpLVItem->mask & LVIF_STATE)
6525 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
6526 if (dispInfo.item.mask & LVIF_STATE)
6528 lpLVItem->state &= ~dispInfo.item.stateMask;
6529 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
6531 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
6533 lpLVItem->state &= ~LVIS_FOCUSED;
6534 if (infoPtr->nFocusedItem == lpLVItem->iItem)
6535 lpLVItem->state |= LVIS_FOCUSED;
6537 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
6539 lpLVItem->state &= ~LVIS_SELECTED;
6540 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
6541 lpLVItem->state |= LVIS_SELECTED;
6545 /* and last, but not least, the indent field */
6546 if (dispInfo.item.mask & LVIF_INDENT)
6548 lpLVItem->iIndent = dispInfo.item.iIndent;
6549 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && lpItem->iIndent == I_INDENTCALLBACK)
6550 lpItem->iIndent = dispInfo.item.iIndent;
6552 else if (lpLVItem->mask & LVIF_INDENT)
6554 lpLVItem->iIndent = lpItem->iIndent;
6562 * Retrieves item attributes.
6565 * [I] hwnd : window handle
6566 * [IO] lpLVItem : item info
6567 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
6568 * if FALSE, then lpLVItem is a LPLVITEMA.
6571 * This is the external 'GetItem' interface -- it properly copies
6572 * the text in the provided buffer.
6578 static BOOL LISTVIEW_GetItemExtT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
6583 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
6586 pszText = lpLVItem->pszText;
6587 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
6588 if (bResult && lpLVItem->pszText != pszText)
6590 if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW)
6591 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
6593 pszText = LPSTR_TEXTCALLBACKW;
6595 lpLVItem->pszText = pszText;
6603 * Retrieves the position (upper-left) of the listview control item.
6604 * Note that for LVS_ICON style, the upper-left is that of the icon
6605 * and not the bounding box.
6608 * [I] infoPtr : valid pointer to the listview structure
6609 * [I] nItem : item index
6610 * [O] lpptPosition : coordinate information
6616 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
6620 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
6622 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
6624 LISTVIEW_GetOrigin(infoPtr, &Origin);
6625 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
6627 if (infoPtr->uView == LV_VIEW_ICON)
6629 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
6630 lpptPosition->y += ICON_TOP_PADDING;
6632 lpptPosition->x += Origin.x;
6633 lpptPosition->y += Origin.y;
6635 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
6642 * Retrieves the bounding rectangle for a listview control item.
6645 * [I] infoPtr : valid pointer to the listview structure
6646 * [I] nItem : item index
6647 * [IO] lprc : bounding rectangle coordinates
6648 * lprc->left specifies the portion of the item for which the bounding
6649 * rectangle will be retrieved.
6651 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
6652 * including the icon and label.
6655 * * Experiment shows that native control returns:
6656 * * width = min (48, length of text line)
6657 * * .left = position.x - (width - iconsize.cx)/2
6658 * * .right = .left + width
6659 * * height = #lines of text * ntmHeight + icon height + 8
6660 * * .top = position.y - 2
6661 * * .bottom = .top + height
6662 * * separation between items .y = itemSpacing.cy - height
6663 * * .x = itemSpacing.cx - width
6664 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
6667 * * Experiment shows that native control returns:
6668 * * width = iconSize.cx + 16
6669 * * .left = position.x - (width - iconsize.cx)/2
6670 * * .right = .left + width
6671 * * height = iconSize.cy + 4
6672 * * .top = position.y - 2
6673 * * .bottom = .top + height
6674 * * separation between items .y = itemSpacing.cy - height
6675 * * .x = itemSpacing.cx - width
6676 * LVIR_LABEL Returns the bounding rectangle of the item text.
6679 * * Experiment shows that native control returns:
6680 * * width = text length
6681 * * .left = position.x - width/2
6682 * * .right = .left + width
6683 * * height = ntmH * linecount + 2
6684 * * .top = position.y + iconSize.cy + 6
6685 * * .bottom = .top + height
6686 * * separation between items .y = itemSpacing.cy - height
6687 * * .x = itemSpacing.cx - width
6688 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
6689 * rectangles, but excludes columns in report view.
6696 * Note that the bounding rectangle of the label in the LVS_ICON view depends
6697 * upon whether the window has the focus currently and on whether the item
6698 * is the one with the focus. Ensure that the control's record of which
6699 * item has the focus agrees with the items' records.
6701 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
6703 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6704 BOOL doLabel = TRUE, oversizedBox = FALSE;
6705 POINT Position, Origin;
6709 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
6711 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
6713 LISTVIEW_GetOrigin(infoPtr, &Origin);
6714 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
6716 /* Be smart and try to figure out the minimum we have to do */
6717 if (lprc->left == LVIR_ICON) doLabel = FALSE;
6718 if (infoPtr->uView == LV_VIEW_DETAILS && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
6719 if (infoPtr->uView == LV_VIEW_ICON && lprc->left != LVIR_ICON &&
6720 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
6721 oversizedBox = TRUE;
6723 /* get what we need from the item before hand, so we make
6724 * only one request. This can speed up things, if data
6725 * is stored on the app side */
6727 if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
6728 if (doLabel) lvItem.mask |= LVIF_TEXT;
6729 lvItem.iItem = nItem;
6730 lvItem.iSubItem = 0;
6731 lvItem.pszText = szDispText;
6732 lvItem.cchTextMax = DISP_TEXT_SIZE;
6733 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
6734 /* we got the state already up, simulate it here, to avoid a reget */
6735 if (infoPtr->uView == LV_VIEW_ICON && (lprc->left != LVIR_ICON))
6737 lvItem.mask |= LVIF_STATE;
6738 lvItem.stateMask = LVIS_FOCUSED;
6739 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
6742 if (infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
6743 lprc->left = LVIR_BOUNDS;
6749 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
6753 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc);
6757 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
6760 case LVIR_SELECTBOUNDS:
6761 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL);
6765 WARN("Unknown value: %d\n", lprc->left);
6769 if (infoPtr->uView == LV_VIEW_DETAILS)
6771 if (mode != LVIR_BOUNDS)
6772 OffsetRect(lprc, Origin.x + LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left,
6773 Position.y + Origin.y);
6775 OffsetRect(lprc, Origin.x, Position.y + Origin.y);
6778 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
6780 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
6787 * Retrieves the spacing between listview control items.
6790 * [I] infoPtr : valid pointer to the listview structure
6791 * [IO] lprc : rectangle to receive the output
6792 * on input, lprc->top = nSubItem
6793 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
6795 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
6796 * not only those of the first column.
6797 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
6803 static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
6809 if (!lprc) return FALSE;
6811 nColumn = lprc->top;
6813 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
6814 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
6816 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
6818 if (infoPtr->uView != LV_VIEW_DETAILS) return FALSE;
6820 /* special case for header items */
6823 if (lprc->left != LVIR_BOUNDS)
6825 FIXME("Only LVIR_BOUNDS is implemented for header, got %d\n", lprc->left);
6829 if (infoPtr->hwndHeader)
6830 return SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, lprc->top, (LPARAM)lprc);
6833 memset(lprc, 0, sizeof(RECT));
6838 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
6840 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6843 lvItem.iItem = nItem;
6844 lvItem.iSubItem = nColumn;
6846 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
6850 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
6855 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
6859 ERR("Unknown bounds=%d\n", lprc->left);
6863 OffsetRect(lprc, 0, Position.y);
6869 * Retrieves the spacing between listview control items.
6872 * [I] infoPtr : valid pointer to the listview structure
6873 * [I] bSmall : flag for small or large icon
6876 * Horizontal + vertical spacing
6878 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall)
6884 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6888 if (infoPtr->uView == LV_VIEW_ICON)
6889 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6891 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6898 * Retrieves the state of a listview control item.
6901 * [I] infoPtr : valid pointer to the listview structure
6902 * [I] nItem : item index
6903 * [I] uMask : state mask
6906 * State specified by the mask.
6908 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
6912 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6914 lvItem.iItem = nItem;
6915 lvItem.iSubItem = 0;
6916 lvItem.mask = LVIF_STATE;
6917 lvItem.stateMask = uMask;
6918 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6920 return lvItem.state & uMask;
6925 * Retrieves the text of a listview control item or subitem.
6928 * [I] hwnd : window handle
6929 * [I] nItem : item index
6930 * [IO] lpLVItem : item information
6931 * [I] isW : TRUE if lpLVItem is Unicode
6934 * SUCCESS : string length
6937 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6939 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6941 lpLVItem->mask = LVIF_TEXT;
6942 lpLVItem->iItem = nItem;
6943 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
6945 return textlenT(lpLVItem->pszText, isW);
6950 * Searches for an item based on properties + relationships.
6953 * [I] infoPtr : valid pointer to the listview structure
6954 * [I] nItem : item index
6955 * [I] uFlags : relationship flag
6958 * SUCCESS : item index
6961 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
6964 LVFINDINFOW lvFindInfo;
6965 INT nCountPerColumn;
6969 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
6970 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
6972 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6974 if (uFlags & LVNI_CUT)
6977 if (uFlags & LVNI_DROPHILITED)
6978 uMask |= LVIS_DROPHILITED;
6980 if (uFlags & LVNI_FOCUSED)
6981 uMask |= LVIS_FOCUSED;
6983 if (uFlags & LVNI_SELECTED)
6984 uMask |= LVIS_SELECTED;
6986 /* if we're asked for the focused item, that's only one,
6987 * so it's worth optimizing */
6988 if (uFlags & LVNI_FOCUSED)
6990 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
6991 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
6994 if (uFlags & LVNI_ABOVE)
6996 if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
7001 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7007 /* Special case for autoarrange - move 'til the top of a list */
7008 if (is_autoarrange(infoPtr))
7010 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
7011 while (nItem - nCountPerRow >= 0)
7013 nItem -= nCountPerRow;
7014 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7019 lvFindInfo.flags = LVFI_NEARESTXY;
7020 lvFindInfo.vkDirection = VK_UP;
7021 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
7022 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
7024 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7029 else if (uFlags & LVNI_BELOW)
7031 if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
7033 while (nItem < infoPtr->nItemCount)
7036 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7042 /* Special case for autoarrange - move 'til the bottom of a list */
7043 if (is_autoarrange(infoPtr))
7045 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
7046 while (nItem + nCountPerRow < infoPtr->nItemCount )
7048 nItem += nCountPerRow;
7049 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7054 lvFindInfo.flags = LVFI_NEARESTXY;
7055 lvFindInfo.vkDirection = VK_DOWN;
7056 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
7057 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
7059 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7064 else if (uFlags & LVNI_TOLEFT)
7066 if (infoPtr->uView == LV_VIEW_LIST)
7068 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
7069 while (nItem - nCountPerColumn >= 0)
7071 nItem -= nCountPerColumn;
7072 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7076 else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
7078 /* Special case for autoarrange - move 'til the beginning of a row */
7079 if (is_autoarrange(infoPtr))
7081 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
7082 while (nItem % nCountPerRow > 0)
7085 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7090 lvFindInfo.flags = LVFI_NEARESTXY;
7091 lvFindInfo.vkDirection = VK_LEFT;
7092 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
7093 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
7095 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7100 else if (uFlags & LVNI_TORIGHT)
7102 if (infoPtr->uView == LV_VIEW_LIST)
7104 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
7105 while (nItem + nCountPerColumn < infoPtr->nItemCount)
7107 nItem += nCountPerColumn;
7108 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7112 else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
7114 /* Special case for autoarrange - move 'til the end of a row */
7115 if (is_autoarrange(infoPtr))
7117 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
7118 while (nItem % nCountPerRow < nCountPerRow - 1 )
7121 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7126 lvFindInfo.flags = LVFI_NEARESTXY;
7127 lvFindInfo.vkDirection = VK_RIGHT;
7128 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
7129 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
7131 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7140 /* search by index */
7141 for (i = nItem; i < infoPtr->nItemCount; i++)
7143 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
7151 /* LISTVIEW_GetNumberOfWorkAreas */
7155 * Retrieves the origin coordinates when in icon or small icon display mode.
7158 * [I] infoPtr : valid pointer to the listview structure
7159 * [O] lpptOrigin : coordinate information
7164 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
7166 INT nHorzPos = 0, nVertPos = 0;
7167 SCROLLINFO scrollInfo;
7169 scrollInfo.cbSize = sizeof(SCROLLINFO);
7170 scrollInfo.fMask = SIF_POS;
7172 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
7173 nHorzPos = scrollInfo.nPos;
7174 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7175 nVertPos = scrollInfo.nPos;
7177 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
7179 lpptOrigin->x = infoPtr->rcList.left;
7180 lpptOrigin->y = infoPtr->rcList.top;
7181 if (infoPtr->uView == LV_VIEW_LIST)
7182 nHorzPos *= infoPtr->nItemWidth;
7183 else if (infoPtr->uView == LV_VIEW_DETAILS)
7184 nVertPos *= infoPtr->nItemHeight;
7186 lpptOrigin->x -= nHorzPos;
7187 lpptOrigin->y -= nVertPos;
7189 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
7194 * Retrieves the width of a string.
7197 * [I] hwnd : window handle
7198 * [I] lpszText : text string to process
7199 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
7202 * SUCCESS : string width (in pixels)
7205 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
7210 if (is_textT(lpszText, isW))
7212 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
7213 HDC hdc = GetDC(infoPtr->hwndSelf);
7214 HFONT hOldFont = SelectObject(hdc, hFont);
7217 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
7219 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
7220 SelectObject(hdc, hOldFont);
7221 ReleaseDC(infoPtr->hwndSelf, hdc);
7223 return stringSize.cx;
7228 * Determines which listview item is located at the specified position.
7231 * [I] infoPtr : valid pointer to the listview structure
7232 * [IO] lpht : hit test information
7233 * [I] subitem : fill out iSubItem.
7234 * [I] select : return the index only if the hit selects the item
7237 * (mm 20001022): We must not allow iSubItem to be touched, for
7238 * an app might pass only a structure with space up to iItem!
7239 * (MS Office 97 does that for instance in the file open dialog)
7242 * SUCCESS : item index
7245 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
7247 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
7248 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
7249 POINT Origin, Position, opt;
7254 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
7258 if (subitem) lpht->iSubItem = 0;
7260 LISTVIEW_GetOrigin(infoPtr, &Origin);
7262 /* set whole list relation flags */
7263 if (subitem && infoPtr->uView == LV_VIEW_DETAILS)
7265 /* LVM_SUBITEMHITTEST checks left bound of possible client area */
7266 if (infoPtr->rcList.left > lpht->pt.x && Origin.x < lpht->pt.x)
7267 lpht->flags |= LVHT_TOLEFT;
7269 if (lpht->pt.y < infoPtr->rcList.top && lpht->pt.y >= 0)
7270 opt.y = lpht->pt.y + infoPtr->rcList.top;
7274 if (infoPtr->rcList.bottom < opt.y)
7275 lpht->flags |= LVHT_BELOW;
7279 if (infoPtr->rcList.left > lpht->pt.x)
7280 lpht->flags |= LVHT_TOLEFT;
7281 else if (infoPtr->rcList.right < lpht->pt.x)
7282 lpht->flags |= LVHT_TORIGHT;
7284 if (infoPtr->rcList.top > lpht->pt.y)
7285 lpht->flags |= LVHT_ABOVE;
7286 else if (infoPtr->rcList.bottom < lpht->pt.y)
7287 lpht->flags |= LVHT_BELOW;
7290 /* even if item is invalid try to find subitem */
7291 if (infoPtr->uView == LV_VIEW_DETAILS && subitem)
7296 opt.x = lpht->pt.x - Origin.x;
7298 lpht->iSubItem = -1;
7299 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
7301 pRect = &LISTVIEW_GetColumnInfo(infoPtr, j)->rcHeader;
7303 if ((opt.x >= pRect->left) && (opt.x < pRect->right))
7309 TRACE("lpht->iSubItem=%d\n", lpht->iSubItem);
7312 TRACE("lpht->flags=0x%x\n", lpht->flags);
7313 if (lpht->flags) return -1;
7315 lpht->flags |= LVHT_NOWHERE;
7317 /* first deal with the large items */
7318 rcSearch.left = lpht->pt.x;
7319 rcSearch.top = lpht->pt.y;
7320 rcSearch.right = rcSearch.left + 1;
7321 rcSearch.bottom = rcSearch.top + 1;
7323 iterator_frameditems(&i, infoPtr, &rcSearch);
7324 iterator_next(&i); /* go to first item in the sequence */
7326 iterator_destroy(&i);
7328 TRACE("lpht->iItem=%d\n", iItem);
7329 if (iItem == -1) return -1;
7331 lvItem.mask = LVIF_STATE | LVIF_TEXT;
7332 if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
7333 lvItem.stateMask = LVIS_STATEIMAGEMASK;
7334 if (infoPtr->uView == LV_VIEW_ICON) lvItem.stateMask |= LVIS_FOCUSED;
7335 lvItem.iItem = iItem;
7336 lvItem.iSubItem = subitem ? lpht->iSubItem : 0;
7337 lvItem.pszText = szDispText;
7338 lvItem.cchTextMax = DISP_TEXT_SIZE;
7339 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
7340 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
7342 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
7343 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
7344 opt.x = lpht->pt.x - Position.x - Origin.x;
7346 if (lpht->pt.y < infoPtr->rcList.top && lpht->pt.y >= 0)
7347 opt.y = lpht->pt.y - Position.y - Origin.y + infoPtr->rcList.top;
7349 opt.y = lpht->pt.y - Position.y - Origin.y;
7351 if (infoPtr->uView == LV_VIEW_DETAILS)
7354 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
7355 opt.x = lpht->pt.x - Origin.x;
7359 UnionRect(&rcBounds, &rcIcon, &rcLabel);
7360 UnionRect(&rcBounds, &rcBounds, &rcState);
7362 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
7363 if (!PtInRect(&rcBounds, opt)) return -1;
7365 if (PtInRect(&rcIcon, opt))
7366 lpht->flags |= LVHT_ONITEMICON;
7367 else if (PtInRect(&rcLabel, opt))
7368 lpht->flags |= LVHT_ONITEMLABEL;
7369 else if (infoPtr->himlState && PtInRect(&rcState, opt))
7370 lpht->flags |= LVHT_ONITEMSTATEICON;
7371 /* special case for LVS_EX_FULLROWSELECT */
7372 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT &&
7373 !(lpht->flags & LVHT_ONITEM))
7375 lpht->flags = LVHT_ONITEM | LVHT_ABOVE;
7377 if (lpht->flags & LVHT_ONITEM)
7378 lpht->flags &= ~LVHT_NOWHERE;
7379 TRACE("lpht->flags=0x%x\n", lpht->flags);
7381 if (select && !(infoPtr->uView == LV_VIEW_DETAILS &&
7382 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
7383 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
7385 if (infoPtr->uView == LV_VIEW_DETAILS)
7387 /* get main item bounds */
7388 lvItem.iSubItem = 0;
7389 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
7390 UnionRect(&rcBounds, &rcIcon, &rcLabel);
7391 UnionRect(&rcBounds, &rcBounds, &rcState);
7393 if (!PtInRect(&rcBounds, opt)) iItem = -1;
7395 return lpht->iItem = iItem;
7400 * Inserts a new item in the listview control.
7403 * [I] infoPtr : valid pointer to the listview structure
7404 * [I] lpLVItem : item information
7405 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
7408 * SUCCESS : new item index
7411 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
7418 BOOL is_sorted, has_changed;
7420 HWND hwndSelf = infoPtr->hwndSelf;
7422 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
7424 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
7426 /* make sure it's an item, and not a subitem; cannot insert a subitem */
7427 if (!lpLVItem || lpLVItem->iSubItem) return -1;
7429 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
7431 if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
7433 /* insert item in listview control data structure */
7434 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
7435 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
7437 /* link with id struct */
7438 if (!(lpID = Alloc(sizeof(ITEM_ID)))) goto fail;
7440 lpID->item = hdpaSubItems;
7441 lpID->id = get_next_itemid(infoPtr);
7442 if ( DPA_InsertPtr(infoPtr->hdpaItemIds, infoPtr->nItemCount, lpID) == -1) goto fail;
7444 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
7445 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
7447 if (lpLVItem->iItem < 0 && !is_sorted) return -1;
7449 /* calculate new item index */
7456 while (i < infoPtr->nItemCount)
7458 hItem = DPA_GetPtr( infoPtr->hdpaItems, i);
7459 item_s = (ITEM_INFO*)DPA_GetPtr(hItem, 0);
7461 cmpv = textcmpWT(item_s->hdr.pszText, lpLVItem->pszText, isW);
7462 if (infoPtr->dwStyle & LVS_SORTDESCENDING) cmpv *= -1;
7464 if (cmpv >= 0) break;
7470 nItem = min(lpLVItem->iItem, infoPtr->nItemCount);
7472 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
7473 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
7474 if (nItem == -1) goto fail;
7475 if (infoPtr->nItemCount++ == 0) LISTVIEW_UpdateItemSize(infoPtr);
7477 /* shift indices first so they don't get tangled */
7478 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
7480 /* set the item attributes */
7481 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
7483 /* full size structure expected - _WIN32IE >= 0x560 */
7486 else if (lpLVItem->mask & LVIF_INDENT)
7488 /* indent member expected - _WIN32IE >= 0x300 */
7489 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
7493 /* minimal structure expected */
7494 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
7497 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7499 item.mask |= LVIF_STATE;
7500 item.stateMask |= LVIS_STATEIMAGEMASK;
7501 item.state &= ~LVIS_STATEIMAGEMASK;
7502 item.state |= INDEXTOSTATEIMAGEMASK(1);
7504 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
7506 /* make room for the position, if we are in the right mode */
7507 if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
7509 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
7511 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
7513 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
7518 /* send LVN_INSERTITEM notification */
7519 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7521 nmlv.lParam = lpItem->lParam;
7522 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
7523 if (!IsWindow(hwndSelf))
7526 /* align items (set position of each item) */
7527 if (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON)
7531 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
7532 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
7534 LISTVIEW_NextIconPosTop(infoPtr, &pt);
7536 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
7539 /* now is the invalidation fun */
7540 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
7544 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
7545 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
7546 infoPtr->nItemCount--;
7548 DPA_DeletePtr(hdpaSubItems, 0);
7549 DPA_Destroy (hdpaSubItems);
7556 * Checks item visibility.
7559 * [I] infoPtr : valid pointer to the listview structure
7560 * [I] nFirst : item index to check for
7563 * Item visible : TRUE
7564 * Item invisible or failure : FALSE
7566 static BOOL LISTVIEW_IsItemVisible(const LISTVIEW_INFO *infoPtr, INT nItem)
7568 POINT Origin, Position;
7573 TRACE("nItem=%d\n", nItem);
7575 if (nItem < 0 || nItem >= DPA_GetPtrCount(infoPtr->hdpaItems)) return FALSE;
7577 LISTVIEW_GetOrigin(infoPtr, &Origin);
7578 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
7579 rcItem.left = Position.x + Origin.x;
7580 rcItem.top = Position.y + Origin.y;
7581 rcItem.right = rcItem.left + infoPtr->nItemWidth;
7582 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
7584 hdc = GetDC(infoPtr->hwndSelf);
7585 if (!hdc) return FALSE;
7586 ret = RectVisible(hdc, &rcItem);
7587 ReleaseDC(infoPtr->hwndSelf, hdc);
7594 * Redraws a range of items.
7597 * [I] infoPtr : valid pointer to the listview structure
7598 * [I] nFirst : first item
7599 * [I] nLast : last item
7605 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
7609 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
7610 max(nFirst, nLast) >= infoPtr->nItemCount)
7613 for (i = nFirst; i <= nLast; i++)
7614 LISTVIEW_InvalidateItem(infoPtr, i);
7621 * Scroll the content of a listview.
7624 * [I] infoPtr : valid pointer to the listview structure
7625 * [I] dx : horizontal scroll amount in pixels
7626 * [I] dy : vertical scroll amount in pixels
7633 * If the control is in report view (LV_VIEW_DETAILS) the control can
7634 * be scrolled only in line increments. "dy" will be rounded to the
7635 * nearest number of pixels that are a whole line. Ex: if line height
7636 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
7637 * is passed, then the scroll will be 0. (per MSDN 7/2002)
7639 * For: (per experimentation with native control and CSpy ListView)
7640 * LV_VIEW_ICON dy=1 = 1 pixel (vertical only)
7642 * LV_VIEW_SMALLICON dy=1 = 1 pixel (vertical only)
7644 * LV_VIEW_LIST dx=1 = 1 column (horizontal only)
7645 * but will only scroll 1 column per message
7646 * no matter what the value.
7647 * dy must be 0 or FALSE returned.
7648 * LV_VIEW_DETAILS dx=1 = 1 pixel
7652 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7654 switch(infoPtr->uView) {
7655 case LV_VIEW_DETAILS:
7656 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
7657 dy /= infoPtr->nItemHeight;
7660 if (dy != 0) return FALSE;
7667 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
7668 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
7675 * Sets the background color.
7678 * [I] infoPtr : valid pointer to the listview structure
7679 * [I] clrBk : background color
7685 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
7687 TRACE("(clrBk=%x)\n", clrBk);
7689 if(infoPtr->clrBk != clrBk) {
7690 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7691 infoPtr->clrBk = clrBk;
7692 if (clrBk == CLR_NONE)
7693 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
7696 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
7697 infoPtr->dwLvExStyle &= ~LVS_EX_TRANSPARENTBKGND;
7699 LISTVIEW_InvalidateList(infoPtr);
7705 /* LISTVIEW_SetBkImage */
7707 /*** Helper for {Insert,Set}ColumnT *only* */
7708 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
7709 const LVCOLUMNW *lpColumn, BOOL isW)
7711 if (lpColumn->mask & LVCF_FMT)
7713 /* format member is valid */
7714 lphdi->mask |= HDI_FORMAT;
7716 /* set text alignment (leftmost column must be left-aligned) */
7717 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
7718 lphdi->fmt |= HDF_LEFT;
7719 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
7720 lphdi->fmt |= HDF_RIGHT;
7721 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
7722 lphdi->fmt |= HDF_CENTER;
7724 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
7725 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
7727 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
7729 lphdi->fmt |= HDF_IMAGE;
7730 lphdi->iImage = I_IMAGECALLBACK;
7733 if (lpColumn->fmt & LVCFMT_FIXED_WIDTH)
7734 lphdi->fmt |= HDF_FIXEDWIDTH;
7737 if (lpColumn->mask & LVCF_WIDTH)
7739 lphdi->mask |= HDI_WIDTH;
7740 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
7742 /* make it fill the remainder of the controls width */
7746 for(item_index = 0; item_index < (nColumn - 1); item_index++)
7748 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
7749 lphdi->cxy += rcHeader.right - rcHeader.left;
7752 /* retrieve the layout of the header */
7753 GetClientRect(infoPtr->hwndSelf, &rcHeader);
7754 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
7756 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
7759 lphdi->cxy = lpColumn->cx;
7762 if (lpColumn->mask & LVCF_TEXT)
7764 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
7765 lphdi->fmt |= HDF_STRING;
7766 lphdi->pszText = lpColumn->pszText;
7767 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
7770 if (lpColumn->mask & LVCF_IMAGE)
7772 lphdi->mask |= HDI_IMAGE;
7773 lphdi->iImage = lpColumn->iImage;
7776 if (lpColumn->mask & LVCF_ORDER)
7778 lphdi->mask |= HDI_ORDER;
7779 lphdi->iOrder = lpColumn->iOrder;
7786 * Inserts a new column.
7789 * [I] infoPtr : valid pointer to the listview structure
7790 * [I] nColumn : column index
7791 * [I] lpColumn : column information
7792 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
7795 * SUCCESS : new column index
7798 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
7799 const LVCOLUMNW *lpColumn, BOOL isW)
7801 COLUMN_INFO *lpColumnInfo;
7805 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
7807 if (!lpColumn || nColumn < 0) return -1;
7808 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
7810 ZeroMemory(&hdi, sizeof(HDITEMW));
7811 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
7814 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
7815 * (can be seen in SPY) otherwise column never gets added.
7817 if (!(lpColumn->mask & LVCF_WIDTH)) {
7818 hdi.mask |= HDI_WIDTH;
7823 * when the iSubItem is available Windows copies it to the header lParam. It seems
7824 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
7826 if (lpColumn->mask & LVCF_SUBITEM)
7828 hdi.mask |= HDI_LPARAM;
7829 hdi.lParam = lpColumn->iSubItem;
7832 /* create header if not present */
7833 LISTVIEW_CreateHeader(infoPtr);
7834 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle) &&
7835 (infoPtr->uView == LV_VIEW_DETAILS) && (WS_VISIBLE & infoPtr->dwStyle))
7837 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7840 /* insert item in header control */
7841 nNewColumn = SendMessageW(infoPtr->hwndHeader,
7842 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
7843 (WPARAM)nColumn, (LPARAM)&hdi);
7844 if (nNewColumn == -1) return -1;
7845 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
7847 /* create our own column info */
7848 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
7849 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
7851 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
7852 if (lpColumn->mask & LVCF_MINWIDTH) lpColumnInfo->cxMin = lpColumn->cxMin;
7853 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, nNewColumn, (LPARAM)&lpColumnInfo->rcHeader))
7856 /* now we have to actually adjust the data */
7857 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
7859 SUBITEM_INFO *lpSubItem;
7863 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
7865 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
7866 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
7868 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
7869 if (lpSubItem->iSubItem >= nNewColumn)
7870 lpSubItem->iSubItem++;
7875 /* make space for the new column */
7876 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7877 LISTVIEW_UpdateItemSize(infoPtr);
7882 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
7885 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
7893 * Sets the attributes of a header item.
7896 * [I] infoPtr : valid pointer to the listview structure
7897 * [I] nColumn : column index
7898 * [I] lpColumn : column attributes
7899 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
7905 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
7906 const LVCOLUMNW *lpColumn, BOOL isW)
7908 HDITEMW hdi, hdiget;
7911 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
7913 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7915 ZeroMemory(&hdi, sizeof(HDITEMW));
7916 if (lpColumn->mask & LVCF_FMT)
7918 hdi.mask |= HDI_FORMAT;
7919 hdiget.mask = HDI_FORMAT;
7920 if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdiget))
7921 hdi.fmt = hdiget.fmt & HDF_STRING;
7923 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
7925 /* set header item attributes */
7926 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
7927 if (!bResult) return FALSE;
7929 if (lpColumn->mask & LVCF_FMT)
7931 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
7932 INT oldFmt = lpColumnInfo->fmt;
7934 lpColumnInfo->fmt = lpColumn->fmt;
7935 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
7937 if (infoPtr->uView == LV_VIEW_DETAILS) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
7941 if (lpColumn->mask & LVCF_MINWIDTH)
7942 LISTVIEW_GetColumnInfo(infoPtr, nColumn)->cxMin = lpColumn->cxMin;
7949 * Sets the column order array
7952 * [I] infoPtr : valid pointer to the listview structure
7953 * [I] iCount : number of elements in column order array
7954 * [I] lpiArray : pointer to column order array
7960 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
7962 TRACE("iCount %d lpiArray %p\n", iCount, lpiArray);
7964 if (!lpiArray || !IsWindow(infoPtr->hwndHeader)) return FALSE;
7966 infoPtr->colRectsDirty = TRUE;
7968 return SendMessageW(infoPtr->hwndHeader, HDM_SETORDERARRAY, iCount, (LPARAM)lpiArray);
7973 * Sets the width of a column
7976 * [I] infoPtr : valid pointer to the listview structure
7977 * [I] nColumn : column index
7978 * [I] cx : column width
7984 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
7986 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
7990 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
7992 /* set column width only if in report or list mode */
7993 if (infoPtr->uView != LV_VIEW_DETAILS && infoPtr->uView != LV_VIEW_LIST) return FALSE;
7995 /* take care of invalid cx values */
7996 if(infoPtr->uView == LV_VIEW_DETAILS && cx < -2) cx = LVSCW_AUTOSIZE;
7997 else if (infoPtr->uView == LV_VIEW_LIST && cx < 1) return FALSE;
7999 /* resize all columns if in LV_VIEW_LIST mode */
8000 if(infoPtr->uView == LV_VIEW_LIST)
8002 infoPtr->nItemWidth = cx;
8003 LISTVIEW_InvalidateList(infoPtr);
8007 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
8009 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
8014 lvItem.mask = LVIF_TEXT;
8016 lvItem.iSubItem = nColumn;
8017 lvItem.pszText = szDispText;
8018 lvItem.cchTextMax = DISP_TEXT_SIZE;
8019 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
8021 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
8022 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
8023 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
8025 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
8026 max_cx += infoPtr->iconSize.cx;
8027 max_cx += TRAILING_LABEL_PADDING;
8030 /* autosize based on listview items width */
8031 if(cx == LVSCW_AUTOSIZE)
8033 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
8035 /* if iCol is the last column make it fill the remainder of the controls width */
8036 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
8041 LISTVIEW_GetOrigin(infoPtr, &Origin);
8042 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
8044 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
8048 /* Despite what the MS docs say, if this is not the last
8049 column, then MS resizes the column to the width of the
8050 largest text string in the column, including headers
8051 and items. This is different from LVSCW_AUTOSIZE in that
8052 LVSCW_AUTOSIZE ignores the header string length. */
8055 /* retrieve header text */
8056 hdi.mask = HDI_TEXT;
8057 hdi.cchTextMax = DISP_TEXT_SIZE;
8058 hdi.pszText = szDispText;
8059 if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdi))
8061 HDC hdc = GetDC(infoPtr->hwndSelf);
8062 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
8065 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
8066 cx = size.cx + TRAILING_HEADER_PADDING;
8067 /* FIXME: Take into account the header image, if one is present */
8068 SelectObject(hdc, old_font);
8069 ReleaseDC(infoPtr->hwndSelf, hdc);
8071 cx = max (cx, max_cx);
8075 if (cx < 0) return FALSE;
8077 /* call header to update the column change */
8078 hdi.mask = HDI_WIDTH;
8079 hdi.cxy = max(cx, LISTVIEW_GetColumnInfo(infoPtr, nColumn)->cxMin);
8080 TRACE("hdi.cxy=%d\n", hdi.cxy);
8081 return SendMessageW(infoPtr->hwndHeader, HDM_SETITEMW, nColumn, (LPARAM)&hdi);
8085 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
8088 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
8091 HBITMAP hbm_im, hbm_mask, hbm_orig;
8093 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
8094 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
8097 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
8098 ILC_COLOR | ILC_MASK, 2, 2);
8099 hdc_wnd = GetDC(infoPtr->hwndSelf);
8100 hdc = CreateCompatibleDC(hdc_wnd);
8101 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
8102 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
8103 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
8105 rc.left = rc.top = 0;
8106 rc.right = GetSystemMetrics(SM_CXSMICON);
8107 rc.bottom = GetSystemMetrics(SM_CYSMICON);
8109 hbm_orig = SelectObject(hdc, hbm_mask);
8110 FillRect(hdc, &rc, hbr_white);
8111 InflateRect(&rc, -2, -2);
8112 FillRect(hdc, &rc, hbr_black);
8114 SelectObject(hdc, hbm_im);
8115 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
8116 SelectObject(hdc, hbm_orig);
8117 ImageList_Add(himl, hbm_im, hbm_mask);
8119 SelectObject(hdc, hbm_im);
8120 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
8121 SelectObject(hdc, hbm_orig);
8122 ImageList_Add(himl, hbm_im, hbm_mask);
8124 DeleteObject(hbm_mask);
8125 DeleteObject(hbm_im);
8133 * Sets the extended listview style.
8136 * [I] infoPtr : valid pointer to the listview structure
8138 * [I] dwStyle : style
8141 * SUCCESS : previous style
8144 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwExStyle)
8146 DWORD dwOldExStyle = infoPtr->dwLvExStyle;
8150 infoPtr->dwLvExStyle = (dwOldExStyle & ~dwMask) | (dwExStyle & dwMask);
8152 infoPtr->dwLvExStyle = dwExStyle;
8154 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_CHECKBOXES)
8156 HIMAGELIST himl = 0;
8157 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
8160 item.mask = LVIF_STATE;
8161 item.stateMask = LVIS_STATEIMAGEMASK;
8162 item.state = INDEXTOSTATEIMAGEMASK(1);
8163 LISTVIEW_SetItemState(infoPtr, -1, &item);
8165 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
8166 if(!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8167 ImageList_Destroy(infoPtr->himlState);
8169 himl = LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
8170 /* checkbox list replaces prevous custom list or... */
8171 if(((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) &&
8172 !(infoPtr->dwStyle & LVS_SHAREIMAGELISTS)) ||
8173 /* ...previous was checkbox list */
8174 (dwOldExStyle & LVS_EX_CHECKBOXES))
8175 ImageList_Destroy(himl);
8178 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_HEADERDRAGDROP)
8182 /* if not already created */
8183 LISTVIEW_CreateHeader(infoPtr);
8185 dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
8186 if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
8187 dwStyle |= HDS_DRAGDROP;
8189 dwStyle &= ~HDS_DRAGDROP;
8190 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
8193 /* GRIDLINES adds decoration at top so changes sizes */
8194 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_GRIDLINES)
8196 LISTVIEW_UpdateSize(infoPtr);
8199 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_TRANSPARENTBKGND)
8201 if (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTBKGND)
8202 LISTVIEW_SetBkColor(infoPtr, CLR_NONE);
8205 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_HEADERINALLVIEWS)
8207 if (infoPtr->dwLvExStyle & LVS_EX_HEADERINALLVIEWS)
8208 LISTVIEW_CreateHeader(infoPtr);
8210 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8211 LISTVIEW_UpdateSize(infoPtr);
8212 LISTVIEW_UpdateScroll(infoPtr);
8215 LISTVIEW_InvalidateList(infoPtr);
8216 return dwOldExStyle;
8221 * Sets the new hot cursor used during hot tracking and hover selection.
8224 * [I] infoPtr : valid pointer to the listview structure
8225 * [I] hCursor : the new hot cursor handle
8228 * Returns the previous hot cursor
8230 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
8232 HCURSOR oldCursor = infoPtr->hHotCursor;
8234 infoPtr->hHotCursor = hCursor;
8242 * Sets the hot item index.
8245 * [I] infoPtr : valid pointer to the listview structure
8246 * [I] iIndex : index
8249 * SUCCESS : previous hot item index
8250 * FAILURE : -1 (no hot item)
8252 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
8254 INT iOldIndex = infoPtr->nHotItem;
8256 infoPtr->nHotItem = iIndex;
8264 * Sets the amount of time the cursor must hover over an item before it is selected.
8267 * [I] infoPtr : valid pointer to the listview structure
8268 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
8271 * Returns the previous hover time
8273 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
8275 DWORD oldHoverTime = infoPtr->dwHoverTime;
8277 infoPtr->dwHoverTime = dwHoverTime;
8279 return oldHoverTime;
8284 * Sets spacing for icons of LVS_ICON style.
8287 * [I] infoPtr : valid pointer to the listview structure
8288 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
8289 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
8292 * MAKELONG(oldcx, oldcy)
8294 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
8296 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
8298 TRACE("requested=(%d,%d)\n", cx, cy);
8300 /* this is supported only for LVS_ICON style */
8301 if (infoPtr->uView != LV_VIEW_ICON) return oldspacing;
8303 /* set to defaults, if instructed to */
8304 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
8305 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
8307 /* if 0 then compute width
8308 * FIXME: Should scan each item and determine max width of
8309 * icon or label, then make that the width */
8311 cx = infoPtr->iconSpacing.cx;
8313 /* if 0 then compute height */
8315 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
8316 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
8319 infoPtr->iconSpacing.cx = cx;
8320 infoPtr->iconSpacing.cy = cy;
8322 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
8323 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
8324 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
8325 infoPtr->ntmHeight);
8327 /* these depend on the iconSpacing */
8328 LISTVIEW_UpdateItemSize(infoPtr);
8333 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
8337 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
8344 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
8345 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
8354 * [I] infoPtr : valid pointer to the listview structure
8355 * [I] nType : image list type
8356 * [I] himl : image list handle
8359 * SUCCESS : old image list
8362 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
8364 INT oldHeight = infoPtr->nItemHeight;
8365 HIMAGELIST himlOld = 0;
8367 TRACE("(nType=%d, himl=%p\n", nType, himl);
8372 himlOld = infoPtr->himlNormal;
8373 infoPtr->himlNormal = himl;
8374 if (infoPtr->uView == LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
8375 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8379 himlOld = infoPtr->himlSmall;
8380 infoPtr->himlSmall = himl;
8381 if (infoPtr->uView != LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
8385 himlOld = infoPtr->himlState;
8386 infoPtr->himlState = himl;
8387 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
8388 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
8392 ERR("Unknown icon type=%d\n", nType);
8396 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
8397 if (infoPtr->nItemHeight != oldHeight)
8398 LISTVIEW_UpdateScroll(infoPtr);
8405 * Preallocates memory (does *not* set the actual count of items !)
8408 * [I] infoPtr : valid pointer to the listview structure
8409 * [I] nItems : item count (projected number of items to allocate)
8410 * [I] dwFlags : update flags
8416 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
8418 TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
8420 if (infoPtr->dwStyle & LVS_OWNERDATA)
8422 INT nOldCount = infoPtr->nItemCount;
8424 if (nItems < nOldCount)
8426 RANGE range = { nItems, nOldCount };
8427 ranges_del(infoPtr->selectionRanges, range);
8428 if (infoPtr->nFocusedItem >= nItems)
8430 LISTVIEW_SetItemFocus(infoPtr, -1);
8431 SetRectEmpty(&infoPtr->rcFocus);
8435 infoPtr->nItemCount = nItems;
8436 LISTVIEW_UpdateScroll(infoPtr);
8438 /* the flags are valid only in ownerdata report and list modes */
8439 if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON) dwFlags = 0;
8441 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
8442 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
8444 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
8445 LISTVIEW_InvalidateList(infoPtr);
8452 LISTVIEW_GetOrigin(infoPtr, &Origin);
8453 nFrom = min(nOldCount, nItems);
8454 nTo = max(nOldCount, nItems);
8456 if (infoPtr->uView == LV_VIEW_DETAILS)
8459 rcErase.top = nFrom * infoPtr->nItemHeight;
8460 rcErase.right = infoPtr->nItemWidth;
8461 rcErase.bottom = nTo * infoPtr->nItemHeight;
8462 OffsetRect(&rcErase, Origin.x, Origin.y);
8463 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
8464 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
8466 else /* LV_VIEW_LIST */
8468 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
8470 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
8471 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
8472 rcErase.right = rcErase.left + infoPtr->nItemWidth;
8473 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
8474 OffsetRect(&rcErase, Origin.x, Origin.y);
8475 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
8476 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
8478 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
8480 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
8481 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
8482 OffsetRect(&rcErase, Origin.x, Origin.y);
8483 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
8484 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
8490 /* According to MSDN for non-LVS_OWNERDATA this is just
8491 * a performance issue. The control allocates its internal
8492 * data structures for the number of items specified. It
8493 * cuts down on the number of memory allocations. Therefore
8494 * we will just issue a WARN here
8496 WARN("for non-ownerdata performance option not implemented.\n");
8504 * Sets the position of an item.
8507 * [I] infoPtr : valid pointer to the listview structure
8508 * [I] nItem : item index
8509 * [I] pt : coordinate
8515 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT *pt)
8519 TRACE("(nItem=%d, pt=%s\n", nItem, wine_dbgstr_point(pt));
8521 if (!pt || nItem < 0 || nItem >= infoPtr->nItemCount ||
8522 !(infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)) return FALSE;
8525 LISTVIEW_GetOrigin(infoPtr, &Origin);
8527 /* This point value seems to be an undocumented feature.
8528 * The best guess is that it means either at the origin,
8529 * or at true beginning of the list. I will assume the origin. */
8530 if ((Pt.x == -1) && (Pt.y == -1))
8533 if (infoPtr->uView == LV_VIEW_ICON)
8535 Pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
8536 Pt.y -= ICON_TOP_PADDING;
8541 infoPtr->bAutoarrange = FALSE;
8543 return LISTVIEW_MoveIconTo(infoPtr, nItem, &Pt, FALSE);
8548 * Sets the state of one or many items.
8551 * [I] infoPtr : valid pointer to the listview structure
8552 * [I] nItem : item index
8553 * [I] lpLVItem : item or subitem info
8559 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
8561 BOOL bResult = TRUE;
8564 lvItem.iItem = nItem;
8565 lvItem.iSubItem = 0;
8566 lvItem.mask = LVIF_STATE;
8567 lvItem.state = lpLVItem->state;
8568 lvItem.stateMask = lpLVItem->stateMask;
8569 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
8573 /* select all isn't allowed in LVS_SINGLESEL */
8574 if ((lvItem.state & lvItem.stateMask & LVIS_SELECTED) && (infoPtr->dwStyle & LVS_SINGLESEL))
8577 /* focus all isn't allowed */
8578 if (lvItem.state & lvItem.stateMask & LVIS_FOCUSED) return FALSE;
8580 /* apply to all items */
8581 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
8582 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
8585 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
8592 * Sets the text of an item or subitem.
8595 * [I] hwnd : window handle
8596 * [I] nItem : item index
8597 * [I] lpLVItem : item or subitem info
8598 * [I] isW : TRUE if input is Unicode
8604 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
8608 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
8610 lvItem.iItem = nItem;
8611 lvItem.iSubItem = lpLVItem->iSubItem;
8612 lvItem.mask = LVIF_TEXT;
8613 lvItem.pszText = lpLVItem->pszText;
8614 lvItem.cchTextMax = lpLVItem->cchTextMax;
8616 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
8618 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
8623 * Set item index that marks the start of a multiple selection.
8626 * [I] infoPtr : valid pointer to the listview structure
8627 * [I] nIndex : index
8630 * Index number or -1 if there is no selection mark.
8632 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
8634 INT nOldIndex = infoPtr->nSelectionMark;
8636 TRACE("(nIndex=%d)\n", nIndex);
8638 infoPtr->nSelectionMark = nIndex;
8645 * Sets the text background color.
8648 * [I] infoPtr : valid pointer to the listview structure
8649 * [I] clrTextBk : text background color
8655 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
8657 TRACE("(clrTextBk=%x)\n", clrTextBk);
8659 if (infoPtr->clrTextBk != clrTextBk)
8661 infoPtr->clrTextBk = clrTextBk;
8662 LISTVIEW_InvalidateList(infoPtr);
8670 * Sets the text foreground color.
8673 * [I] infoPtr : valid pointer to the listview structure
8674 * [I] clrText : text color
8680 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
8682 TRACE("(clrText=%x)\n", clrText);
8684 if (infoPtr->clrText != clrText)
8686 infoPtr->clrText = clrText;
8687 LISTVIEW_InvalidateList(infoPtr);
8695 * Sets new ToolTip window to ListView control.
8698 * [I] infoPtr : valid pointer to the listview structure
8699 * [I] hwndNewToolTip : handle to new ToolTip
8704 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
8706 HWND hwndOldToolTip = infoPtr->hwndToolTip;
8707 infoPtr->hwndToolTip = hwndNewToolTip;
8708 return hwndOldToolTip;
8713 * sets the Unicode character format flag for the control
8715 * [I] infoPtr :valid pointer to the listview structure
8716 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
8719 * Old Unicode Format
8721 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL fUnicode)
8723 SHORT rc = infoPtr->notifyFormat;
8724 infoPtr->notifyFormat = (fUnicode) ? NFR_UNICODE : NFR_ANSI;
8725 return rc == NFR_UNICODE;
8730 * sets the control view mode
8732 * [I] infoPtr :valid pointer to the listview structure
8733 * [I] nView :new view mode value
8739 static INT LISTVIEW_SetView(LISTVIEW_INFO *infoPtr, DWORD nView)
8741 SIZE oldIconSize = infoPtr->iconSize;
8744 if (infoPtr->uView == nView) return 1;
8746 if ((INT)nView < 0 || nView > LV_VIEW_MAX) return -1;
8747 if (nView == LV_VIEW_TILE)
8749 FIXME("View LV_VIEW_TILE unimplemented\n");
8753 infoPtr->uView = nView;
8755 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8756 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8758 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8759 SetRectEmpty(&infoPtr->rcFocus);
8761 himl = (nView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8762 set_icon_size(&infoPtr->iconSize, himl, nView != LV_VIEW_ICON);
8767 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8769 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
8770 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8771 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8773 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8775 case LV_VIEW_SMALLICON:
8776 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8778 case LV_VIEW_DETAILS:
8783 LISTVIEW_CreateHeader( infoPtr );
8785 hl.prc = &infoPtr->rcList;
8787 SendMessageW(infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl);
8788 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
8789 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
8796 LISTVIEW_UpdateItemSize(infoPtr);
8797 LISTVIEW_UpdateSize(infoPtr);
8798 LISTVIEW_UpdateScroll(infoPtr);
8799 LISTVIEW_InvalidateList(infoPtr);
8801 TRACE("nView=%d\n", nView);
8806 /* LISTVIEW_SetWorkAreas */
8810 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMS
8813 * [I] first : pointer to first ITEM_INFO to compare
8814 * [I] second : pointer to second ITEM_INFO to compare
8815 * [I] lParam : HWND of control
8818 * if first comes before second : negative
8819 * if first comes after second : positive
8820 * if first and second are equivalent : zero
8822 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
8824 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
8825 ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
8826 ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
8828 /* Forward the call to the client defined callback */
8829 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
8834 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMSEX
8837 * [I] first : pointer to first ITEM_INFO to compare
8838 * [I] second : pointer to second ITEM_INFO to compare
8839 * [I] lParam : HWND of control
8842 * if first comes before second : negative
8843 * if first comes after second : positive
8844 * if first and second are equivalent : zero
8846 static INT WINAPI LISTVIEW_CallBackCompareEx(LPVOID first, LPVOID second, LPARAM lParam)
8848 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
8849 INT first_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, first );
8850 INT second_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, second );
8852 /* Forward the call to the client defined callback */
8853 return (infoPtr->pfnCompare)( first_idx, second_idx, infoPtr->lParamSort );
8858 * Sorts the listview items.
8861 * [I] infoPtr : valid pointer to the listview structure
8862 * [I] pfnCompare : application-defined value
8863 * [I] lParamSort : pointer to comparison callback
8864 * [I] IsEx : TRUE when LVM_SORTITEMSEX used
8870 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare,
8871 LPARAM lParamSort, BOOL IsEx)
8875 LPVOID selectionMarkItem = NULL;
8876 LPVOID focusedItem = NULL;
8879 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
8881 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
8883 if (!pfnCompare) return FALSE;
8884 if (!infoPtr->hdpaItems) return FALSE;
8886 /* if there are 0 or 1 items, there is no need to sort */
8887 if (infoPtr->nItemCount < 2) return TRUE;
8889 /* clear selection */
8890 ranges_clear(infoPtr->selectionRanges);
8892 /* save selection mark and focused item */
8893 if (infoPtr->nSelectionMark >= 0)
8894 selectionMarkItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark);
8895 if (infoPtr->nFocusedItem >= 0)
8896 focusedItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
8898 infoPtr->pfnCompare = pfnCompare;
8899 infoPtr->lParamSort = lParamSort;
8901 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompareEx, (LPARAM)infoPtr);
8903 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
8905 /* restore selection ranges */
8906 for (i=0; i < infoPtr->nItemCount; i++)
8908 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
8909 lpItem = DPA_GetPtr(hdpaSubItems, 0);
8911 if (lpItem->state & LVIS_SELECTED)
8912 ranges_additem(infoPtr->selectionRanges, i);
8914 /* restore selection mark and focused item */
8915 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
8916 infoPtr->nFocusedItem = DPA_GetPtrIndex(infoPtr->hdpaItems, focusedItem);
8918 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
8920 /* refresh the display */
8921 if (infoPtr->uView != LV_VIEW_ICON && infoPtr->uView != LV_VIEW_SMALLICON)
8922 LISTVIEW_InvalidateList(infoPtr);
8929 * Update theme handle after a theme change.
8932 * [I] infoPtr : valid pointer to the listview structure
8936 * FAILURE : something else
8938 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr)
8940 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8941 CloseThemeData(theme);
8942 OpenThemeData(infoPtr->hwndSelf, themeClass);
8948 * Updates an items or rearranges the listview control.
8951 * [I] infoPtr : valid pointer to the listview structure
8952 * [I] nItem : item index
8958 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
8960 TRACE("(nItem=%d)\n", nItem);
8962 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
8964 /* rearrange with default alignment style */
8965 if (is_autoarrange(infoPtr))
8966 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8968 LISTVIEW_InvalidateItem(infoPtr, nItem);
8975 * Draw the track line at the place defined in the infoPtr structure.
8976 * The line is drawn with a XOR pen so drawing the line for the second time
8977 * in the same place erases the line.
8980 * [I] infoPtr : valid pointer to the listview structure
8986 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
8992 if (infoPtr->xTrackLine == -1)
8995 if (!(hdc = GetDC(infoPtr->hwndSelf)))
8997 hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
8998 oldROP = SetROP2(hdc, R2_XORPEN);
8999 MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
9000 LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
9001 SetROP2(hdc, oldROP);
9002 SelectObject(hdc, hOldPen);
9003 ReleaseDC(infoPtr->hwndSelf, hdc);
9009 * Called when an edit control should be displayed. This function is called after
9010 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
9013 * [I] hwnd : Handle to the listview
9014 * [I] uMsg : WM_TIMER (ignored)
9015 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
9016 * [I] dwTimer : The elapsed time (ignored)
9021 static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
9023 DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
9024 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9026 KillTimer(hwnd, idEvent);
9027 editItem->fEnabled = FALSE;
9028 /* check if the item is still selected */
9029 if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
9030 LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
9035 * Creates the listview control - the WM_NCCREATE phase.
9038 * [I] hwnd : window handle
9039 * [I] lpcs : the create parameters
9045 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
9047 LISTVIEW_INFO *infoPtr;
9050 TRACE("(lpcs=%p)\n", lpcs);
9052 /* initialize info pointer */
9053 infoPtr = Alloc(sizeof(LISTVIEW_INFO));
9054 if (!infoPtr) return FALSE;
9056 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
9058 infoPtr->hwndSelf = hwnd;
9059 infoPtr->dwStyle = lpcs->style; /* Note: may be changed in WM_CREATE */
9060 map_style_view(infoPtr);
9061 /* determine the type of structures to use */
9062 infoPtr->hwndNotify = lpcs->hwndParent;
9063 /* infoPtr->notifyFormat will be filled in WM_CREATE */
9065 /* initialize color information */
9066 infoPtr->clrBk = CLR_NONE;
9067 infoPtr->clrText = CLR_DEFAULT;
9068 infoPtr->clrTextBk = CLR_DEFAULT;
9069 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
9071 /* set default values */
9072 infoPtr->nFocusedItem = -1;
9073 infoPtr->nSelectionMark = -1;
9074 infoPtr->nHotItem = -1;
9075 infoPtr->bRedraw = TRUE;
9076 infoPtr->bNoItemMetrics = TRUE;
9077 infoPtr->bDoChangeNotify = TRUE;
9078 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
9079 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
9080 infoPtr->nEditLabelItem = -1;
9081 infoPtr->nLButtonDownItem = -1;
9082 infoPtr->dwHoverTime = -1; /* default system hover time */
9083 infoPtr->nMeasureItemHeight = 0;
9084 infoPtr->xTrackLine = -1; /* no track line */
9085 infoPtr->itemEdit.fEnabled = FALSE;
9086 infoPtr->iVersion = COMCTL32_VERSION;
9087 infoPtr->colRectsDirty = FALSE;
9089 /* get default font (icon title) */
9090 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
9091 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
9092 infoPtr->hFont = infoPtr->hDefaultFont;
9093 LISTVIEW_SaveTextMetrics(infoPtr);
9095 /* allocate memory for the data structure */
9096 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
9097 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
9098 if (!(infoPtr->hdpaItemIds = DPA_Create(10))) goto fail;
9099 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
9100 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
9101 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
9105 DestroyWindow(infoPtr->hwndHeader);
9106 ranges_destroy(infoPtr->selectionRanges);
9107 DPA_Destroy(infoPtr->hdpaItems);
9108 DPA_Destroy(infoPtr->hdpaItemIds);
9109 DPA_Destroy(infoPtr->hdpaPosX);
9110 DPA_Destroy(infoPtr->hdpaPosY);
9111 DPA_Destroy(infoPtr->hdpaColumns);
9118 * Creates the listview control - the WM_CREATE phase. Most of the data is
9119 * already set up in LISTVIEW_NCCreate
9122 * [I] hwnd : window handle
9123 * [I] lpcs : the create parameters
9129 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
9131 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9133 TRACE("(lpcs=%p)\n", lpcs);
9135 infoPtr->dwStyle = lpcs->style;
9136 map_style_view(infoPtr);
9138 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
9139 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
9140 /* on error defaulting to ANSI notifications */
9141 if (infoPtr->notifyFormat == 0) infoPtr->notifyFormat = NFR_ANSI;
9143 if ((infoPtr->uView == LV_VIEW_DETAILS) && (lpcs->style & WS_VISIBLE))
9145 if (LISTVIEW_CreateHeader(infoPtr) < 0) return -1;
9148 infoPtr->hwndHeader = 0;
9150 /* init item size to avoid division by 0 */
9151 LISTVIEW_UpdateItemSize (infoPtr);
9153 if (infoPtr->uView == LV_VIEW_DETAILS)
9155 if (!(LVS_NOCOLUMNHEADER & lpcs->style) && (WS_VISIBLE & lpcs->style))
9157 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9159 LISTVIEW_UpdateScroll(infoPtr);
9160 /* send WM_MEASUREITEM notification */
9161 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED) notify_measureitem(infoPtr);
9164 OpenThemeData(hwnd, themeClass);
9166 /* initialize the icon sizes */
9167 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, infoPtr->uView != LV_VIEW_ICON);
9168 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
9174 * Destroys the listview control.
9177 * [I] infoPtr : valid pointer to the listview structure
9183 static LRESULT LISTVIEW_Destroy(const LISTVIEW_INFO *infoPtr)
9185 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
9186 CloseThemeData(theme);
9192 * Enables the listview control.
9195 * [I] infoPtr : valid pointer to the listview structure
9196 * [I] bEnable : specifies whether to enable or disable the window
9202 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr, BOOL bEnable)
9204 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
9205 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
9211 * Erases the background of the listview control.
9214 * [I] infoPtr : valid pointer to the listview structure
9215 * [I] hdc : device context handle
9221 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
9225 TRACE("(hdc=%p)\n", hdc);
9227 if (!GetClipBox(hdc, &rc)) return FALSE;
9229 if (infoPtr->clrBk == CLR_NONE)
9231 if (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTBKGND)
9232 return SendMessageW(infoPtr->hwndNotify, WM_PRINTCLIENT,
9233 (WPARAM)hdc, PRF_ERASEBKGND);
9235 return SendMessageW(infoPtr->hwndNotify, WM_ERASEBKGND, (WPARAM)hdc, 0);
9238 /* for double buffered controls we need to do this during refresh */
9239 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
9241 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
9247 * Helper function for LISTVIEW_[HV]Scroll *only*.
9248 * Performs vertical/horizontal scrolling by a give amount.
9251 * [I] infoPtr : valid pointer to the listview structure
9252 * [I] dx : amount of horizontal scroll
9253 * [I] dy : amount of vertical scroll
9255 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
9257 /* now we can scroll the list */
9258 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
9259 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
9260 /* if we have focus, adjust rect */
9261 OffsetRect(&infoPtr->rcFocus, dx, dy);
9262 UpdateWindow(infoPtr->hwndSelf);
9267 * Performs vertical scrolling.
9270 * [I] infoPtr : valid pointer to the listview structure
9271 * [I] nScrollCode : scroll code
9272 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
9273 * [I] hScrollWnd : scrollbar control window handle
9279 * SB_LINEUP/SB_LINEDOWN:
9280 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
9281 * for LVS_REPORT is 1 line
9282 * for LVS_LIST cannot occur
9285 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
9286 INT nScrollDiff, HWND hScrollWnd)
9288 INT nOldScrollPos, nNewScrollPos;
9289 SCROLLINFO scrollInfo;
9292 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
9293 debugscrollcode(nScrollCode), nScrollDiff);
9295 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9297 scrollInfo.cbSize = sizeof(SCROLLINFO);
9298 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
9300 is_an_icon = ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON));
9302 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
9304 nOldScrollPos = scrollInfo.nPos;
9305 switch (nScrollCode)
9311 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
9315 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
9319 nScrollDiff = -scrollInfo.nPage;
9323 nScrollDiff = scrollInfo.nPage;
9326 case SB_THUMBPOSITION:
9328 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
9335 /* quit right away if pos isn't changing */
9336 if (nScrollDiff == 0) return 0;
9338 /* calculate new position, and handle overflows */
9339 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
9340 if (nScrollDiff > 0) {
9341 if (nNewScrollPos < nOldScrollPos ||
9342 nNewScrollPos > scrollInfo.nMax)
9343 nNewScrollPos = scrollInfo.nMax;
9345 if (nNewScrollPos > nOldScrollPos ||
9346 nNewScrollPos < scrollInfo.nMin)
9347 nNewScrollPos = scrollInfo.nMin;
9350 /* set the new position, and reread in case it changed */
9351 scrollInfo.fMask = SIF_POS;
9352 scrollInfo.nPos = nNewScrollPos;
9353 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
9355 /* carry on only if it really changed */
9356 if (nNewScrollPos == nOldScrollPos) return 0;
9358 /* now adjust to client coordinates */
9359 nScrollDiff = nOldScrollPos - nNewScrollPos;
9360 if (infoPtr->uView == LV_VIEW_DETAILS) nScrollDiff *= infoPtr->nItemHeight;
9362 /* and scroll the window */
9363 scroll_list(infoPtr, 0, nScrollDiff);
9370 * Performs horizontal scrolling.
9373 * [I] infoPtr : valid pointer to the listview structure
9374 * [I] nScrollCode : scroll code
9375 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
9376 * [I] hScrollWnd : scrollbar control window handle
9382 * SB_LINELEFT/SB_LINERIGHT:
9383 * for LVS_ICON, LVS_SMALLICON 1 pixel
9384 * for LVS_REPORT is 1 pixel
9385 * for LVS_LIST is 1 column --> which is a 1 because the
9386 * scroll is based on columns not pixels
9389 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
9390 INT nScrollDiff, HWND hScrollWnd)
9392 INT nOldScrollPos, nNewScrollPos;
9393 SCROLLINFO scrollInfo;
9395 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
9396 debugscrollcode(nScrollCode), nScrollDiff);
9398 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9400 scrollInfo.cbSize = sizeof(SCROLLINFO);
9401 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
9403 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
9405 nOldScrollPos = scrollInfo.nPos;
9407 switch (nScrollCode)
9421 nScrollDiff = -scrollInfo.nPage;
9425 nScrollDiff = scrollInfo.nPage;
9428 case SB_THUMBPOSITION:
9430 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
9437 /* quit right away if pos isn't changing */
9438 if (nScrollDiff == 0) return 0;
9440 /* calculate new position, and handle overflows */
9441 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
9442 if (nScrollDiff > 0) {
9443 if (nNewScrollPos < nOldScrollPos ||
9444 nNewScrollPos > scrollInfo.nMax)
9445 nNewScrollPos = scrollInfo.nMax;
9447 if (nNewScrollPos > nOldScrollPos ||
9448 nNewScrollPos < scrollInfo.nMin)
9449 nNewScrollPos = scrollInfo.nMin;
9452 /* set the new position, and reread in case it changed */
9453 scrollInfo.fMask = SIF_POS;
9454 scrollInfo.nPos = nNewScrollPos;
9455 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
9457 /* carry on only if it really changed */
9458 if (nNewScrollPos == nOldScrollPos) return 0;
9460 if (infoPtr->hwndHeader) LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
9462 /* now adjust to client coordinates */
9463 nScrollDiff = nOldScrollPos - nNewScrollPos;
9464 if (infoPtr->uView == LV_VIEW_LIST) nScrollDiff *= infoPtr->nItemWidth;
9466 /* and scroll the window */
9467 scroll_list(infoPtr, nScrollDiff, 0);
9472 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
9474 INT gcWheelDelta = 0;
9475 INT pulScrollLines = 3;
9476 SCROLLINFO scrollInfo;
9478 TRACE("(wheelDelta=%d)\n", wheelDelta);
9480 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
9481 gcWheelDelta -= wheelDelta;
9483 scrollInfo.cbSize = sizeof(SCROLLINFO);
9484 scrollInfo.fMask = SIF_POS;
9486 switch(infoPtr->uView)
9489 case LV_VIEW_SMALLICON:
9491 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
9492 * should be fixed in the future.
9494 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
9495 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
9498 case LV_VIEW_DETAILS:
9499 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
9501 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
9502 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
9503 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
9508 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
9519 * [I] infoPtr : valid pointer to the listview structure
9520 * [I] nVirtualKey : virtual key
9521 * [I] lKeyData : key data
9526 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
9528 HWND hwndSelf = infoPtr->hwndSelf;
9530 NMLVKEYDOWN nmKeyDown;
9532 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
9534 /* send LVN_KEYDOWN notification */
9535 nmKeyDown.wVKey = nVirtualKey;
9536 nmKeyDown.flags = 0;
9537 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
9538 if (!IsWindow(hwndSelf))
9541 switch (nVirtualKey)
9544 nItem = infoPtr->nFocusedItem;
9545 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
9546 toggle_checkbox_state(infoPtr, infoPtr->nFocusedItem);
9550 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
9552 if (!notify(infoPtr, NM_RETURN)) return 0;
9553 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
9558 if (infoPtr->nItemCount > 0)
9563 if (infoPtr->nItemCount > 0)
9564 nItem = infoPtr->nItemCount - 1;
9568 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TOLEFT);
9572 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_ABOVE);
9576 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TORIGHT);
9580 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_BELOW);
9584 if (infoPtr->uView == LV_VIEW_DETAILS)
9586 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
9587 if (infoPtr->nFocusedItem == topidx)
9588 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
9593 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
9594 * LISTVIEW_GetCountPerRow(infoPtr);
9595 if(nItem < 0) nItem = 0;
9599 if (infoPtr->uView == LV_VIEW_DETAILS)
9601 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
9602 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
9603 if (infoPtr->nFocusedItem == topidx + cnt - 1)
9604 nItem = infoPtr->nFocusedItem + cnt - 1;
9606 nItem = topidx + cnt - 1;
9609 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
9610 * LISTVIEW_GetCountPerRow(infoPtr);
9611 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
9615 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
9616 LISTVIEW_KeySelection(infoPtr, nItem, nVirtualKey == VK_SPACE);
9626 * [I] infoPtr : valid pointer to the listview structure
9631 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
9635 /* if we did not have the focus, there's nothing to do */
9636 if (!infoPtr->bFocus) return 0;
9638 /* send NM_KILLFOCUS notification */
9639 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
9641 /* if we have a focus rectangle, get rid of it */
9642 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
9644 /* if have a marquee selection, stop it */
9645 if (infoPtr->bMarqueeSelect)
9647 /* Remove the marquee rectangle and release our mouse capture */
9648 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeRect);
9651 SetRect(&infoPtr->marqueeRect, 0, 0, 0, 0);
9653 infoPtr->bMarqueeSelect = FALSE;
9654 infoPtr->bScrolling = FALSE;
9655 KillTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr);
9658 /* set window focus flag */
9659 infoPtr->bFocus = FALSE;
9661 /* invalidate the selected items before resetting focus flag */
9662 LISTVIEW_InvalidateSelectedItems(infoPtr);
9669 * Processes double click messages (left mouse button).
9672 * [I] infoPtr : valid pointer to the listview structure
9673 * [I] wKey : key flag
9674 * [I] x,y : mouse coordinate
9679 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9681 LVHITTESTINFO htInfo;
9683 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
9685 /* Cancel the item edition if any */
9686 if (infoPtr->itemEdit.fEnabled)
9688 KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
9689 infoPtr->itemEdit.fEnabled = FALSE;
9692 /* send NM_RELEASEDCAPTURE notification */
9693 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9698 /* send NM_DBLCLK notification */
9699 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
9700 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
9702 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
9703 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
9710 * Processes mouse down messages (left mouse button).
9713 * infoPtr [I ] valid pointer to the listview structure
9714 * wKey [I ] key flag
9715 * x,y [I ] mouse coordinate
9720 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9722 LVHITTESTINFO lvHitTestInfo;
9723 static BOOL bGroupSelect = TRUE;
9724 POINT pt = { x, y };
9727 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
9729 /* send NM_RELEASEDCAPTURE notification */
9730 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9732 /* set left button down flag and record the click position */
9733 infoPtr->bLButtonDown = TRUE;
9734 infoPtr->ptClickPos = pt;
9735 infoPtr->bDragging = FALSE;
9736 infoPtr->bMarqueeSelect = FALSE;
9737 infoPtr->bScrolling = FALSE;
9739 lvHitTestInfo.pt.x = x;
9740 lvHitTestInfo.pt.y = y;
9742 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
9743 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
9744 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9746 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
9748 toggle_checkbox_state(infoPtr, nItem);
9752 if (infoPtr->dwStyle & LVS_SINGLESEL)
9754 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9755 infoPtr->nEditLabelItem = nItem;
9757 LISTVIEW_SetSelection(infoPtr, nItem);
9761 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
9765 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
9766 LISTVIEW_SetItemFocus(infoPtr, nItem);
9767 infoPtr->nSelectionMark = nItem;
9773 item.state = LVIS_SELECTED | LVIS_FOCUSED;
9774 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
9776 LISTVIEW_SetItemState(infoPtr,nItem,&item);
9777 infoPtr->nSelectionMark = nItem;
9780 else if (wKey & MK_CONTROL)
9784 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
9786 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
9787 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
9788 LISTVIEW_SetItemState(infoPtr, nItem, &item);
9789 infoPtr->nSelectionMark = nItem;
9791 else if (wKey & MK_SHIFT)
9793 LISTVIEW_SetGroupSelection(infoPtr, nItem);
9797 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9799 infoPtr->nEditLabelItem = nItem;
9800 infoPtr->nLButtonDownItem = nItem;
9802 LISTVIEW_SetItemFocus(infoPtr, nItem);
9805 /* set selection (clears other pre-existing selections) */
9806 LISTVIEW_SetSelection(infoPtr, nItem);
9810 if (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE)
9811 if(lvHitTestInfo.iItem != -1) notify_itemactivate(infoPtr,&lvHitTestInfo);
9815 if (!infoPtr->bFocus)
9816 SetFocus(infoPtr->hwndSelf);
9818 /* remove all selections */
9819 if (!(wKey & MK_CONTROL) && !(wKey & MK_SHIFT))
9820 LISTVIEW_DeselectAll(infoPtr);
9829 * Processes mouse up messages (left mouse button).
9832 * infoPtr [I ] valid pointer to the listview structure
9833 * wKey [I ] key flag
9834 * x,y [I ] mouse coordinate
9839 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9841 LVHITTESTINFO lvHitTestInfo;
9843 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
9845 if (!infoPtr->bLButtonDown) return 0;
9847 lvHitTestInfo.pt.x = x;
9848 lvHitTestInfo.pt.y = y;
9850 /* send NM_CLICK notification */
9851 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9852 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
9854 /* set left button flag */
9855 infoPtr->bLButtonDown = FALSE;
9857 /* set a single selection, reset others */
9858 if(lvHitTestInfo.iItem == infoPtr->nLButtonDownItem && lvHitTestInfo.iItem != -1)
9859 LISTVIEW_SetSelection(infoPtr, infoPtr->nLButtonDownItem);
9860 infoPtr->nLButtonDownItem = -1;
9862 if (infoPtr->bDragging || infoPtr->bMarqueeSelect)
9864 /* Remove the marquee rectangle and release our mouse capture */
9865 if (infoPtr->bMarqueeSelect)
9867 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeDrawRect);
9871 SetRect(&infoPtr->marqueeRect, 0, 0, 0, 0);
9872 SetRect(&infoPtr->marqueeDrawRect, 0, 0, 0, 0);
9874 infoPtr->bDragging = FALSE;
9875 infoPtr->bMarqueeSelect = FALSE;
9876 infoPtr->bScrolling = FALSE;
9878 KillTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr);
9882 /* if we clicked on a selected item, edit the label */
9883 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
9885 /* we want to make sure the user doesn't want to do a double click. So we will
9886 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
9888 infoPtr->itemEdit.fEnabled = TRUE;
9889 infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
9890 SetTimer(infoPtr->hwndSelf,
9891 (UINT_PTR)&infoPtr->itemEdit,
9892 GetDoubleClickTime(),
9893 LISTVIEW_DelayedEditItem);
9896 if (!infoPtr->bFocus)
9897 SetFocus(infoPtr->hwndSelf);
9904 * Destroys the listview control (called after WM_DESTROY).
9907 * [I] infoPtr : valid pointer to the listview structure
9912 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
9918 /* delete all items */
9919 LISTVIEW_DeleteAllItems(infoPtr, TRUE);
9921 /* destroy data structure */
9922 DPA_Destroy(infoPtr->hdpaItems);
9923 DPA_Destroy(infoPtr->hdpaItemIds);
9924 DPA_Destroy(infoPtr->hdpaPosX);
9925 DPA_Destroy(infoPtr->hdpaPosY);
9927 for (i = 0; i < DPA_GetPtrCount(infoPtr->hdpaColumns); i++)
9928 Free(DPA_GetPtr(infoPtr->hdpaColumns, i));
9929 DPA_Destroy(infoPtr->hdpaColumns);
9930 ranges_destroy(infoPtr->selectionRanges);
9932 /* destroy image lists */
9933 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
9935 ImageList_Destroy(infoPtr->himlNormal);
9936 ImageList_Destroy(infoPtr->himlSmall);
9937 ImageList_Destroy(infoPtr->himlState);
9940 /* destroy font, bkgnd brush */
9942 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
9943 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
9945 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
9947 /* free listview info pointer*/
9955 * Handles notifications from header.
9958 * [I] infoPtr : valid pointer to the listview structure
9959 * [I] nCtrlId : control identifier
9960 * [I] lpnmh : notification information
9965 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
9967 HWND hwndSelf = infoPtr->hwndSelf;
9969 TRACE("(lpnmh=%p)\n", lpnmh);
9971 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
9973 switch (lpnmh->hdr.code)
9978 COLUMN_INFO *lpColumnInfo;
9982 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
9985 /* remove the old line (if any) */
9986 LISTVIEW_DrawTrackLine(infoPtr);
9988 /* compute & draw the new line */
9989 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
9990 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
9991 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
9992 infoPtr->xTrackLine = x + ptOrigin.x;
9993 LISTVIEW_DrawTrackLine(infoPtr);
9999 /* remove the track line (if any) */
10000 LISTVIEW_DrawTrackLine(infoPtr);
10001 infoPtr->xTrackLine = -1;
10004 case HDN_BEGINDRAG:
10005 notify_forward_header(infoPtr, lpnmh);
10006 return (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP) == 0;
10009 infoPtr->colRectsDirty = TRUE;
10010 LISTVIEW_InvalidateList(infoPtr);
10011 notify_forward_header(infoPtr, lpnmh);
10014 case HDN_ITEMCHANGINGW:
10015 case HDN_ITEMCHANGINGA:
10016 return notify_forward_header(infoPtr, lpnmh);
10018 case HDN_ITEMCHANGEDW:
10019 case HDN_ITEMCHANGEDA:
10021 COLUMN_INFO *lpColumnInfo;
10025 notify_forward_header(infoPtr, lpnmh);
10026 if (!IsWindow(hwndSelf))
10029 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
10031 hdi.mask = HDI_WIDTH;
10032 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, lpnmh->iItem, (LPARAM)&hdi)) return 0;
10036 cxy = lpnmh->pitem->cxy;
10038 /* determine how much we change since the last know position */
10039 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
10040 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
10043 lpColumnInfo->rcHeader.right += dx;
10045 hdi.mask = HDI_ORDER;
10046 SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, lpnmh->iItem, (LPARAM)&hdi);
10048 /* not the rightmost one */
10049 if (hdi.iOrder + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
10051 INT nIndex = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
10052 hdi.iOrder + 1, 0);
10053 LISTVIEW_ScrollColumns(infoPtr, nIndex, dx);
10057 /* only needs to update the scrolls */
10058 infoPtr->nItemWidth += dx;
10059 LISTVIEW_UpdateScroll(infoPtr);
10061 LISTVIEW_UpdateItemSize(infoPtr);
10062 if (infoPtr->uView == LV_VIEW_DETAILS && is_redrawing(infoPtr))
10065 RECT rcCol = lpColumnInfo->rcHeader;
10067 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
10068 OffsetRect(&rcCol, ptOrigin.x, 0);
10070 rcCol.top = infoPtr->rcList.top;
10071 rcCol.bottom = infoPtr->rcList.bottom;
10073 /* resizing left-aligned columns leaves most of the left side untouched */
10074 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
10076 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
10079 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
10082 /* when shrinking the last column clear the now unused field */
10083 if (hdi.iOrder == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
10089 /* deal with right from rightmost column area */
10090 right.left = rcCol.right;
10091 right.top = rcCol.top;
10092 right.bottom = rcCol.bottom;
10093 right.right = infoPtr->rcList.right;
10095 LISTVIEW_InvalidateRect(infoPtr, &right);
10098 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
10104 case HDN_ITEMCLICKW:
10105 case HDN_ITEMCLICKA:
10107 /* Handle sorting by Header Column */
10110 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
10112 nmlv.iSubItem = lpnmh->iItem;
10113 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
10114 notify_forward_header(infoPtr, lpnmh);
10118 case HDN_DIVIDERDBLCLICKW:
10119 case HDN_DIVIDERDBLCLICKA:
10120 /* FIXME: for LVS_EX_HEADERINALLVIEWS and not LV_VIEW_DETAILS
10121 we should use LVSCW_AUTOSIZE_USEHEADER, helper rework or
10122 split needed for that */
10123 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
10124 notify_forward_header(infoPtr, lpnmh);
10133 * Paint non-client area of control.
10136 * [I] infoPtr : valid pointer to the listview structureof the sender
10137 * [I] region : update region
10140 * TRUE - frame was painted
10141 * FALSE - call default window proc
10143 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
10145 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
10149 int cxEdge = GetSystemMetrics (SM_CXEDGE),
10150 cyEdge = GetSystemMetrics (SM_CYEDGE);
10153 return DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)region, 0);
10155 GetWindowRect(infoPtr->hwndSelf, &r);
10157 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
10158 r.right - cxEdge, r.bottom - cyEdge);
10159 if (region != (HRGN)1)
10160 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
10161 OffsetRect(&r, -r.left, -r.top);
10163 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
10164 OffsetRect(&r, -r.left, -r.top);
10166 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
10167 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
10168 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
10169 ReleaseDC(infoPtr->hwndSelf, dc);
10171 /* Call default proc to get the scrollbars etc. painted */
10172 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
10179 * Determines the type of structure to use.
10182 * [I] infoPtr : valid pointer to the listview structureof the sender
10183 * [I] hwndFrom : listview window handle
10184 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
10189 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
10191 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
10193 if (nCommand == NF_REQUERY)
10194 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
10196 return infoPtr->notifyFormat;
10201 * Paints/Repaints the listview control. Internal use.
10204 * [I] infoPtr : valid pointer to the listview structure
10205 * [I] hdc : device context handle
10210 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
10212 TRACE("(hdc=%p)\n", hdc);
10214 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
10216 infoPtr->bNoItemMetrics = FALSE;
10217 LISTVIEW_UpdateItemSize(infoPtr);
10218 if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)
10219 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
10220 LISTVIEW_UpdateScroll(infoPtr);
10223 if (infoPtr->hwndHeader) UpdateWindow(infoPtr->hwndHeader);
10226 LISTVIEW_Refresh(infoPtr, hdc, NULL);
10231 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
10232 if (!hdc) return 1;
10233 LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
10234 EndPaint(infoPtr->hwndSelf, &ps);
10242 * Paints/Repaints the listview control, WM_PAINT handler.
10245 * [I] infoPtr : valid pointer to the listview structure
10246 * [I] hdc : device context handle
10251 static inline LRESULT LISTVIEW_WMPaint(LISTVIEW_INFO *infoPtr, HDC hdc)
10253 TRACE("(hdc=%p)\n", hdc);
10255 if (!is_redrawing(infoPtr))
10256 return DefWindowProcW (infoPtr->hwndSelf, WM_PAINT, (WPARAM)hdc, 0);
10258 return LISTVIEW_Paint(infoPtr, hdc);
10263 * Paints/Repaints the listview control.
10266 * [I] infoPtr : valid pointer to the listview structure
10267 * [I] hdc : device context handle
10268 * [I] options : drawing options
10273 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
10275 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
10277 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
10280 if (options & PRF_ERASEBKGND)
10281 LISTVIEW_EraseBkgnd(infoPtr, hdc);
10283 if (options & PRF_CLIENT)
10284 LISTVIEW_Paint(infoPtr, hdc);
10292 * Processes double click messages (right mouse button).
10295 * [I] infoPtr : valid pointer to the listview structure
10296 * [I] wKey : key flag
10297 * [I] x,y : mouse coordinate
10302 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10304 LVHITTESTINFO lvHitTestInfo;
10306 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
10308 /* send NM_RELEASEDCAPTURE notification */
10309 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
10311 /* send NM_RDBLCLK notification */
10312 lvHitTestInfo.pt.x = x;
10313 lvHitTestInfo.pt.y = y;
10314 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
10315 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
10322 * Processes mouse down messages (right mouse button).
10325 * [I] infoPtr : valid pointer to the listview structure
10326 * [I] wKey : key flag
10327 * [I] x,y : mouse coordinate
10332 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10334 LVHITTESTINFO lvHitTestInfo;
10337 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
10339 /* send NM_RELEASEDCAPTURE notification */
10340 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
10342 /* make sure the listview control window has the focus */
10343 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
10345 /* set right button down flag */
10346 infoPtr->bRButtonDown = TRUE;
10348 /* determine the index of the selected item */
10349 lvHitTestInfo.pt.x = x;
10350 lvHitTestInfo.pt.y = y;
10351 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
10353 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
10355 LISTVIEW_SetItemFocus(infoPtr, nItem);
10356 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
10357 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
10358 LISTVIEW_SetSelection(infoPtr, nItem);
10362 LISTVIEW_DeselectAll(infoPtr);
10370 * Processes mouse up messages (right mouse button).
10373 * [I] infoPtr : valid pointer to the listview structure
10374 * [I] wKey : key flag
10375 * [I] x,y : mouse coordinate
10380 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10382 LVHITTESTINFO lvHitTestInfo;
10385 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
10387 if (!infoPtr->bRButtonDown) return 0;
10389 /* set button flag */
10390 infoPtr->bRButtonDown = FALSE;
10392 /* Send NM_RCLICK notification */
10393 lvHitTestInfo.pt.x = x;
10394 lvHitTestInfo.pt.y = y;
10395 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
10396 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
10398 /* Change to screen coordinate for WM_CONTEXTMENU */
10399 pt = lvHitTestInfo.pt;
10400 ClientToScreen(infoPtr->hwndSelf, &pt);
10402 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
10403 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
10404 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
10415 * [I] infoPtr : valid pointer to the listview structure
10416 * [I] hwnd : window handle of window containing the cursor
10417 * [I] nHittest : hit-test code
10418 * [I] wMouseMsg : ideintifier of the mouse message
10421 * TRUE if cursor is set
10424 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
10426 LVHITTESTINFO lvHitTestInfo;
10428 if (!(LISTVIEW_isHotTracking(infoPtr))) goto forward;
10430 if (!infoPtr->hHotCursor) goto forward;
10432 GetCursorPos(&lvHitTestInfo.pt);
10433 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) goto forward;
10435 SetCursor(infoPtr->hHotCursor);
10441 return DefWindowProcW(infoPtr->hwndSelf, WM_SETCURSOR, wParam, lParam);
10449 * [I] infoPtr : valid pointer to the listview structure
10450 * [I] hwndLoseFocus : handle of previously focused window
10455 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
10457 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
10459 /* if we have the focus already, there's nothing to do */
10460 if (infoPtr->bFocus) return 0;
10462 /* send NM_SETFOCUS notification */
10463 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
10465 /* set window focus flag */
10466 infoPtr->bFocus = TRUE;
10468 /* put the focus rect back on */
10469 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
10471 /* redraw all visible selected items */
10472 LISTVIEW_InvalidateSelectedItems(infoPtr);
10482 * [I] infoPtr : valid pointer to the listview structure
10483 * [I] fRedraw : font handle
10484 * [I] fRedraw : redraw flag
10489 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
10491 HFONT oldFont = infoPtr->hFont;
10493 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
10495 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
10496 if (infoPtr->hFont == oldFont) return 0;
10498 LISTVIEW_SaveTextMetrics(infoPtr);
10500 if (infoPtr->uView == LV_VIEW_DETAILS)
10502 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
10503 LISTVIEW_UpdateSize(infoPtr);
10504 LISTVIEW_UpdateScroll(infoPtr);
10507 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
10514 * Message handling for WM_SETREDRAW.
10515 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
10518 * [I] infoPtr : valid pointer to the listview structure
10519 * [I] bRedraw: state of redraw flag
10522 * DefWinProc return value
10524 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
10526 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
10528 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
10529 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
10531 infoPtr->bRedraw = bRedraw;
10533 if(!bRedraw) return 0;
10535 if (is_autoarrange(infoPtr))
10536 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
10537 LISTVIEW_UpdateScroll(infoPtr);
10539 /* despite what the WM_SETREDRAW docs says, apps expect us
10540 * to invalidate the listview here... stupid! */
10541 LISTVIEW_InvalidateList(infoPtr);
10548 * Resizes the listview control. This function processes WM_SIZE
10549 * messages. At this time, the width and height are not used.
10552 * [I] infoPtr : valid pointer to the listview structure
10553 * [I] Width : new width
10554 * [I] Height : new height
10559 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
10561 RECT rcOld = infoPtr->rcList;
10563 TRACE("(width=%d, height=%d)\n", Width, Height);
10565 LISTVIEW_UpdateSize(infoPtr);
10566 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
10568 /* do not bother with display related stuff if we're not redrawing */
10569 if (!is_redrawing(infoPtr)) return 0;
10571 if (is_autoarrange(infoPtr))
10572 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
10574 LISTVIEW_UpdateScroll(infoPtr);
10576 /* refresh all only for lists whose height changed significantly */
10577 if ((infoPtr->uView == LV_VIEW_LIST) &&
10578 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
10579 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
10580 LISTVIEW_InvalidateList(infoPtr);
10587 * Sets the size information.
10590 * [I] infoPtr : valid pointer to the listview structure
10595 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
10597 TRACE("uView=%d, rcList(old)=%s\n", infoPtr->uView, wine_dbgstr_rect(&infoPtr->rcList));
10599 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
10601 if (infoPtr->uView == LV_VIEW_LIST)
10603 /* Apparently the "LIST" style is supposed to have the same
10604 * number of items in a column even if there is no scroll bar.
10605 * Since if a scroll bar already exists then the bottom is already
10606 * reduced, only reduce if the scroll bar does not currently exist.
10607 * The "2" is there to mimic the native control. I think it may be
10608 * related to either padding or edges. (GLA 7/2002)
10610 if (!(infoPtr->dwStyle & WS_HSCROLL))
10611 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
10612 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
10615 /* if control created invisible header isn't created */
10616 if (infoPtr->hwndHeader)
10621 hl.prc = &infoPtr->rcList;
10623 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
10624 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp.flags, wp.x, wp.y, wp.cx, wp.cy);
10626 if (LISTVIEW_IsHeaderEnabled(infoPtr))
10627 wp.flags |= SWP_SHOWWINDOW;
10630 wp.flags |= SWP_HIDEWINDOW;
10634 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
10635 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy);
10637 infoPtr->rcList.top = max(wp.cy, 0);
10639 /* extra padding for grid */
10640 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
10641 infoPtr->rcList.top += 2;
10643 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
10648 * Processes WM_STYLECHANGED messages.
10651 * [I] infoPtr : valid pointer to the listview structure
10652 * [I] wStyleType : window style type (normal or extended)
10653 * [I] lpss : window style information
10658 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
10659 const STYLESTRUCT *lpss)
10661 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
10662 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
10665 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
10666 wStyleType, lpss->styleOld, lpss->styleNew);
10668 if (wStyleType != GWL_STYLE) return 0;
10670 infoPtr->dwStyle = lpss->styleNew;
10671 map_style_view(infoPtr);
10673 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
10674 ((lpss->styleNew & WS_HSCROLL) == 0))
10675 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
10677 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
10678 ((lpss->styleNew & WS_VSCROLL) == 0))
10679 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
10681 if (uNewView != uOldView)
10683 SIZE oldIconSize = infoPtr->iconSize;
10686 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
10687 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
10689 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
10690 SetRectEmpty(&infoPtr->rcFocus);
10692 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
10693 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
10695 if (uNewView == LVS_ICON)
10697 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
10699 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
10700 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
10701 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
10704 else if (uNewView == LVS_REPORT)
10709 LISTVIEW_CreateHeader( infoPtr );
10711 hl.prc = &infoPtr->rcList;
10713 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
10714 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
10715 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
10716 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
10719 LISTVIEW_UpdateItemSize(infoPtr);
10722 if (uNewView == LVS_REPORT || infoPtr->dwLvExStyle & LVS_EX_HEADERINALLVIEWS)
10724 if ((lpss->styleOld ^ lpss->styleNew) & LVS_NOCOLUMNHEADER)
10726 if (lpss->styleNew & LVS_NOCOLUMNHEADER)
10728 /* Turn off the header control */
10729 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
10730 TRACE("Hide header control, was 0x%08x\n", style);
10731 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style | HDS_HIDDEN);
10733 /* Turn on the header control */
10734 if ((style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE)) & HDS_HIDDEN)
10736 TRACE("Show header control, was 0x%08x\n", style);
10737 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, (style & ~HDS_HIDDEN) | WS_VISIBLE);
10743 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
10744 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
10745 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
10747 /* update the size of the client area */
10748 LISTVIEW_UpdateSize(infoPtr);
10750 /* add scrollbars if needed */
10751 LISTVIEW_UpdateScroll(infoPtr);
10753 /* invalidate client area + erase background */
10754 LISTVIEW_InvalidateList(infoPtr);
10761 * Processes WM_STYLECHANGING messages.
10764 * [I] infoPtr : valid pointer to the listview structure
10765 * [I] wStyleType : window style type (normal or extended)
10766 * [I0] lpss : window style information
10771 static INT LISTVIEW_StyleChanging(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
10774 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
10775 wStyleType, lpss->styleOld, lpss->styleNew);
10777 /* don't forward LVS_OWNERDATA only if not already set to */
10778 if ((lpss->styleNew ^ lpss->styleOld) & LVS_OWNERDATA)
10780 if (lpss->styleOld & LVS_OWNERDATA)
10781 lpss->styleNew |= LVS_OWNERDATA;
10783 lpss->styleNew &= ~LVS_OWNERDATA;
10791 * Processes WM_SHOWWINDOW messages.
10794 * [I] infoPtr : valid pointer to the listview structure
10795 * [I] bShown : window is being shown (FALSE when hidden)
10796 * [I] iStatus : window show status
10801 static LRESULT LISTVIEW_ShowWindow(LISTVIEW_INFO *infoPtr, WPARAM bShown, LPARAM iStatus)
10803 /* header delayed creation */
10804 if ((infoPtr->uView == LV_VIEW_DETAILS) && bShown)
10806 LISTVIEW_CreateHeader(infoPtr);
10808 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle))
10809 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
10812 return DefWindowProcW(infoPtr->hwndSelf, WM_SHOWWINDOW, bShown, iStatus);
10817 * Processes CCM_GETVERSION messages.
10820 * [I] infoPtr : valid pointer to the listview structure
10825 static inline LRESULT LISTVIEW_GetVersion(const LISTVIEW_INFO *infoPtr)
10827 return infoPtr->iVersion;
10832 * Processes CCM_SETVERSION messages.
10835 * [I] infoPtr : valid pointer to the listview structure
10836 * [I] iVersion : version to be set
10839 * -1 when requested version is greater than DLL version;
10840 * previous version otherwise
10842 static LRESULT LISTVIEW_SetVersion(LISTVIEW_INFO *infoPtr, DWORD iVersion)
10844 INT iOldVersion = infoPtr->iVersion;
10846 if (iVersion > COMCTL32_VERSION)
10849 infoPtr->iVersion = iVersion;
10851 TRACE("new version %d\n", iVersion);
10853 return iOldVersion;
10858 * Window procedure of the listview control.
10861 static LRESULT WINAPI
10862 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10864 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
10866 TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg, wParam, lParam);
10868 if (!infoPtr && (uMsg != WM_NCCREATE))
10869 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10873 case LVM_APPROXIMATEVIEWRECT:
10874 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
10875 LOWORD(lParam), HIWORD(lParam));
10877 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
10879 case LVM_CANCELEDITLABEL:
10880 return LISTVIEW_CancelEditLabel(infoPtr);
10882 case LVM_CREATEDRAGIMAGE:
10883 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
10885 case LVM_DELETEALLITEMS:
10886 return LISTVIEW_DeleteAllItems(infoPtr, FALSE);
10888 case LVM_DELETECOLUMN:
10889 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
10891 case LVM_DELETEITEM:
10892 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
10894 case LVM_EDITLABELA:
10895 case LVM_EDITLABELW:
10896 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam,
10897 uMsg == LVM_EDITLABELW);
10898 /* case LVM_ENABLEGROUPVIEW: */
10900 case LVM_ENSUREVISIBLE:
10901 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
10903 case LVM_FINDITEMW:
10904 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
10906 case LVM_FINDITEMA:
10907 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
10909 case LVM_GETBKCOLOR:
10910 return infoPtr->clrBk;
10912 /* case LVM_GETBKIMAGE: */
10914 case LVM_GETCALLBACKMASK:
10915 return infoPtr->uCallbackMask;
10917 case LVM_GETCOLUMNA:
10918 case LVM_GETCOLUMNW:
10919 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam,
10920 uMsg == LVM_GETCOLUMNW);
10922 case LVM_GETCOLUMNORDERARRAY:
10923 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
10925 case LVM_GETCOLUMNWIDTH:
10926 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
10928 case LVM_GETCOUNTPERPAGE:
10929 return LISTVIEW_GetCountPerPage(infoPtr);
10931 case LVM_GETEDITCONTROL:
10932 return (LRESULT)infoPtr->hwndEdit;
10934 case LVM_GETEXTENDEDLISTVIEWSTYLE:
10935 return infoPtr->dwLvExStyle;
10937 /* case LVM_GETGROUPINFO: */
10939 /* case LVM_GETGROUPMETRICS: */
10941 case LVM_GETHEADER:
10942 return (LRESULT)infoPtr->hwndHeader;
10944 case LVM_GETHOTCURSOR:
10945 return (LRESULT)infoPtr->hHotCursor;
10947 case LVM_GETHOTITEM:
10948 return infoPtr->nHotItem;
10950 case LVM_GETHOVERTIME:
10951 return infoPtr->dwHoverTime;
10953 case LVM_GETIMAGELIST:
10954 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
10956 /* case LVM_GETINSERTMARK: */
10958 /* case LVM_GETINSERTMARKCOLOR: */
10960 /* case LVM_GETINSERTMARKRECT: */
10962 case LVM_GETISEARCHSTRINGA:
10963 case LVM_GETISEARCHSTRINGW:
10964 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
10969 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, uMsg == LVM_GETITEMW);
10971 case LVM_GETITEMCOUNT:
10972 return infoPtr->nItemCount;
10974 case LVM_GETITEMPOSITION:
10975 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
10977 case LVM_GETITEMRECT:
10978 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
10980 case LVM_GETITEMSPACING:
10981 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
10983 case LVM_GETITEMSTATE:
10984 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
10986 case LVM_GETITEMTEXTA:
10987 case LVM_GETITEMTEXTW:
10988 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam,
10989 uMsg == LVM_GETITEMTEXTW);
10991 case LVM_GETNEXTITEM:
10992 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
10994 case LVM_GETNUMBEROFWORKAREAS:
10995 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
10998 case LVM_GETORIGIN:
10999 if (!lParam) return FALSE;
11000 if (infoPtr->uView == LV_VIEW_DETAILS ||
11001 infoPtr->uView == LV_VIEW_LIST) return FALSE;
11002 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
11005 /* case LVM_GETOUTLINECOLOR: */
11007 /* case LVM_GETSELECTEDCOLUMN: */
11009 case LVM_GETSELECTEDCOUNT:
11010 return LISTVIEW_GetSelectedCount(infoPtr);
11012 case LVM_GETSELECTIONMARK:
11013 return infoPtr->nSelectionMark;
11015 case LVM_GETSTRINGWIDTHA:
11016 case LVM_GETSTRINGWIDTHW:
11017 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam,
11018 uMsg == LVM_GETSTRINGWIDTHW);
11020 case LVM_GETSUBITEMRECT:
11021 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
11023 case LVM_GETTEXTBKCOLOR:
11024 return infoPtr->clrTextBk;
11026 case LVM_GETTEXTCOLOR:
11027 return infoPtr->clrText;
11029 /* case LVM_GETTILEINFO: */
11031 /* case LVM_GETTILEVIEWINFO: */
11033 case LVM_GETTOOLTIPS:
11034 if( !infoPtr->hwndToolTip )
11035 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
11036 return (LRESULT)infoPtr->hwndToolTip;
11038 case LVM_GETTOPINDEX:
11039 return LISTVIEW_GetTopIndex(infoPtr);
11041 case LVM_GETUNICODEFORMAT:
11042 return (infoPtr->notifyFormat == NFR_UNICODE);
11045 return infoPtr->uView;
11047 case LVM_GETVIEWRECT:
11048 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
11050 case LVM_GETWORKAREAS:
11051 FIXME("LVM_GETWORKAREAS: unimplemented\n");
11054 /* case LVM_HASGROUP: */
11057 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, TRUE);
11059 case LVM_INSERTCOLUMNA:
11060 case LVM_INSERTCOLUMNW:
11061 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam,
11062 uMsg == LVM_INSERTCOLUMNW);
11064 /* case LVM_INSERTGROUP: */
11066 /* case LVM_INSERTGROUPSORTED: */
11068 case LVM_INSERTITEMA:
11069 case LVM_INSERTITEMW:
11070 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, uMsg == LVM_INSERTITEMW);
11072 /* case LVM_INSERTMARKHITTEST: */
11074 /* case LVM_ISGROUPVIEWENABLED: */
11076 case LVM_ISITEMVISIBLE:
11077 return LISTVIEW_IsItemVisible(infoPtr, (INT)wParam);
11079 case LVM_MAPIDTOINDEX:
11080 return LISTVIEW_MapIdToIndex(infoPtr, (UINT)wParam);
11082 case LVM_MAPINDEXTOID:
11083 return LISTVIEW_MapIndexToId(infoPtr, (INT)wParam);
11085 /* case LVM_MOVEGROUP: */
11087 /* case LVM_MOVEITEMTOGROUP: */
11089 case LVM_REDRAWITEMS:
11090 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
11092 /* case LVM_REMOVEALLGROUPS: */
11094 /* case LVM_REMOVEGROUP: */
11097 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
11099 case LVM_SETBKCOLOR:
11100 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
11102 /* case LVM_SETBKIMAGE: */
11104 case LVM_SETCALLBACKMASK:
11105 infoPtr->uCallbackMask = (UINT)wParam;
11108 case LVM_SETCOLUMNA:
11109 case LVM_SETCOLUMNW:
11110 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam,
11111 uMsg == LVM_SETCOLUMNW);
11113 case LVM_SETCOLUMNORDERARRAY:
11114 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
11116 case LVM_SETCOLUMNWIDTH:
11117 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
11119 case LVM_SETEXTENDEDLISTVIEWSTYLE:
11120 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
11122 /* case LVM_SETGROUPINFO: */
11124 /* case LVM_SETGROUPMETRICS: */
11126 case LVM_SETHOTCURSOR:
11127 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
11129 case LVM_SETHOTITEM:
11130 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
11132 case LVM_SETHOVERTIME:
11133 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
11135 case LVM_SETICONSPACING:
11136 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
11138 case LVM_SETIMAGELIST:
11139 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
11141 /* case LVM_SETINFOTIP: */
11143 /* case LVM_SETINSERTMARK: */
11145 /* case LVM_SETINSERTMARKCOLOR: */
11150 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
11151 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, (uMsg == LVM_SETITEMW));
11154 case LVM_SETITEMCOUNT:
11155 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
11157 case LVM_SETITEMPOSITION:
11160 pt.x = (short)LOWORD(lParam);
11161 pt.y = (short)HIWORD(lParam);
11162 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, &pt);
11165 case LVM_SETITEMPOSITION32:
11166 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, (POINT*)lParam);
11168 case LVM_SETITEMSTATE:
11169 if (lParam == 0) return FALSE;
11170 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
11172 case LVM_SETITEMTEXTA:
11173 case LVM_SETITEMTEXTW:
11174 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam,
11175 uMsg == LVM_SETITEMTEXTW);
11177 /* case LVM_SETOUTLINECOLOR: */
11179 /* case LVM_SETSELECTEDCOLUMN: */
11181 case LVM_SETSELECTIONMARK:
11182 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
11184 case LVM_SETTEXTBKCOLOR:
11185 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
11187 case LVM_SETTEXTCOLOR:
11188 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
11190 /* case LVM_SETTILEINFO: */
11192 /* case LVM_SETTILEVIEWINFO: */
11194 /* case LVM_SETTILEWIDTH: */
11196 case LVM_SETTOOLTIPS:
11197 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
11199 case LVM_SETUNICODEFORMAT:
11200 return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
11203 return LISTVIEW_SetView(infoPtr, wParam);
11205 /* case LVM_SETWORKAREAS: */
11207 /* case LVM_SORTGROUPS: */
11209 case LVM_SORTITEMS:
11210 case LVM_SORTITEMSEX:
11211 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam,
11212 uMsg == LVM_SORTITEMSEX);
11213 case LVM_SUBITEMHITTEST:
11214 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
11217 return LISTVIEW_Update(infoPtr, (INT)wParam);
11219 case CCM_GETVERSION:
11220 return LISTVIEW_GetVersion(infoPtr);
11222 case CCM_SETVERSION:
11223 return LISTVIEW_SetVersion(infoPtr, wParam);
11226 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
11229 return LISTVIEW_Command(infoPtr, wParam, lParam);
11232 return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
11235 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
11238 return LISTVIEW_Destroy(infoPtr);
11241 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
11243 case WM_ERASEBKGND:
11244 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
11246 case WM_GETDLGCODE:
11247 return DLGC_WANTCHARS | DLGC_WANTARROWS;
11250 return (LRESULT)infoPtr->hFont;
11253 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
11256 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
11259 return LISTVIEW_KillFocus(infoPtr);
11261 case WM_LBUTTONDBLCLK:
11262 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11264 case WM_LBUTTONDOWN:
11265 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11268 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11271 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11273 case WM_MOUSEHOVER:
11274 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11277 return LISTVIEW_NCDestroy(infoPtr);
11280 return LISTVIEW_NCPaint(infoPtr, (HRGN)wParam);
11283 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
11284 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
11287 case WM_NOTIFYFORMAT:
11288 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
11290 case WM_PRINTCLIENT:
11291 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
11294 return LISTVIEW_WMPaint(infoPtr, (HDC)wParam);
11296 case WM_RBUTTONDBLCLK:
11297 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11299 case WM_RBUTTONDOWN:
11300 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11303 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11306 return LISTVIEW_SetCursor(infoPtr, wParam, lParam);
11309 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
11312 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
11315 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
11317 case WM_SHOWWINDOW:
11318 return LISTVIEW_ShowWindow(infoPtr, wParam, lParam);
11321 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
11323 case WM_STYLECHANGED:
11324 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
11326 case WM_STYLECHANGING:
11327 return LISTVIEW_StyleChanging(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
11329 case WM_SYSCOLORCHANGE:
11330 COMCTL32_RefreshSysColors();
11333 /* case WM_TIMER: */
11334 case WM_THEMECHANGED:
11335 return LISTVIEW_ThemeChanged(infoPtr);
11338 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
11340 case WM_MOUSEWHEEL:
11341 if (wParam & (MK_SHIFT | MK_CONTROL))
11342 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
11343 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
11345 case WM_WINDOWPOSCHANGED:
11346 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
11348 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
11349 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
11351 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
11353 if (notify_measureitem(infoPtr)) LISTVIEW_InvalidateList(infoPtr);
11356 LISTVIEW_UpdateSize(infoPtr);
11357 LISTVIEW_UpdateScroll(infoPtr);
11359 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
11361 /* case WM_WININICHANGE: */
11364 if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
11365 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
11367 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
11374 * Registers the window class.
11382 void LISTVIEW_Register(void)
11384 WNDCLASSW wndClass;
11386 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
11387 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
11388 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
11389 wndClass.cbClsExtra = 0;
11390 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
11391 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
11392 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
11393 wndClass.lpszClassName = WC_LISTVIEWW;
11394 RegisterClassW(&wndClass);
11399 * Unregisters the window class.
11407 void LISTVIEW_Unregister(void)
11409 UnregisterClassW(WC_LISTVIEWW, NULL);
11414 * Handle any WM_COMMAND messages
11417 * [I] infoPtr : valid pointer to the listview structure
11418 * [I] wParam : the first message parameter
11419 * [I] lParam : the second message parameter
11424 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
11426 switch (HIWORD(wParam))
11431 * Adjust the edit window size
11433 WCHAR buffer[1024];
11434 HDC hdc = GetDC(infoPtr->hwndEdit);
11435 HFONT hFont, hOldFont = 0;
11439 if (!infoPtr->hwndEdit || !hdc) return 0;
11440 GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
11441 GetWindowRect(infoPtr->hwndEdit, &rect);
11443 /* Select font to get the right dimension of the string */
11444 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
11447 hOldFont = SelectObject(hdc, hFont);
11450 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
11452 TEXTMETRICW textMetric;
11454 /* Add Extra spacing for the next character */
11455 GetTextMetricsW(hdc, &textMetric);
11456 sz.cx += (textMetric.tmMaxCharWidth * 2);
11464 rect.bottom - rect.top,
11465 SWP_DRAWFRAME|SWP_NOMOVE);
11468 SelectObject(hdc, hOldFont);
11470 ReleaseDC(infoPtr->hwndEdit, hdc);
11476 LISTVIEW_CancelEditLabel(infoPtr);
11481 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
11490 * Subclassed edit control windproc function
11493 * [I] hwnd : the edit window handle
11494 * [I] uMsg : the message that is to be processed
11495 * [I] wParam : first message parameter
11496 * [I] lParam : second message parameter
11497 * [I] isW : TRUE if input is Unicode
11502 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
11504 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
11507 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
11508 hwnd, uMsg, wParam, lParam, isW);
11512 case WM_GETDLGCODE:
11513 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
11517 WNDPROC editProc = infoPtr->EditWndProc;
11518 infoPtr->EditWndProc = 0;
11519 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
11520 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
11524 if (VK_ESCAPE == (INT)wParam)
11529 else if (VK_RETURN == (INT)wParam)
11533 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
11536 /* kill the edit */
11537 if (infoPtr->hwndEdit)
11538 LISTVIEW_EndEditLabelT(infoPtr, save, isW);
11540 SendMessageW(hwnd, WM_CLOSE, 0, 0);
11546 * Subclassed edit control Unicode windproc function
11549 * [I] hwnd : the edit window handle
11550 * [I] uMsg : the message that is to be processed
11551 * [I] wParam : first message parameter
11552 * [I] lParam : second message parameter
11556 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
11558 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
11563 * Subclassed edit control ANSI windproc function
11566 * [I] hwnd : the edit window handle
11567 * [I] uMsg : the message that is to be processed
11568 * [I] wParam : first message parameter
11569 * [I] lParam : second message parameter
11573 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
11575 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
11580 * Creates a subclassed edit control
11583 * [I] infoPtr : valid pointer to the listview structure
11584 * [I] text : initial text for the edit
11585 * [I] style : the window style
11586 * [I] isW : TRUE if input is Unicode
11590 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style, BOOL isW)
11593 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
11595 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
11597 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
11599 /* Window will be resized and positioned after LVN_BEGINLABELEDIT */
11601 hedit = CreateWindowW(WC_EDITW, text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0);
11603 hedit = CreateWindowA(WC_EDITA, (LPCSTR)text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0);
11605 if (!hedit) return 0;
11607 infoPtr->EditWndProc = (WNDPROC)
11608 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
11609 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
11611 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);