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)
83 * -- LVS_NOSCROLL (see Q137520)
87 * -- LVS_EX_BORDERSELECT
91 * -- LVS_EX_MULTIWORKAREAS
93 * -- LVS_EX_SIMPLESELECT
94 * -- LVS_EX_TWOCLICKACTIVATE
95 * -- LVS_EX_UNDERLINECOLD
96 * -- LVS_EX_UNDERLINEHOT
99 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
107 * -- LVM_ENABLEGROUPVIEW
108 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
109 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
110 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
111 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
112 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
113 * -- LVM_GETINSERTMARKRECT
114 * -- LVM_GETNUMBEROFWORKAREAS
115 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
116 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
117 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
118 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
119 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
120 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
121 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
122 * -- LVM_INSERTGROUPSORTED
123 * -- LVM_INSERTMARKHITTEST
124 * -- LVM_ISGROUPVIEWENABLED
126 * -- LVM_MOVEITEMTOGROUP
128 * -- LVM_SETTILEWIDTH
132 * -- ListView_GetHoverTime, ListView_SetHoverTime
133 * -- ListView_GetISearchString
134 * -- ListView_GetNumberOfWorkAreas
135 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
140 * Known differences in message stream from native control (not known if
141 * these differences cause problems):
142 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
143 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
144 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
145 * processing for "USEDOUBLECLICKTIME".
149 #include "wine/port.h"
164 #include "commctrl.h"
165 #include "comctl32.h"
168 #include "wine/debug.h"
169 #include "wine/unicode.h"
171 WINE_DEFAULT_DEBUG_CHANNEL(listview);
173 /* make sure you set this to 0 for production use! */
174 #define DEBUG_RANGES 1
176 typedef struct tagCOLUMN_INFO
178 RECT rcHeader; /* tracks the header's rectangle */
179 INT fmt; /* same as LVCOLUMN.fmt */
183 typedef struct tagITEMHDR
187 } ITEMHDR, *LPITEMHDR;
189 typedef struct tagSUBITEM_INFO
195 typedef struct tagITEM_ID ITEM_ID;
197 typedef struct tagITEM_INFO
208 UINT id; /* item id */
209 HDPA item; /* link to item data */
212 typedef struct tagRANGE
218 typedef struct tagRANGES
223 typedef struct tagITERATOR
232 typedef struct tagDELAYED_ITEM_EDIT
238 typedef struct tagLISTVIEW_INFO
242 RECT rcList; /* This rectangle is really the window
243 * client rectangle possibly reduced by the
244 * horizontal scroll bar and/or header - see
245 * LISTVIEW_UpdateSize. This rectangle offset
246 * by the LISTVIEW_GetOrigin value is in
247 * client coordinates */
249 /* notification window */
252 BOOL bDoChangeNotify; /* send change notification messages? */
259 INT nItemCount; /* the number of items in the list */
260 HDPA hdpaItems; /* array ITEM_INFO pointers */
261 HDPA hdpaItemIds; /* array of ITEM_ID pointers */
262 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
263 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
264 RANGES selectionRanges;
265 INT nSelectionMark; /* item to start next multiselection from */
267 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
270 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
271 BOOL colRectsDirty; /* trigger column rectangles requery from header */
274 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
279 PFNLVCOMPARE pfnCompare; /* sorting callback pointer */
283 DWORD dwStyle; /* the cached window GWL_STYLE */
284 DWORD dwLvExStyle; /* extended listview style */
285 DWORD uView; /* current view available through LVM_[G,S]ETVIEW */
291 DELAYED_ITEM_EDIT itemEdit; /* Pointer to this structure will be the timer ID */
294 HIMAGELIST himlNormal;
295 HIMAGELIST himlSmall;
296 HIMAGELIST himlState;
300 POINT currIconPos; /* this is the position next icon will be placed */
304 INT xTrackLine; /* The x coefficient of the track line or -1 if none */
306 /* marquee selection */
307 BOOL bMarqueeSelect; /* marquee selection/highlight underway */
309 RECT marqueeRect; /* absolute coordinates of marquee selection */
310 RECT marqueeDrawRect; /* relative coordinates for drawing marquee */
311 POINT marqueeOrigin; /* absolute coordinates of marquee click origin */
314 BOOL bFocus; /* control has focus */
316 RECT rcFocus; /* focus bounds */
327 INT ntmHeight; /* Some cached metrics of the font used */
328 INT ntmMaxCharWidth; /* by the listview to draw items */
331 /* mouse operation */
335 POINT ptClickPos; /* point where the user clicked */
336 INT nLButtonDownItem; /* tracks item to reset multiselection on WM_LBUTTONUP */
340 /* keyboard operation */
341 DWORD lastKeyPressTimestamp;
343 INT nSearchParamLength;
344 WCHAR szSearchParam[ MAX_PATH ];
347 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
348 BOOL bIsDrawing; /* Drawing in progress */
349 INT nMeasureItemHeight; /* WM_MEASUREITEM result */
350 BOOL bRedraw; /* WM_SETREDRAW switch */
353 DWORD iVersion; /* CCM_[G,S]ETVERSION */
359 /* How many we debug buffer to allocate */
360 #define DEBUG_BUFFERS 20
361 /* The size of a single debug buffer */
362 #define DEBUG_BUFFER_SIZE 256
364 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
365 #define SB_INTERNAL -1
367 /* maximum size of a label */
368 #define DISP_TEXT_SIZE 512
370 /* padding for items in list and small icon display modes */
371 #define WIDTH_PADDING 12
373 /* padding for items in list, report and small icon display modes */
374 #define HEIGHT_PADDING 1
376 /* offset of items in report display mode */
377 #define REPORT_MARGINX 2
379 /* padding for icon in large icon display mode
380 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
381 * that HITTEST will see.
382 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
383 * ICON_TOP_PADDING - sum of the two above.
384 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
385 * LABEL_HOR_PADDING - between text and sides of box
386 * LABEL_VERT_PADDING - between bottom of text and end of box
388 * ICON_LR_PADDING - additional width above icon size.
389 * ICON_LR_HALF - half of the above value
391 #define ICON_TOP_PADDING_NOTHITABLE 2
392 #define ICON_TOP_PADDING_HITABLE 2
393 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
394 #define ICON_BOTTOM_PADDING 4
395 #define LABEL_HOR_PADDING 5
396 #define LABEL_VERT_PADDING 7
397 #define ICON_LR_PADDING 16
398 #define ICON_LR_HALF (ICON_LR_PADDING/2)
400 /* default label width for items in list and small icon display modes */
401 #define DEFAULT_LABEL_WIDTH 40
402 /* maximum select rectangle width for empty text item in LV_VIEW_DETAILS */
403 #define MAX_EMPTYTEXT_SELECT_WIDTH 80
405 /* default column width for items in list display mode */
406 #define DEFAULT_COLUMN_WIDTH 96
408 /* Size of "line" scroll for V & H scrolls */
409 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
411 /* Padding between image and label */
412 #define IMAGE_PADDING 2
414 /* Padding behind the label */
415 #define TRAILING_LABEL_PADDING 12
416 #define TRAILING_HEADER_PADDING 11
418 /* Border for the icon caption */
419 #define CAPTION_BORDER 2
421 /* Standard DrawText flags */
422 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
423 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
424 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
426 /* Image index from state */
427 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
429 /* The time in milliseconds to reset the search in the list */
430 #define KEY_DELAY 450
432 /* Dump the LISTVIEW_INFO structure to the debug channel */
433 #define LISTVIEW_DUMP(iP) do { \
434 TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
435 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
436 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
437 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
438 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
439 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
440 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
441 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
442 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
443 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
446 static const WCHAR themeClass[] = {'L','i','s','t','V','i','e','w',0};
449 * forward declarations
451 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *, LPLVITEMW, BOOL);
452 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *, INT, LPRECT);
453 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *, INT, LPPOINT);
454 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *, INT, LPPOINT);
455 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *, INT, LPRECT);
456 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *, INT);
457 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *, LPPOINT);
458 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *, LPRECT);
459 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
460 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
461 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *, LPCWSTR, BOOL);
462 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT, BOOL);
463 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *, INT, UINT);
464 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
465 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
466 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
467 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
468 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, BOOL);
469 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
470 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
471 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *, BOOL, BOOL);
472 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *, INT, INT);
474 /******** Text handling functions *************************************/
476 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
477 * text string. The string may be ANSI or Unicode, in which case
478 * the boolean isW tells us the type of the string.
480 * The name of the function tell what type of strings it expects:
481 * W: Unicode, T: ANSI/Unicode - function of isW
484 static inline BOOL is_textW(LPCWSTR text)
486 return text != NULL && text != LPSTR_TEXTCALLBACKW;
489 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
491 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
492 return is_textW(text);
495 static inline int textlenT(LPCWSTR text, BOOL isW)
497 return !is_textT(text, isW) ? 0 :
498 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
501 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
504 if (isSrcW) lstrcpynW(dest, src, max);
505 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
507 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
508 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
511 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
513 LPWSTR wstr = (LPWSTR)text;
515 if (!isW && is_textT(text, isW))
517 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
518 wstr = Alloc(len * sizeof(WCHAR));
519 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
521 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
525 static inline void textfreeT(LPWSTR wstr, BOOL isW)
527 if (!isW && is_textT(wstr, isW)) Free (wstr);
531 * dest is a pointer to a Unicode string
532 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
534 static BOOL textsetptrT(LPWSTR *dest, LPCWSTR src, BOOL isW)
538 if (src == LPSTR_TEXTCALLBACKW)
540 if (is_textW(*dest)) Free(*dest);
541 *dest = LPSTR_TEXTCALLBACKW;
545 LPWSTR pszText = textdupTtoW(src, isW);
546 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
547 bResult = Str_SetPtrW(dest, pszText);
548 textfreeT(pszText, isW);
554 * compares a Unicode to a Unicode/ANSI text string
556 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
558 if (!aw) return bt ? -1 : 0;
559 if (!bt) return aw ? 1 : 0;
560 if (aw == LPSTR_TEXTCALLBACKW)
561 return bt == LPSTR_TEXTCALLBACKW ? 1 : -1;
562 if (bt != LPSTR_TEXTCALLBACKW)
564 LPWSTR bw = textdupTtoW(bt, isW);
565 int r = bw ? lstrcmpW(aw, bw) : 1;
573 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
577 n = min(min(n, lstrlenW(s1)), lstrlenW(s2));
578 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
579 return res ? res - sizeof(WCHAR) : res;
582 /******** Debugging functions *****************************************/
584 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
586 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
587 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
590 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
592 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
593 n = min(textlenT(text, isW), n);
594 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
597 static char* debug_getbuf(void)
599 static int index = 0;
600 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
601 return buffers[index++ % DEBUG_BUFFERS];
604 static inline const char* debugrange(const RANGE *lprng)
606 if (!lprng) return "(null)";
607 return wine_dbg_sprintf("[%d, %d]", lprng->lower, lprng->upper);
610 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
612 char* buf = debug_getbuf(), *text = buf;
613 int len, size = DEBUG_BUFFER_SIZE;
615 if (pScrollInfo == NULL) return "(null)";
616 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
617 if (len == -1) goto end; buf += len; size -= len;
618 if (pScrollInfo->fMask & SIF_RANGE)
619 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
621 if (len == -1) goto end; buf += len; size -= len;
622 if (pScrollInfo->fMask & SIF_PAGE)
623 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
625 if (len == -1) goto end; buf += len; size -= len;
626 if (pScrollInfo->fMask & SIF_POS)
627 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
629 if (len == -1) goto end; buf += len; size -= len;
630 if (pScrollInfo->fMask & SIF_TRACKPOS)
631 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
633 if (len == -1) goto end; buf += len; size -= len;
636 buf = text + strlen(text);
638 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
642 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
644 if (!plvnm) return "(null)";
645 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
646 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
647 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
648 plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam);
651 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
653 char* buf = debug_getbuf(), *text = buf;
654 int len, size = DEBUG_BUFFER_SIZE;
656 if (lpLVItem == NULL) return "(null)";
657 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
658 if (len == -1) goto end; buf += len; size -= len;
659 if (lpLVItem->mask & LVIF_STATE)
660 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
662 if (len == -1) goto end; buf += len; size -= len;
663 if (lpLVItem->mask & LVIF_TEXT)
664 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
666 if (len == -1) goto end; buf += len; size -= len;
667 if (lpLVItem->mask & LVIF_IMAGE)
668 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
670 if (len == -1) goto end; buf += len; size -= len;
671 if (lpLVItem->mask & LVIF_PARAM)
672 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
674 if (len == -1) goto end; buf += len; size -= len;
675 if (lpLVItem->mask & LVIF_INDENT)
676 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
678 if (len == -1) goto end; buf += len; size -= len;
681 buf = text + strlen(text);
683 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
687 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
689 char* buf = debug_getbuf(), *text = buf;
690 int len, size = DEBUG_BUFFER_SIZE;
692 if (lpColumn == NULL) return "(null)";
693 len = snprintf(buf, size, "{");
694 if (len == -1) goto end; buf += len; size -= len;
695 if (lpColumn->mask & LVCF_SUBITEM)
696 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
698 if (len == -1) goto end; buf += len; size -= len;
699 if (lpColumn->mask & LVCF_FMT)
700 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
702 if (len == -1) goto end; buf += len; size -= len;
703 if (lpColumn->mask & LVCF_WIDTH)
704 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
706 if (len == -1) goto end; buf += len; size -= len;
707 if (lpColumn->mask & LVCF_TEXT)
708 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
710 if (len == -1) goto end; buf += len; size -= len;
711 if (lpColumn->mask & LVCF_IMAGE)
712 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
714 if (len == -1) goto end; buf += len; size -= len;
715 if (lpColumn->mask & LVCF_ORDER)
716 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
718 if (len == -1) goto end; buf += len; size -= len;
721 buf = text + strlen(text);
723 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
727 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
729 if (!lpht) return "(null)";
731 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
732 wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
735 /* Return the corresponding text for a given scroll value */
736 static inline LPCSTR debugscrollcode(int nScrollCode)
740 case SB_LINELEFT: return "SB_LINELEFT";
741 case SB_LINERIGHT: return "SB_LINERIGHT";
742 case SB_PAGELEFT: return "SB_PAGELEFT";
743 case SB_PAGERIGHT: return "SB_PAGERIGHT";
744 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
745 case SB_THUMBTRACK: return "SB_THUMBTRACK";
746 case SB_ENDSCROLL: return "SB_ENDSCROLL";
747 case SB_INTERNAL: return "SB_INTERNAL";
748 default: return "unknown";
753 /******** Notification functions ************************************/
755 static int get_ansi_notification(UINT unicodeNotificationCode)
757 switch (unicodeNotificationCode)
759 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
760 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
761 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
762 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
763 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
764 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
765 /* header forwards */
766 case HDN_TRACKW: return HDN_TRACKA;
767 case HDN_ENDTRACKW: return HDN_ENDTRACKA;
768 case HDN_BEGINDRAG: return HDN_BEGINDRAG;
769 case HDN_ENDDRAG: return HDN_ENDDRAG;
770 case HDN_ITEMCHANGINGW: return HDN_ITEMCHANGINGA;
771 case HDN_ITEMCHANGEDW: return HDN_ITEMCHANGEDA;
772 case HDN_ITEMCLICKW: return HDN_ITEMCLICKA;
773 case HDN_DIVIDERDBLCLICKW: return HDN_DIVIDERDBLCLICKA;
775 ERR("unknown notification %x\n", unicodeNotificationCode);
780 /* forwards header notifications to listview parent */
781 static LRESULT notify_forward_header(const LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
785 HD_TEXTFILTERA textfilter;
786 LPSTR text = NULL, filter = NULL;
789 /* on unicode format exit earlier */
790 if (infoPtr->notifyFormat == NFR_UNICODE)
791 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
792 (WPARAM)lpnmh->hdr.idFrom, (LPARAM)lpnmh);
794 /* header always supplies unicode notifications,
795 all we have to do is to convert strings to ANSI */
796 nmhA = *(NMHEADERA*)lpnmh;
799 hditema = *(HDITEMA*)lpnmh->pitem;
800 nmhA.pitem = &hditema;
801 /* convert item text */
802 if (lpnmh->pitem->mask & HDI_TEXT)
804 hditema.pszText = NULL;
805 Str_SetPtrWtoA(&hditema.pszText, lpnmh->pitem->pszText);
806 text = hditema.pszText;
808 /* convert filter text */
809 if ((lpnmh->pitem->mask & HDI_FILTER) && (lpnmh->pitem->type == HDFT_ISSTRING) &&
810 lpnmh->pitem->pvFilter)
812 hditema.pvFilter = &textfilter;
813 textfilter = *(HD_TEXTFILTERA*)(lpnmh->pitem->pvFilter);
814 textfilter.pszText = NULL;
815 Str_SetPtrWtoA(&textfilter.pszText, ((HD_TEXTFILTERW*)lpnmh->pitem->pvFilter)->pszText);
816 filter = textfilter.pszText;
819 nmhA.hdr.code = get_ansi_notification(lpnmh->hdr.code);
821 ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
822 (WPARAM)nmhA.hdr.idFrom, (LPARAM)&nmhA);
831 static LRESULT notify_hdr(const LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
835 TRACE("(code=%d)\n", code);
837 pnmh->hwndFrom = infoPtr->hwndSelf;
838 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
840 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, pnmh->idFrom, (LPARAM)pnmh);
842 TRACE(" <= %ld\n", result);
847 static inline BOOL notify(const LISTVIEW_INFO *infoPtr, INT code)
850 HWND hwnd = infoPtr->hwndSelf;
851 notify_hdr(infoPtr, code, &nmh);
852 return IsWindow(hwnd);
855 static inline void notify_itemactivate(const LISTVIEW_INFO *infoPtr, const LVHITTESTINFO *htInfo)
866 item.mask = LVIF_PARAM|LVIF_STATE;
867 item.iItem = htInfo->iItem;
869 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
870 nmia.lParam = item.lParam;
871 nmia.uOldState = item.state;
872 nmia.uNewState = item.state | LVIS_ACTIVATING;
873 nmia.uChanged = LVIF_STATE;
876 nmia.iItem = htInfo->iItem;
877 nmia.iSubItem = htInfo->iSubItem;
878 nmia.ptAction = htInfo->pt;
880 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
881 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
882 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
884 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
887 static inline LRESULT notify_listview(const LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
889 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
890 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
893 static BOOL notify_click(const LISTVIEW_INFO *infoPtr, INT code, const LVHITTESTINFO *lvht)
897 HWND hwnd = infoPtr->hwndSelf;
899 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
900 ZeroMemory(&nmia, sizeof(nmia));
901 nmia.iItem = lvht->iItem;
902 nmia.iSubItem = lvht->iSubItem;
903 nmia.ptAction = lvht->pt;
904 item.mask = LVIF_PARAM;
905 item.iItem = lvht->iItem;
907 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmia.lParam = item.lParam;
908 notify_hdr(infoPtr, code, (LPNMHDR)&nmia);
909 return IsWindow(hwnd);
912 static BOOL notify_deleteitem(const LISTVIEW_INFO *infoPtr, INT nItem)
916 HWND hwnd = infoPtr->hwndSelf;
918 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
920 item.mask = LVIF_PARAM;
923 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
924 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
925 return IsWindow(hwnd);
929 Send notification. depends on dispinfoW having same
930 structure as dispinfoA.
931 infoPtr : listview struct
932 notificationCode : *Unicode* notification code
933 pdi : dispinfo structure (can be unicode or ansi)
934 isW : TRUE if dispinfo is Unicode
936 static BOOL notify_dispinfoT(const LISTVIEW_INFO *infoPtr, UINT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
938 BOOL bResult = FALSE;
939 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
940 INT cchTempBufMax = 0, savCchTextMax = 0;
942 LPWSTR pszTempBuf = NULL, savPszText = NULL;
944 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
946 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
947 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
950 if (convertToAnsi || convertToUnicode)
952 if (notificationCode != LVN_GETDISPINFOW)
954 cchTempBufMax = convertToUnicode ?
955 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
956 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
960 cchTempBufMax = pdi->item.cchTextMax;
961 *pdi->item.pszText = 0; /* make sure we don't process garbage */
964 pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
965 if (!pszTempBuf) return FALSE;
967 if (convertToUnicode)
968 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
969 pszTempBuf, cchTempBufMax);
971 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
972 cchTempBufMax, NULL, NULL);
974 savCchTextMax = pdi->item.cchTextMax;
975 savPszText = pdi->item.pszText;
976 pdi->item.pszText = pszTempBuf;
977 pdi->item.cchTextMax = cchTempBufMax;
980 if (infoPtr->notifyFormat == NFR_ANSI)
981 realNotifCode = get_ansi_notification(notificationCode);
983 realNotifCode = notificationCode;
984 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
985 bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
987 if (convertToUnicode || convertToAnsi)
989 if (convertToUnicode) /* note : pointer can be changed by app ! */
990 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
991 savCchTextMax, NULL, NULL);
993 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
994 savPszText, savCchTextMax);
995 pdi->item.pszText = savPszText; /* restores our buffer */
996 pdi->item.cchTextMax = savCchTextMax;
1002 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, const LISTVIEW_INFO *infoPtr, HDC hdc,
1003 const RECT *rcBounds, const LVITEMW *lplvItem)
1005 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
1006 lpnmlvcd->nmcd.hdc = hdc;
1007 lpnmlvcd->nmcd.rc = *rcBounds;
1008 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
1009 lpnmlvcd->clrText = infoPtr->clrText;
1010 if (!lplvItem) return;
1011 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
1012 lpnmlvcd->iSubItem = lplvItem->iSubItem;
1013 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
1014 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
1015 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
1016 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
1019 static inline DWORD notify_customdraw (const LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
1021 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
1024 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
1025 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
1026 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
1027 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
1028 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
1029 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
1033 static void prepaint_setup (const LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd, BOOL SubItem)
1035 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
1036 lpnmlvcd->clrTextBk = comctl32_color.clrWindow;
1037 if (lpnmlvcd->clrText == CLR_DEFAULT)
1038 lpnmlvcd->clrText = comctl32_color.clrWindowText;
1040 /* apparently, for selected items, we have to override the returned values */
1043 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
1045 if (infoPtr->bFocus)
1047 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
1048 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
1050 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
1052 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
1053 lpnmlvcd->clrText = comctl32_color.clrBtnText;
1058 /* Set the text attributes */
1059 if (lpnmlvcd->clrTextBk != CLR_NONE)
1061 SetBkMode(hdc, OPAQUE);
1062 SetBkColor(hdc,lpnmlvcd->clrTextBk);
1065 SetBkMode(hdc, TRANSPARENT);
1066 SetTextColor(hdc, lpnmlvcd->clrText);
1069 static inline DWORD notify_postpaint (const LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
1071 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
1074 /* returns TRUE when repaint needed, FALSE otherwise */
1075 static BOOL notify_measureitem(LISTVIEW_INFO *infoPtr)
1077 MEASUREITEMSTRUCT mis;
1078 mis.CtlType = ODT_LISTVIEW;
1079 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1083 mis.itemHeight= infoPtr->nItemHeight;
1084 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
1085 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
1087 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
1093 /******** Item iterator functions **********************************/
1095 static RANGES ranges_create(int count);
1096 static void ranges_destroy(RANGES ranges);
1097 static BOOL ranges_add(RANGES ranges, RANGE range);
1098 static BOOL ranges_del(RANGES ranges, RANGE range);
1099 static void ranges_dump(RANGES ranges);
1101 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
1103 RANGE range = { nItem, nItem + 1 };
1105 return ranges_add(ranges, range);
1108 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
1110 RANGE range = { nItem, nItem + 1 };
1112 return ranges_del(ranges, range);
1116 * ITERATOR DOCUMENTATION
1118 * The iterator functions allow for easy, and convenient iteration
1119 * over items of interest in the list. Typically, you create a
1120 * iterator, use it, and destroy it, as such:
1123 * iterator_xxxitems(&i, ...);
1124 * while (iterator_{prev,next}(&i)
1126 * //code which uses i.nItem
1128 * iterator_destroy(&i);
1130 * where xxx is either: framed, or visible.
1131 * Note that it is important that the code destroys the iterator
1132 * after it's done with it, as the creation of the iterator may
1133 * allocate memory, which thus needs to be freed.
1135 * You can iterate both forwards, and backwards through the list,
1136 * by using iterator_next or iterator_prev respectively.
1138 * Lower numbered items are draw on top of higher number items in
1139 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1140 * items may overlap). So, to test items, you should use
1142 * which lists the items top to bottom (in Z-order).
1143 * For drawing items, you should use
1145 * which lists the items bottom to top (in Z-order).
1146 * If you keep iterating over the items after the end-of-items
1147 * marker (-1) is returned, the iterator will start from the
1148 * beginning. Typically, you don't need to test for -1,
1149 * because iterator_{next,prev} will return TRUE if more items
1150 * are to be iterated over, or FALSE otherwise.
1152 * Note: the iterator is defined to be bidirectional. That is,
1153 * any number of prev followed by any number of next, or
1154 * five versa, should leave the iterator at the same item:
1155 * prev * n, next * n = next * n, prev * n
1157 * The iterator has a notion of an out-of-order, special item,
1158 * which sits at the start of the list. This is used in
1159 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1160 * which needs to be first, as it may overlap other items.
1162 * The code is a bit messy because we have:
1163 * - a special item to deal with
1164 * - simple range, or composite range
1166 * If you find bugs, or want to add features, please make sure you
1167 * always check/modify *both* iterator_prev, and iterator_next.
1171 * This function iterates through the items in increasing order,
1172 * but prefixed by the special item, then -1. That is:
1173 * special, 1, 2, 3, ..., n, -1.
1174 * Each item is listed only once.
1176 static inline BOOL iterator_next(ITERATOR* i)
1180 i->nItem = i->nSpecial;
1181 if (i->nItem != -1) return TRUE;
1183 if (i->nItem == i->nSpecial)
1185 if (i->ranges) i->index = 0;
1191 if (i->nItem == i->nSpecial) i->nItem++;
1192 if (i->nItem < i->range.upper) return TRUE;
1197 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1198 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1201 else if (i->nItem >= i->range.upper) goto end;
1203 i->nItem = i->range.lower;
1204 if (i->nItem >= 0) goto testitem;
1211 * This function iterates through the items in decreasing order,
1212 * followed by the special item, then -1. That is:
1213 * n, n-1, ..., 3, 2, 1, special, -1.
1214 * Each item is listed only once.
1216 static inline BOOL iterator_prev(ITERATOR* i)
1223 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1226 if (i->nItem == i->nSpecial)
1234 if (i->nItem == i->nSpecial) i->nItem--;
1235 if (i->nItem >= i->range.lower) return TRUE;
1241 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1244 else if (!start && i->nItem < i->range.lower) goto end;
1246 i->nItem = i->range.upper;
1247 if (i->nItem > 0) goto testitem;
1249 return (i->nItem = i->nSpecial) != -1;
1252 static RANGE iterator_range(const ITERATOR *i)
1256 if (!i->ranges) return i->range;
1258 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1260 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1261 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1263 else range.lower = range.upper = 0;
1269 * Releases resources associated with this ierator.
1271 static inline void iterator_destroy(const ITERATOR *i)
1273 ranges_destroy(i->ranges);
1277 * Create an empty iterator.
1279 static inline BOOL iterator_empty(ITERATOR* i)
1281 ZeroMemory(i, sizeof(*i));
1282 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1287 * Create an iterator over a range.
1289 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1297 * Create an iterator over a bunch of ranges.
1298 * Please note that the iterator will take ownership of the ranges,
1299 * and will free them upon destruction.
1301 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1309 * Creates an iterator over the items which intersect frame.
1310 * Uses absolute coordinates rather than compensating for the current offset.
1312 static BOOL iterator_frameditems_absolute(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *frame)
1314 RECT rcItem, rcTemp;
1316 /* in case we fail, we want to return an empty iterator */
1317 if (!iterator_empty(i)) return FALSE;
1319 TRACE("(frame=%s)\n", wine_dbgstr_rect(frame));
1321 if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)
1325 if (infoPtr->uView == LV_VIEW_ICON && infoPtr->nFocusedItem != -1)
1327 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1328 if (IntersectRect(&rcTemp, &rcItem, frame))
1329 i->nSpecial = infoPtr->nFocusedItem;
1331 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1332 /* to do better here, we need to have PosX, and PosY sorted */
1333 TRACE("building icon ranges:\n");
1334 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1336 rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1337 rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1338 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1339 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1340 if (IntersectRect(&rcTemp, &rcItem, frame))
1341 ranges_additem(i->ranges, nItem);
1345 else if (infoPtr->uView == LV_VIEW_DETAILS)
1349 if (frame->left >= infoPtr->nItemWidth) return TRUE;
1350 if (frame->top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1352 range.lower = max(frame->top / infoPtr->nItemHeight, 0);
1353 range.upper = min((frame->bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1354 if (range.upper <= range.lower) return TRUE;
1355 if (!iterator_rangeitems(i, range)) return FALSE;
1356 TRACE(" report=%s\n", debugrange(&i->range));
1360 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1361 INT nFirstRow = max(frame->top / infoPtr->nItemHeight, 0);
1362 INT nLastRow = min((frame->bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1369 if (infoPtr->nItemWidth)
1371 nFirstCol = max(frame->left / infoPtr->nItemWidth, 0);
1372 nLastCol = min((frame->right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1376 nFirstCol = max(frame->left, 0);
1377 nLastCol = min(frame->right - 1, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1380 lower = nFirstCol * nPerCol + nFirstRow;
1382 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1383 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1385 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1387 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1388 TRACE("building list ranges:\n");
1389 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1391 item_range.lower = nCol * nPerCol + nFirstRow;
1392 if(item_range.lower >= infoPtr->nItemCount) break;
1393 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1394 TRACE(" list=%s\n", debugrange(&item_range));
1395 ranges_add(i->ranges, item_range);
1403 * Creates an iterator over the items which intersect lprc.
1405 static BOOL iterator_frameditems(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *lprc)
1410 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc));
1412 LISTVIEW_GetOrigin(infoPtr, &Origin);
1413 OffsetRect(&frame, -Origin.x, -Origin.y);
1415 return iterator_frameditems_absolute(i, infoPtr, &frame);
1419 * Creates an iterator over the items which intersect the visible region of hdc.
1421 static BOOL iterator_visibleitems(ITERATOR *i, const LISTVIEW_INFO *infoPtr, HDC hdc)
1423 POINT Origin, Position;
1424 RECT rcItem, rcClip;
1427 rgntype = GetClipBox(hdc, &rcClip);
1428 if (rgntype == NULLREGION) return iterator_empty(i);
1429 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1430 if (rgntype == SIMPLEREGION) return TRUE;
1432 /* first deal with the special item */
1433 if (i->nSpecial != -1)
1435 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1436 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1439 /* if we can't deal with the region, we'll just go with the simple range */
1440 LISTVIEW_GetOrigin(infoPtr, &Origin);
1441 TRACE("building visible range:\n");
1442 if (!i->ranges && i->range.lower < i->range.upper)
1444 if (!(i->ranges = ranges_create(50))) return TRUE;
1445 if (!ranges_add(i->ranges, i->range))
1447 ranges_destroy(i->ranges);
1453 /* now delete the invisible items from the list */
1454 while(iterator_next(i))
1456 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1457 rcItem.left = (infoPtr->uView == LV_VIEW_DETAILS) ? Origin.x : Position.x + Origin.x;
1458 rcItem.top = Position.y + Origin.y;
1459 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1460 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1461 if (!RectVisible(hdc, &rcItem))
1462 ranges_delitem(i->ranges, i->nItem);
1464 /* the iterator should restart on the next iterator_next */
1470 /******** Misc helper functions ************************************/
1472 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1473 WPARAM wParam, LPARAM lParam, BOOL isW)
1475 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1476 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1479 static inline BOOL is_autoarrange(const LISTVIEW_INFO *infoPtr)
1481 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1482 (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON);
1485 static void toggle_checkbox_state(LISTVIEW_INFO *infoPtr, INT nItem)
1487 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
1488 if(state == 1 || state == 2)
1492 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
1493 lvitem.stateMask = LVIS_STATEIMAGEMASK;
1494 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
1498 /* this should be called after window style got updated,
1499 it used to reset view state to match current window style */
1500 static inline void map_style_view(LISTVIEW_INFO *infoPtr)
1502 switch (infoPtr->dwStyle & LVS_TYPEMASK)
1505 infoPtr->uView = LV_VIEW_ICON;
1508 infoPtr->uView = LV_VIEW_DETAILS;
1511 infoPtr->uView = LV_VIEW_SMALLICON;
1514 infoPtr->uView = LV_VIEW_LIST;
1518 /* computes next item id value */
1519 static DWORD get_next_itemid(const LISTVIEW_INFO *infoPtr)
1521 INT count = DPA_GetPtrCount(infoPtr->hdpaItemIds);
1525 ITEM_ID *lpID = DPA_GetPtr(infoPtr->hdpaItemIds, count - 1);
1526 return lpID->id + 1;
1531 /******** Internal API functions ************************************/
1533 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(const LISTVIEW_INFO *infoPtr, INT nSubItem)
1535 static COLUMN_INFO mainItem;
1537 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1538 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1540 /* update cached column rectangles */
1541 if (infoPtr->colRectsDirty)
1544 LISTVIEW_INFO *Ptr = (LISTVIEW_INFO*)infoPtr;
1547 for (i = 0; i < DPA_GetPtrCount(infoPtr->hdpaColumns); i++) {
1548 info = DPA_GetPtr(infoPtr->hdpaColumns, i);
1549 SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, i, (LPARAM)&info->rcHeader);
1551 Ptr->colRectsDirty = FALSE;
1554 return DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1557 static INT LISTVIEW_CreateHeader(LISTVIEW_INFO *infoPtr)
1559 DWORD dFlags = WS_CHILD | HDS_HORZ | HDS_FULLDRAG | HDS_DRAGDROP;
1562 if (infoPtr->hwndHeader) return 0;
1564 TRACE("Creating header for list %p\n", infoPtr->hwndSelf);
1566 /* setup creation flags */
1567 dFlags |= (LVS_NOSORTHEADER & infoPtr->dwStyle) ? 0 : HDS_BUTTONS;
1568 dFlags |= (LVS_NOCOLUMNHEADER & infoPtr->dwStyle) ? HDS_HIDDEN : 0;
1570 hInst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
1573 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL, dFlags,
1574 0, 0, 0, 0, infoPtr->hwndSelf, NULL, hInst, NULL);
1575 if (!infoPtr->hwndHeader) return -1;
1577 /* set header unicode format */
1578 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, TRUE, 0);
1580 /* set header font */
1581 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
1583 LISTVIEW_UpdateSize(infoPtr);
1588 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO *infoPtr, INT nSubItem, LPRECT lprc)
1590 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1593 static inline BOOL LISTVIEW_GetItemW(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1595 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1598 /* used to handle collapse main item column case */
1599 static inline BOOL LISTVIEW_DrawFocusRect(const LISTVIEW_INFO *infoPtr, HDC hdc)
1601 return (infoPtr->rcFocus.left < infoPtr->rcFocus.right) ?
1602 DrawFocusRect(hdc, &infoPtr->rcFocus) : FALSE;
1605 /* Listview invalidation functions: use _only_ these functions to invalidate */
1607 static inline BOOL is_redrawing(const LISTVIEW_INFO *infoPtr)
1609 return infoPtr->bRedraw;
1612 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO *infoPtr, const RECT* rect)
1614 if(!is_redrawing(infoPtr)) return;
1615 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect));
1616 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1619 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO *infoPtr, INT nItem)
1623 if(!is_redrawing(infoPtr)) return;
1624 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1625 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1628 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1630 POINT Origin, Position;
1633 if(!is_redrawing(infoPtr)) return;
1634 assert (infoPtr->uView == LV_VIEW_DETAILS);
1635 LISTVIEW_GetOrigin(infoPtr, &Origin);
1636 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1637 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1639 rcBox.bottom = infoPtr->nItemHeight;
1640 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1641 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1644 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO *infoPtr)
1646 LISTVIEW_InvalidateRect(infoPtr, NULL);
1649 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO *infoPtr, INT nColumn)
1653 if(!is_redrawing(infoPtr)) return;
1654 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1655 rcCol.top = infoPtr->rcList.top;
1656 rcCol.bottom = infoPtr->rcList.bottom;
1657 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1662 * Retrieves the number of items that can fit vertically in the client area.
1665 * [I] infoPtr : valid pointer to the listview structure
1668 * Number of items per row.
1670 static inline INT LISTVIEW_GetCountPerRow(const LISTVIEW_INFO *infoPtr)
1672 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1674 return max(nListWidth/(infoPtr->nItemWidth ? infoPtr->nItemWidth : 1), 1);
1679 * Retrieves the number of items that can fit horizontally in the client
1683 * [I] infoPtr : valid pointer to the listview structure
1686 * Number of items per column.
1688 static inline INT LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO *infoPtr)
1690 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1692 return max(nListHeight / infoPtr->nItemHeight, 1);
1696 /*************************************************************************
1697 * LISTVIEW_ProcessLetterKeys
1699 * Processes keyboard messages generated by pressing the letter keys
1701 * What this does is perform a case insensitive search from the
1702 * current position with the following quirks:
1703 * - If two chars or more are pressed in quick succession we search
1704 * for the corresponding string (e.g. 'abc').
1705 * - If there is a delay we wipe away the current search string and
1706 * restart with just that char.
1707 * - If the user keeps pressing the same character, whether slowly or
1708 * fast, so that the search string is entirely composed of this
1709 * character ('aaaaa' for instance), then we search for first item
1710 * that starting with that character.
1711 * - If the user types the above character in quick succession, then
1712 * we must also search for the corresponding string ('aaaaa'), and
1713 * go to that string if there is a match.
1716 * [I] hwnd : handle to the window
1717 * [I] charCode : the character code, the actual character
1718 * [I] keyData : key data
1726 * - The current implementation has a list of characters it will
1727 * accept and it ignores everything else. In particular it will
1728 * ignore accentuated characters which seems to match what
1729 * Windows does. But I'm not sure it makes sense to follow
1731 * - We don't sound a beep when the search fails.
1735 * TREEVIEW_ProcessLetterKeys
1737 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1742 WCHAR buffer[MAX_PATH];
1743 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1745 /* simple parameter checking */
1746 if (!charCode || !keyData) return 0;
1748 /* only allow the valid WM_CHARs through */
1749 if (!isalnumW(charCode) &&
1750 charCode != '.' && charCode != '`' && charCode != '!' &&
1751 charCode != '@' && charCode != '#' && charCode != '$' &&
1752 charCode != '%' && charCode != '^' && charCode != '&' &&
1753 charCode != '*' && charCode != '(' && charCode != ')' &&
1754 charCode != '-' && charCode != '_' && charCode != '+' &&
1755 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1756 charCode != '}' && charCode != '[' && charCode != '{' &&
1757 charCode != '/' && charCode != '?' && charCode != '>' &&
1758 charCode != '<' && charCode != ',' && charCode != '~')
1761 /* if there's one item or less, there is no where to go */
1762 if (infoPtr->nItemCount <= 1) return 0;
1764 /* update the search parameters */
1765 infoPtr->lastKeyPressTimestamp = GetTickCount();
1766 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1767 if (infoPtr->nSearchParamLength < MAX_PATH-1)
1768 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1769 if (infoPtr->charCode != charCode)
1770 infoPtr->charCode = charCode = 0;
1772 infoPtr->charCode=charCode;
1773 infoPtr->szSearchParam[0]=charCode;
1774 infoPtr->nSearchParamLength=1;
1775 /* Redundant with the 1 char string */
1779 /* and search from the current position */
1781 if (infoPtr->nFocusedItem >= 0) {
1782 endidx=infoPtr->nFocusedItem;
1784 /* if looking for single character match,
1785 * then we must always move forward
1787 if (infoPtr->nSearchParamLength == 1)
1790 endidx=infoPtr->nItemCount;
1794 /* Let application handle this for virtual listview */
1795 if (infoPtr->dwStyle & LVS_OWNERDATA)
1800 ZeroMemory(&lvfi, sizeof(lvfi));
1801 lvfi.flags = (LVFI_WRAP | LVFI_PARTIAL);
1802 infoPtr->szSearchParam[infoPtr->nSearchParamLength] = '\0';
1803 lvfi.psz = infoPtr->szSearchParam;
1807 nItem = notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
1810 LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1816 if (idx == infoPtr->nItemCount) {
1817 if (endidx == infoPtr->nItemCount || endidx == 0)
1823 item.mask = LVIF_TEXT;
1826 item.pszText = buffer;
1827 item.cchTextMax = MAX_PATH;
1828 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1830 /* check for a match */
1831 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1834 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1835 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1836 /* This would work but we must keep looking for a longer match */
1840 } while (idx != endidx);
1843 LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1848 /*************************************************************************
1849 * LISTVIEW_UpdateHeaderSize [Internal]
1851 * Function to resize the header control
1854 * [I] hwnd : handle to a window
1855 * [I] nNewScrollPos : scroll pos to set
1860 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1865 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1867 if (!infoPtr->hwndHeader) return;
1869 GetWindowRect(infoPtr->hwndHeader, &winRect);
1870 point[0].x = winRect.left;
1871 point[0].y = winRect.top;
1872 point[1].x = winRect.right;
1873 point[1].y = winRect.bottom;
1875 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1876 point[0].x = -nNewScrollPos;
1877 point[1].x += nNewScrollPos;
1879 SetWindowPos(infoPtr->hwndHeader,0,
1880 point[0].x,point[0].y,point[1].x,point[1].y,
1881 (infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW |
1882 SWP_NOZORDER | SWP_NOACTIVATE);
1887 * Update the scrollbars. This functions should be called whenever
1888 * the content, size or view changes.
1891 * [I] infoPtr : valid pointer to the listview structure
1896 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *infoPtr)
1898 SCROLLINFO horzInfo, vertInfo;
1901 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1903 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1904 horzInfo.cbSize = sizeof(SCROLLINFO);
1905 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1907 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1908 if (infoPtr->uView == LV_VIEW_LIST)
1910 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1911 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1913 /* scroll by at least one column per page */
1914 if(horzInfo.nPage < infoPtr->nItemWidth)
1915 horzInfo.nPage = infoPtr->nItemWidth;
1917 if (infoPtr->nItemWidth)
1918 horzInfo.nPage /= infoPtr->nItemWidth;
1920 else if (infoPtr->uView == LV_VIEW_DETAILS)
1922 horzInfo.nMax = infoPtr->nItemWidth;
1924 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
1928 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1931 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1932 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1933 dx = GetScrollPos(infoPtr->hwndSelf, SB_HORZ);
1934 dx -= SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1935 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1937 /* Setting the horizontal scroll can change the listview size
1938 * (and potentially everything else) so we need to recompute
1939 * everything again for the vertical scroll
1942 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1943 vertInfo.cbSize = sizeof(SCROLLINFO);
1944 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1946 if (infoPtr->uView == LV_VIEW_DETAILS)
1948 vertInfo.nMax = infoPtr->nItemCount;
1950 /* scroll by at least one page */
1951 if(vertInfo.nPage < infoPtr->nItemHeight)
1952 vertInfo.nPage = infoPtr->nItemHeight;
1954 if (infoPtr->nItemHeight > 0)
1955 vertInfo.nPage /= infoPtr->nItemHeight;
1957 else if (infoPtr->uView != LV_VIEW_LIST) /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
1961 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1964 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1965 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1966 dy = GetScrollPos(infoPtr->hwndSelf, SB_VERT);
1967 dy -= SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1968 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1970 /* Change of the range may have changed the scroll pos. If so move the content */
1971 if (dx != 0 || dy != 0)
1974 listRect = infoPtr->rcList;
1975 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &listRect, &listRect, 0, 0,
1976 SW_ERASE | SW_INVALIDATE);
1979 /* Update the Header Control */
1980 if (infoPtr->uView == LV_VIEW_DETAILS)
1982 horzInfo.fMask = SIF_POS;
1983 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1984 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1991 * Shows/hides the focus rectangle.
1994 * [I] infoPtr : valid pointer to the listview structure
1995 * [I] fShow : TRUE to show the focus, FALSE to hide it.
2000 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO *infoPtr, BOOL fShow)
2004 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
2006 if (infoPtr->nFocusedItem < 0) return;
2008 /* we need some gymnastics in ICON mode to handle large items */
2009 if (infoPtr->uView == LV_VIEW_ICON)
2013 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
2014 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
2016 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
2021 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
2023 /* for some reason, owner draw should work only in report mode */
2024 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
2029 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2030 HFONT hOldFont = SelectObject(hdc, hFont);
2032 item.iItem = infoPtr->nFocusedItem;
2034 item.mask = LVIF_PARAM;
2035 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
2037 ZeroMemory(&dis, sizeof(dis));
2038 dis.CtlType = ODT_LISTVIEW;
2039 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
2040 dis.itemID = item.iItem;
2041 dis.itemAction = ODA_FOCUS;
2042 if (fShow) dis.itemState |= ODS_FOCUS;
2043 dis.hwndItem = infoPtr->hwndSelf;
2045 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
2046 dis.itemData = item.lParam;
2048 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
2050 SelectObject(hdc, hOldFont);
2054 LISTVIEW_DrawFocusRect(infoPtr, hdc);
2057 ReleaseDC(infoPtr->hwndSelf, hdc);
2061 * Invalidates all visible selected items.
2063 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO *infoPtr)
2067 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
2068 while(iterator_next(&i))
2070 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
2071 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
2073 iterator_destroy(&i);
2078 * DESCRIPTION: [INTERNAL]
2079 * Computes an item's (left,top) corner, relative to rcView.
2080 * That is, the position has NOT been made relative to the Origin.
2081 * This is deliberate, to avoid computing the Origin over, and
2082 * over again, when this function is called in a loop. Instead,
2083 * one can factor the computation of the Origin before the loop,
2084 * and offset the value returned by this function, on every iteration.
2087 * [I] infoPtr : valid pointer to the listview structure
2088 * [I] nItem : item number
2089 * [O] lpptOrig : item top, left corner
2094 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
2096 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
2098 if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
2100 lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2101 lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2103 else if (infoPtr->uView == LV_VIEW_LIST)
2105 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
2106 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
2107 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
2109 else /* LV_VIEW_DETAILS */
2111 lpptPosition->x = REPORT_MARGINX;
2112 /* item is always at zero indexed column */
2113 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2114 lpptPosition->x += LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left;
2115 lpptPosition->y = nItem * infoPtr->nItemHeight;
2120 * DESCRIPTION: [INTERNAL]
2121 * Compute the rectangles of an item. This is to localize all
2122 * the computations in one place. If you are not interested in some
2123 * of these values, simply pass in a NULL -- the function is smart
2124 * enough to compute only what's necessary. The function computes
2125 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
2126 * one, the BOX rectangle. This rectangle is very cheap to compute,
2127 * and is guaranteed to contain all the other rectangles. Computing
2128 * the ICON rect is also cheap, but all the others are potentially
2129 * expensive. This gives an easy and effective optimization when
2130 * searching (like point inclusion, or rectangle intersection):
2131 * first test against the BOX, and if TRUE, test against the desired
2133 * If the function does not have all the necessary information
2134 * to computed the requested rectangles, will crash with a
2135 * failed assertion. This is done so we catch all programming
2136 * errors, given that the function is called only from our code.
2138 * We have the following 'special' meanings for a few fields:
2139 * * If LVIS_FOCUSED is set, we assume the item has the focus
2140 * This is important in ICON mode, where it might get a larger
2141 * then usual rectangle
2143 * Please note that subitem support works only in REPORT mode.
2146 * [I] infoPtr : valid pointer to the listview structure
2147 * [I] lpLVItem : item to compute the measures for
2148 * [O] lprcBox : ptr to Box rectangle
2149 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
2150 * [0] lprcSelectBox : ptr to select box rectangle
2151 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
2152 * [O] lprcIcon : ptr to Icon rectangle
2153 * Same as LVM_GETITEMRECT with LVIR_ICON
2154 * [O] lprcStateIcon: ptr to State Icon rectangle
2155 * [O] lprcLabel : ptr to Label rectangle
2156 * Same as LVM_GETITEMRECT with LVIR_LABEL
2161 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
2162 LPRECT lprcBox, LPRECT lprcSelectBox,
2163 LPRECT lprcIcon, LPRECT lprcStateIcon, LPRECT lprcLabel)
2165 BOOL doSelectBox = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
2166 RECT Box, SelectBox, Icon, Label;
2167 COLUMN_INFO *lpColumnInfo = NULL;
2168 SIZE labelSize = { 0, 0 };
2170 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
2172 /* Be smart and try to figure out the minimum we have to do */
2173 if (lpLVItem->iSubItem) assert(infoPtr->uView == LV_VIEW_DETAILS);
2174 if (infoPtr->uView == LV_VIEW_ICON && (lprcBox || lprcLabel))
2176 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
2177 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
2179 if (lprcSelectBox) doSelectBox = TRUE;
2180 if (lprcLabel) doLabel = TRUE;
2181 if (doLabel || lprcIcon || lprcStateIcon) doIcon = TRUE;
2188 /************************************************************/
2189 /* compute the box rectangle (it should be cheap to do) */
2190 /************************************************************/
2191 if (lpLVItem->iSubItem || infoPtr->uView == LV_VIEW_DETAILS)
2192 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
2194 if (lpLVItem->iSubItem)
2196 Box = lpColumnInfo->rcHeader;
2201 Box.right = infoPtr->nItemWidth;
2204 Box.bottom = infoPtr->nItemHeight;
2206 /******************************************************************/
2207 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
2208 /******************************************************************/
2211 LONG state_width = 0;
2213 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
2214 state_width = infoPtr->iconStateSize.cx;
2216 if (infoPtr->uView == LV_VIEW_ICON)
2218 Icon.left = Box.left + state_width;
2219 if (infoPtr->himlNormal)
2220 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx - state_width) / 2;
2221 Icon.top = Box.top + ICON_TOP_PADDING;
2222 Icon.right = Icon.left;
2223 Icon.bottom = Icon.top;
2224 if (infoPtr->himlNormal)
2226 Icon.right += infoPtr->iconSize.cx;
2227 Icon.bottom += infoPtr->iconSize.cy;
2230 else /* LV_VIEW_SMALLICON, LV_VIEW_LIST or LV_VIEW_DETAILS */
2232 Icon.left = Box.left + state_width;
2234 if (infoPtr->uView == LV_VIEW_DETAILS && lpLVItem->iSubItem == 0)
2236 /* we need the indent in report mode */
2237 assert(lpLVItem->mask & LVIF_INDENT);
2238 Icon.left += infoPtr->iconSize.cx * lpLVItem->iIndent + REPORT_MARGINX;
2242 Icon.right = Icon.left;
2243 if (infoPtr->himlSmall &&
2244 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
2245 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
2246 Icon.right += infoPtr->iconSize.cx;
2247 Icon.bottom = Icon.top + infoPtr->iconSize.cy;
2249 if(lprcIcon) *lprcIcon = Icon;
2250 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon));
2252 /* TODO: is this correct? */
2255 lprcStateIcon->left = Icon.left - state_width;
2256 lprcStateIcon->right = Icon.left;
2257 lprcStateIcon->top = Icon.top;
2258 lprcStateIcon->bottom = lprcStateIcon->top + infoPtr->iconSize.cy;
2259 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon));
2262 else Icon.right = 0;
2264 /************************************************************/
2265 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2266 /************************************************************/
2269 /* calculate how far to the right can the label stretch */
2270 Label.right = Box.right;
2271 if (infoPtr->uView == LV_VIEW_DETAILS)
2273 if (lpLVItem->iSubItem == 0)
2275 /* we need a zero based rect here */
2276 Label = lpColumnInfo->rcHeader;
2277 OffsetRect(&Label, -Label.left, 0);
2281 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && infoPtr->uView == LV_VIEW_DETAILS))
2283 labelSize.cx = infoPtr->nItemWidth;
2284 labelSize.cy = infoPtr->nItemHeight;
2288 /* we need the text in non owner draw mode */
2289 assert(lpLVItem->mask & LVIF_TEXT);
2290 if (is_textT(lpLVItem->pszText, TRUE))
2292 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2293 HDC hdc = GetDC(infoPtr->hwndSelf);
2294 HFONT hOldFont = SelectObject(hdc, hFont);
2298 /* compute rough rectangle where the label will go */
2299 SetRectEmpty(&rcText);
2300 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2301 rcText.bottom = infoPtr->nItemHeight;
2302 if (infoPtr->uView == LV_VIEW_ICON)
2303 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2305 /* now figure out the flags */
2306 if (infoPtr->uView == LV_VIEW_ICON)
2307 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2309 uFormat = LV_SL_DT_FLAGS;
2311 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2313 if (rcText.right != rcText.left)
2314 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2316 labelSize.cy = rcText.bottom - rcText.top;
2318 SelectObject(hdc, hOldFont);
2319 ReleaseDC(infoPtr->hwndSelf, hdc);
2323 if (infoPtr->uView == LV_VIEW_ICON)
2325 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2326 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2327 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2328 Label.right = Label.left + labelSize.cx;
2329 Label.bottom = Label.top + infoPtr->nItemHeight;
2330 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2332 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2333 labelSize.cy /= infoPtr->ntmHeight;
2334 labelSize.cy = max(labelSize.cy, 1);
2335 labelSize.cy *= infoPtr->ntmHeight;
2337 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2339 else if (infoPtr->uView == LV_VIEW_DETAILS)
2341 Label.left = Icon.right;
2342 Label.top = Box.top;
2343 Label.right = lpLVItem->iSubItem ? lpColumnInfo->rcHeader.right :
2344 lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
2345 Label.bottom = Label.top + infoPtr->nItemHeight;
2347 else /* LV_VIEW_SMALLICON or LV_VIEW_LIST */
2349 Label.left = Icon.right;
2350 Label.top = Box.top;
2351 Label.right = min(Label.left + labelSize.cx, Label.right);
2352 Label.bottom = Label.top + infoPtr->nItemHeight;
2355 if (lprcLabel) *lprcLabel = Label;
2356 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label));
2359 /************************************************************/
2360 /* compute SELECT bounding box */
2361 /************************************************************/
2364 if (infoPtr->uView == LV_VIEW_DETAILS)
2366 SelectBox.left = Icon.left;
2367 SelectBox.top = Box.top;
2368 SelectBox.bottom = Box.bottom;
2371 SelectBox.right = min(Label.left + labelSize.cx, Label.right);
2373 SelectBox.right = min(Label.left + MAX_EMPTYTEXT_SELECT_WIDTH, Label.right);
2377 UnionRect(&SelectBox, &Icon, &Label);
2379 if (lprcSelectBox) *lprcSelectBox = SelectBox;
2380 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox));
2383 /* Fix the Box if necessary */
2386 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2387 else *lprcBox = Box;
2389 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box));
2393 * DESCRIPTION: [INTERNAL]
2396 * [I] infoPtr : valid pointer to the listview structure
2397 * [I] nItem : item number
2398 * [O] lprcBox : ptr to Box rectangle
2403 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2405 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2406 POINT Position, Origin;
2409 LISTVIEW_GetOrigin(infoPtr, &Origin);
2410 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2412 /* Be smart and try to figure out the minimum we have to do */
2414 if (infoPtr->uView == LV_VIEW_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2415 lvItem.mask |= LVIF_TEXT;
2416 lvItem.iItem = nItem;
2417 lvItem.iSubItem = 0;
2418 lvItem.pszText = szDispText;
2419 lvItem.cchTextMax = DISP_TEXT_SIZE;
2420 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2421 if (infoPtr->uView == LV_VIEW_ICON)
2423 lvItem.mask |= LVIF_STATE;
2424 lvItem.stateMask = LVIS_FOCUSED;
2425 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2427 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0, 0);
2429 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT &&
2430 SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 0, 0))
2432 OffsetRect(lprcBox, Origin.x, Position.y + Origin.y);
2435 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2438 /* LISTVIEW_MapIdToIndex helper */
2439 static INT CALLBACK MapIdSearchCompare(LPVOID p1, LPVOID p2, LPARAM lParam)
2441 ITEM_ID *id1 = (ITEM_ID*)p1;
2442 ITEM_ID *id2 = (ITEM_ID*)p2;
2444 if (id1->id == id2->id) return 0;
2446 return (id1->id < id2->id) ? -1 : 1;
2451 * Returns the item index for id specified.
2454 * [I] infoPtr : valid pointer to the listview structure
2455 * [I] iID : item id to get index for
2458 * Item index, or -1 on failure.
2460 static INT LISTVIEW_MapIdToIndex(const LISTVIEW_INFO *infoPtr, UINT iID)
2465 TRACE("iID=%d\n", iID);
2467 if (infoPtr->dwStyle & LVS_OWNERDATA) return -1;
2468 if (infoPtr->nItemCount == 0) return -1;
2471 index = DPA_Search(infoPtr->hdpaItemIds, &ID, -1, &MapIdSearchCompare, 0, DPAS_SORTED);
2475 ITEM_ID *lpID = DPA_GetPtr(infoPtr->hdpaItemIds, index);
2476 return DPA_GetPtrIndex(infoPtr->hdpaItems, lpID->item);
2484 * Returns the item id for index given.
2487 * [I] infoPtr : valid pointer to the listview structure
2488 * [I] iItem : item index to get id for
2493 static DWORD LISTVIEW_MapIndexToId(const LISTVIEW_INFO *infoPtr, INT iItem)
2498 TRACE("iItem=%d\n", iItem);
2500 if (infoPtr->dwStyle & LVS_OWNERDATA) return -1;
2501 if (iItem < 0 || iItem >= infoPtr->nItemCount) return -1;
2503 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, iItem);
2504 lpItem = DPA_GetPtr(hdpaSubItems, 0);
2506 return lpItem->id->id;
2511 * Returns the current icon position, and advances it along the top.
2512 * The returned position is not offset by Origin.
2515 * [I] infoPtr : valid pointer to the listview structure
2516 * [O] lpPos : will get the current icon position
2521 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2523 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2525 *lpPos = infoPtr->currIconPos;
2527 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2528 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2530 infoPtr->currIconPos.x = 0;
2531 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2537 * Returns the current icon position, and advances it down the left edge.
2538 * The returned position is not offset by Origin.
2541 * [I] infoPtr : valid pointer to the listview structure
2542 * [O] lpPos : will get the current icon position
2547 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2549 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2551 *lpPos = infoPtr->currIconPos;
2553 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2554 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2556 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2557 infoPtr->currIconPos.y = 0;
2563 * Moves an icon to the specified position.
2564 * It takes care of invalidating the item, etc.
2567 * [I] infoPtr : valid pointer to the listview structure
2568 * [I] nItem : the item to move
2569 * [I] lpPos : the new icon position
2570 * [I] isNew : flags the item as being new
2576 static BOOL LISTVIEW_MoveIconTo(const LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2582 old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2583 old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2585 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2586 LISTVIEW_InvalidateItem(infoPtr, nItem);
2589 /* Allocating a POINTER for every item is too resource intensive,
2590 * so we'll keep the (x,y) in different arrays */
2591 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2592 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2594 LISTVIEW_InvalidateItem(infoPtr, nItem);
2601 * Arranges listview items in icon display mode.
2604 * [I] infoPtr : valid pointer to the listview structure
2605 * [I] nAlignCode : alignment code
2611 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2613 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2617 if (infoPtr->uView != LV_VIEW_ICON && infoPtr->uView != LV_VIEW_SMALLICON) return FALSE;
2619 TRACE("nAlignCode=%d\n", nAlignCode);
2621 if (nAlignCode == LVA_DEFAULT)
2623 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2624 else nAlignCode = LVA_ALIGNTOP;
2629 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2630 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2631 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2632 default: return FALSE;
2635 infoPtr->bAutoarrange = TRUE;
2636 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2637 for (i = 0; i < infoPtr->nItemCount; i++)
2639 next_pos(infoPtr, &pos);
2640 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2648 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2649 * For LVS_REPORT always returns empty rectangle.
2652 * [I] infoPtr : valid pointer to the listview structure
2653 * [O] lprcView : bounding rectangle
2659 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2663 SetRectEmpty(lprcView);
2665 switch (infoPtr->uView)
2668 case LV_VIEW_SMALLICON:
2669 for (i = 0; i < infoPtr->nItemCount; i++)
2671 x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2672 y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2673 lprcView->right = max(lprcView->right, x);
2674 lprcView->bottom = max(lprcView->bottom, y);
2676 if (infoPtr->nItemCount > 0)
2678 lprcView->right += infoPtr->nItemWidth;
2679 lprcView->bottom += infoPtr->nItemHeight;
2684 y = LISTVIEW_GetCountPerColumn(infoPtr);
2685 x = infoPtr->nItemCount / y;
2686 if (infoPtr->nItemCount % y) x++;
2687 lprcView->right = x * infoPtr->nItemWidth;
2688 lprcView->bottom = y * infoPtr->nItemHeight;
2695 * Retrieves the bounding rectangle of all the items.
2698 * [I] infoPtr : valid pointer to the listview structure
2699 * [O] lprcView : bounding rectangle
2705 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2709 TRACE("(lprcView=%p)\n", lprcView);
2711 if (!lprcView) return FALSE;
2713 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2715 if (infoPtr->uView != LV_VIEW_DETAILS)
2717 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2718 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2721 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView));
2728 * Retrieves the subitem pointer associated with the subitem index.
2731 * [I] hdpaSubItems : DPA handle for a specific item
2732 * [I] nSubItem : index of subitem
2735 * SUCCESS : subitem pointer
2738 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2740 SUBITEM_INFO *lpSubItem;
2743 /* we should binary search here if need be */
2744 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2746 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
2747 if (lpSubItem->iSubItem == nSubItem)
2757 * Calculates the desired item width.
2760 * [I] infoPtr : valid pointer to the listview structure
2763 * The desired item width.
2765 static INT LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO *infoPtr)
2769 TRACE("uView=%d\n", infoPtr->uView);
2771 if (infoPtr->uView == LV_VIEW_ICON)
2772 nItemWidth = infoPtr->iconSpacing.cx;
2773 else if (infoPtr->uView == LV_VIEW_DETAILS)
2775 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2780 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
2781 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
2783 LISTVIEW_GetHeaderRect(infoPtr, index, &rcHeader);
2784 nItemWidth = rcHeader.right;
2787 else /* LV_VIEW_SMALLICON, or LV_VIEW_LIST */
2792 for (i = 0; i < infoPtr->nItemCount; i++)
2793 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2794 empty = nItemWidth == 0;
2796 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2797 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2800 nItemWidth = max(nItemWidth, DEFAULT_COLUMN_WIDTH);
2802 nItemWidth += WIDTH_PADDING;
2810 * Calculates the desired item height.
2813 * [I] infoPtr : valid pointer to the listview structure
2816 * The desired item height.
2818 static INT LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO *infoPtr)
2822 TRACE("uView=%d\n", infoPtr->uView);
2824 if (infoPtr->uView == LV_VIEW_ICON)
2825 nItemHeight = infoPtr->iconSpacing.cy;
2828 nItemHeight = infoPtr->ntmHeight;
2829 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
2831 if (infoPtr->himlState)
2832 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2833 if (infoPtr->himlSmall)
2834 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2835 if (infoPtr->himlState || infoPtr->himlSmall)
2836 nItemHeight += HEIGHT_PADDING;
2837 if (infoPtr->nMeasureItemHeight > 0)
2838 nItemHeight = infoPtr->nMeasureItemHeight;
2841 return max(nItemHeight, 1);
2846 * Updates the width, and height of an item.
2849 * [I] infoPtr : valid pointer to the listview structure
2854 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2856 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2857 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2863 * Retrieves and saves important text metrics info for the current
2867 * [I] infoPtr : valid pointer to the listview structure
2870 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2872 HDC hdc = GetDC(infoPtr->hwndSelf);
2873 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2874 HFONT hOldFont = SelectObject(hdc, hFont);
2878 if (GetTextMetricsW(hdc, &tm))
2880 infoPtr->ntmHeight = tm.tmHeight;
2881 infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
2884 if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
2885 infoPtr->nEllipsisWidth = sz.cx;
2887 SelectObject(hdc, hOldFont);
2888 ReleaseDC(infoPtr->hwndSelf, hdc);
2890 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2895 * A compare function for ranges
2898 * [I] range1 : pointer to range 1;
2899 * [I] range2 : pointer to range 2;
2903 * > 0 : if range 1 > range 2
2904 * < 0 : if range 2 > range 1
2905 * = 0 : if range intersects range 2
2907 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2911 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2913 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2918 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1), debugrange(range2), cmp);
2924 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2926 #define ranges_check(ranges, desc) do { } while(0)
2929 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2934 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2936 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2937 ranges_dump(ranges);
2938 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2940 prev = DPA_GetPtr(ranges->hdpa, 0);
2941 assert (prev->lower >= 0 && prev->lower < prev->upper);
2942 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2944 curr = DPA_GetPtr(ranges->hdpa, i);
2945 assert (prev->upper <= curr->lower);
2946 assert (curr->lower < curr->upper);
2950 TRACE("--- Done checking---\n");
2953 static RANGES ranges_create(int count)
2955 RANGES ranges = Alloc(sizeof(struct tagRANGES));
2956 if (!ranges) return NULL;
2957 ranges->hdpa = DPA_Create(count);
2958 if (ranges->hdpa) return ranges;
2963 static void ranges_clear(RANGES ranges)
2967 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2968 Free(DPA_GetPtr(ranges->hdpa, i));
2969 DPA_DeleteAllPtrs(ranges->hdpa);
2973 static void ranges_destroy(RANGES ranges)
2975 if (!ranges) return;
2976 ranges_clear(ranges);
2977 DPA_Destroy(ranges->hdpa);
2981 static RANGES ranges_clone(RANGES ranges)
2986 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2988 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2990 RANGE *newrng = Alloc(sizeof(RANGE));
2991 if (!newrng) goto fail;
2992 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2993 DPA_SetPtr(clone->hdpa, i, newrng);
2998 TRACE ("clone failed\n");
2999 ranges_destroy(clone);
3003 static RANGES ranges_diff(RANGES ranges, RANGES sub)
3007 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
3008 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
3013 static void ranges_dump(RANGES ranges)
3017 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
3018 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
3021 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
3023 RANGE srchrng = { nItem, nItem + 1 };
3025 TRACE("(nItem=%d)\n", nItem);
3026 ranges_check(ranges, "before contain");
3027 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
3030 static INT ranges_itemcount(RANGES ranges)
3034 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
3036 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
3037 count += sel->upper - sel->lower;
3043 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
3045 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
3048 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
3049 if (index == -1) return TRUE;
3051 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
3053 chkrng = DPA_GetPtr(ranges->hdpa, index);
3054 if (chkrng->lower >= nItem)
3055 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
3056 if (chkrng->upper > nItem)
3057 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
3062 static BOOL ranges_add(RANGES ranges, RANGE range)
3067 TRACE("(%s)\n", debugrange(&range));
3068 ranges_check(ranges, "before add");
3070 /* try find overlapping regions first */
3071 srchrgn.lower = range.lower - 1;
3072 srchrgn.upper = range.upper + 1;
3073 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
3079 TRACE("Adding new range\n");
3081 /* create the brand new range to insert */
3082 newrgn = Alloc(sizeof(RANGE));
3083 if(!newrgn) goto fail;
3086 /* figure out where to insert it */
3087 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
3088 TRACE("index=%d\n", index);
3089 if (index == -1) index = 0;
3091 /* and get it over with */
3092 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
3100 RANGE *chkrgn, *mrgrgn;
3101 INT fromindex, mergeindex;
3103 chkrgn = DPA_GetPtr(ranges->hdpa, index);
3104 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
3106 chkrgn->lower = min(range.lower, chkrgn->lower);
3107 chkrgn->upper = max(range.upper, chkrgn->upper);
3109 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
3111 /* merge now common ranges */
3113 srchrgn.lower = chkrgn->lower - 1;
3114 srchrgn.upper = chkrgn->upper + 1;
3118 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
3119 if (mergeindex == -1) break;
3120 if (mergeindex == index)
3122 fromindex = index + 1;
3126 TRACE("Merge with index %i\n", mergeindex);
3128 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
3129 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
3130 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
3132 DPA_DeletePtr(ranges->hdpa, mergeindex);
3133 if (mergeindex < index) index --;
3137 ranges_check(ranges, "after add");
3141 ranges_check(ranges, "failed add");
3145 static BOOL ranges_del(RANGES ranges, RANGE range)
3150 TRACE("(%s)\n", debugrange(&range));
3151 ranges_check(ranges, "before del");
3153 /* we don't use DPAS_SORTED here, since we need *
3154 * to find the first overlapping range */
3155 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
3158 chkrgn = DPA_GetPtr(ranges->hdpa, index);
3160 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
3162 /* case 1: Same range */
3163 if ( (chkrgn->upper == range.upper) &&
3164 (chkrgn->lower == range.lower) )
3166 DPA_DeletePtr(ranges->hdpa, index);
3169 /* case 2: engulf */
3170 else if ( (chkrgn->upper <= range.upper) &&
3171 (chkrgn->lower >= range.lower) )
3173 DPA_DeletePtr(ranges->hdpa, index);
3175 /* case 3: overlap upper */
3176 else if ( (chkrgn->upper <= range.upper) &&
3177 (chkrgn->lower < range.lower) )
3179 chkrgn->upper = range.lower;
3181 /* case 4: overlap lower */
3182 else if ( (chkrgn->upper > range.upper) &&
3183 (chkrgn->lower >= range.lower) )
3185 chkrgn->lower = range.upper;
3188 /* case 5: fully internal */
3191 RANGE tmprgn = *chkrgn, *newrgn;
3193 if (!(newrgn = Alloc(sizeof(RANGE)))) goto fail;
3194 newrgn->lower = chkrgn->lower;
3195 newrgn->upper = range.lower;
3196 chkrgn->lower = range.upper;
3197 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
3206 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
3209 ranges_check(ranges, "after del");
3213 ranges_check(ranges, "failed del");
3219 * Removes all selection ranges
3222 * [I] infoPtr : valid pointer to the listview structure
3223 * [I] toSkip : item range to skip removing the selection
3229 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
3238 lvItem.stateMask = LVIS_SELECTED;
3240 /* need to clone the DPA because callbacks can change it */
3241 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
3242 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
3243 while(iterator_next(&i))
3244 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
3245 /* note that the iterator destructor will free the cloned range */
3246 iterator_destroy(&i);
3251 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
3255 if (!(toSkip = ranges_create(1))) return FALSE;
3256 if (nItem != -1) ranges_additem(toSkip, nItem);
3257 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
3258 ranges_destroy(toSkip);
3262 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
3264 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
3269 * Retrieves the number of items that are marked as selected.
3272 * [I] infoPtr : valid pointer to the listview structure
3275 * Number of items selected.
3277 static INT LISTVIEW_GetSelectedCount(const LISTVIEW_INFO *infoPtr)
3279 INT nSelectedCount = 0;
3281 if (infoPtr->uCallbackMask & LVIS_SELECTED)
3284 for (i = 0; i < infoPtr->nItemCount; i++)
3286 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
3291 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
3293 TRACE("nSelectedCount=%d\n", nSelectedCount);
3294 return nSelectedCount;
3299 * Manages the item focus.
3302 * [I] infoPtr : valid pointer to the listview structure
3303 * [I] nItem : item index
3306 * TRUE : focused item changed
3307 * FALSE : focused item has NOT changed
3309 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
3311 INT oldFocus = infoPtr->nFocusedItem;
3314 if (nItem == infoPtr->nFocusedItem) return FALSE;
3316 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
3317 lvItem.stateMask = LVIS_FOCUSED;
3318 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
3320 return oldFocus != infoPtr->nFocusedItem;
3323 /* Helper function for LISTVIEW_ShiftIndices *only* */
3324 static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
3326 if (nShiftItem < nItem) return nShiftItem;
3328 if (nShiftItem > nItem) return nShiftItem + direction;
3330 if (direction > 0) return nShiftItem + direction;
3332 return min(nShiftItem, infoPtr->nItemCount - 1);
3337 * Updates the various indices after an item has been inserted or deleted.
3340 * [I] infoPtr : valid pointer to the listview structure
3341 * [I] nItem : item index
3342 * [I] direction : Direction of shift, +1 or -1.
3347 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
3352 /* temporarily disable change notification while shifting items */
3353 bOldChange = infoPtr->bDoChangeNotify;
3354 infoPtr->bDoChangeNotify = FALSE;
3356 TRACE("Shifting %iu, %i steps\n", nItem, direction);
3358 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
3360 assert(abs(direction) == 1);
3362 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
3364 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
3365 if (nNewFocus != infoPtr->nFocusedItem)
3366 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
3368 /* But we are not supposed to modify nHotItem! */
3370 infoPtr->bDoChangeNotify = bOldChange;
3376 * Adds a block of selections.
3379 * [I] infoPtr : valid pointer to the listview structure
3380 * [I] nItem : item index
3383 * Whether the window is still valid.
3385 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3387 INT nFirst = min(infoPtr->nSelectionMark, nItem);
3388 INT nLast = max(infoPtr->nSelectionMark, nItem);
3389 HWND hwndSelf = infoPtr->hwndSelf;
3390 NMLVODSTATECHANGE nmlv;
3395 /* Temporarily disable change notification
3396 * If the control is LVS_OWNERDATA, we need to send
3397 * only one LVN_ODSTATECHANGED notification.
3398 * See MSDN documentation for LVN_ITEMCHANGED.
3400 bOldChange = infoPtr->bDoChangeNotify;
3401 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3403 if (nFirst == -1) nFirst = nItem;
3405 item.state = LVIS_SELECTED;
3406 item.stateMask = LVIS_SELECTED;
3408 for (i = nFirst; i <= nLast; i++)
3409 LISTVIEW_SetItemState(infoPtr,i,&item);
3411 ZeroMemory(&nmlv, sizeof(nmlv));
3412 nmlv.iFrom = nFirst;
3415 nmlv.uOldState = item.state;
3417 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3418 if (!IsWindow(hwndSelf))
3420 infoPtr->bDoChangeNotify = bOldChange;
3427 * Sets a single group selection.
3430 * [I] infoPtr : valid pointer to the listview structure
3431 * [I] nItem : item index
3436 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3443 if (!(selection = ranges_create(100))) return;
3445 item.state = LVIS_SELECTED;
3446 item.stateMask = LVIS_SELECTED;
3448 if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
3450 if (infoPtr->nSelectionMark == -1)
3452 infoPtr->nSelectionMark = nItem;
3453 ranges_additem(selection, nItem);
3459 sel.lower = min(infoPtr->nSelectionMark, nItem);
3460 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3461 ranges_add(selection, sel);
3466 RECT rcItem, rcSel, rcSelMark;
3469 rcItem.left = LVIR_BOUNDS;
3470 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3471 rcSelMark.left = LVIR_BOUNDS;
3472 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3473 UnionRect(&rcSel, &rcItem, &rcSelMark);
3474 iterator_frameditems(&i, infoPtr, &rcSel);
3475 while(iterator_next(&i))
3477 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3478 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3480 iterator_destroy(&i);
3483 /* disable per item notifications on LVS_OWNERDATA style
3484 FIXME: single LVN_ODSTATECHANGED should be used */
3485 bOldChange = infoPtr->bDoChangeNotify;
3486 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3488 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3491 iterator_rangesitems(&i, selection);
3492 while(iterator_next(&i))
3493 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3494 /* this will also destroy the selection */
3495 iterator_destroy(&i);
3497 infoPtr->bDoChangeNotify = bOldChange;
3499 LISTVIEW_SetItemFocus(infoPtr, nItem);
3504 * Sets a single selection.
3507 * [I] infoPtr : valid pointer to the listview structure
3508 * [I] nItem : item index
3513 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3517 TRACE("nItem=%d\n", nItem);
3519 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3521 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3522 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3523 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3525 infoPtr->nSelectionMark = nItem;
3530 * Set selection(s) with keyboard.
3533 * [I] infoPtr : valid pointer to the listview structure
3534 * [I] nItem : item index
3535 * [I] space : VK_SPACE code sent
3538 * SUCCESS : TRUE (needs to be repainted)
3539 * FAILURE : FALSE (nothing has changed)
3541 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem, BOOL space)
3543 /* FIXME: pass in the state */
3544 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3545 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3546 BOOL bResult = FALSE;
3548 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3549 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3553 if (infoPtr->dwStyle & LVS_SINGLESEL || (wShift == 0 && wCtrl == 0))
3554 LISTVIEW_SetSelection(infoPtr, nItem);
3558 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3562 lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3563 lvItem.stateMask = LVIS_SELECTED;
3566 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3567 if (lvItem.state & LVIS_SELECTED)
3568 infoPtr->nSelectionMark = nItem;
3570 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3573 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3576 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3580 static BOOL LISTVIEW_GetItemAtPt(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3582 LVHITTESTINFO lvHitTestInfo;
3584 ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3585 lvHitTestInfo.pt.x = pt.x;
3586 lvHitTestInfo.pt.y = pt.y;
3588 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3590 lpLVItem->mask = LVIF_PARAM;
3591 lpLVItem->iItem = lvHitTestInfo.iItem;
3592 lpLVItem->iSubItem = 0;
3594 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3597 static inline BOOL LISTVIEW_isHotTracking(const LISTVIEW_INFO *infoPtr)
3599 return ((infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) ||
3600 (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE) ||
3601 (infoPtr->dwLvExStyle & LVS_EX_TWOCLICKACTIVATE));
3606 * Called when the mouse is being actively tracked and has hovered for a specified
3610 * [I] infoPtr : valid pointer to the listview structure
3611 * [I] fwKeys : key indicator
3612 * [I] x,y : mouse position
3615 * 0 if the message was processed, non-zero if there was an error
3618 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3619 * over the item for a certain period of time.
3622 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3624 if (LISTVIEW_isHotTracking(infoPtr))
3632 if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3633 LISTVIEW_SetSelection(infoPtr, item.iItem);
3639 #define SCROLL_LEFT 0x1
3640 #define SCROLL_RIGHT 0x2
3641 #define SCROLL_UP 0x4
3642 #define SCROLL_DOWN 0x8
3646 * Utility routine to draw and highlight items within a marquee selection rectangle.
3649 * [I] infoPtr : valid pointer to the listview structure
3650 * [I] coords_orig : original co-ordinates of the cursor
3651 * [I] coords_offs : offsetted coordinates of the cursor
3652 * [I] offset : offset amount
3653 * [I] scroll : Bitmask of which directions we should scroll, if at all
3658 static void LISTVIEW_MarqueeHighlight(LISTVIEW_INFO *infoPtr, LPPOINT coords_orig, LPPOINT coords_offs, LPPOINT offset, INT scroll)
3660 BOOL controlDown = FALSE;
3665 if (coords_offs->x > infoPtr->marqueeOrigin.x)
3667 rect.left = infoPtr->marqueeOrigin.x;
3668 rect.right = coords_offs->x;
3672 rect.left = coords_offs->x;
3673 rect.right = infoPtr->marqueeOrigin.x;
3676 if (coords_offs->y > infoPtr->marqueeOrigin.y)
3678 rect.top = infoPtr->marqueeOrigin.y;
3679 rect.bottom = coords_offs->y;
3683 rect.top = coords_offs->y;
3684 rect.bottom = infoPtr->marqueeOrigin.y;
3687 /* Cancel out the old marquee rectangle and draw the new one */
3688 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeDrawRect);
3690 /* Scroll by the appropriate distance if applicable - speed up scrolling as
3691 the cursor is further away */
3693 if ((scroll & SCROLL_LEFT) && (coords_orig->x <= 0))
3694 LISTVIEW_Scroll(infoPtr, coords_orig->x, 0);
3696 if ((scroll & SCROLL_RIGHT) && (coords_orig->x >= infoPtr->rcList.right))
3697 LISTVIEW_Scroll(infoPtr, (coords_orig->x - infoPtr->rcList.right), 0);
3699 if ((scroll & SCROLL_UP) && (coords_orig->y <= 0))
3700 LISTVIEW_Scroll(infoPtr, 0, coords_orig->y);
3702 if ((scroll & SCROLL_DOWN) && (coords_orig->y >= infoPtr->rcList.bottom))
3703 LISTVIEW_Scroll(infoPtr, 0, (coords_orig->y - infoPtr->rcList.bottom));
3705 /* Invert the items in the old marquee rectangle */
3706 iterator_frameditems_absolute(&i, infoPtr, &infoPtr->marqueeRect);
3708 while (iterator_next(&i))
3712 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED) == LVIS_SELECTED)
3715 item.state = LVIS_SELECTED;
3717 item.stateMask = LVIS_SELECTED;
3719 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3723 iterator_destroy(&i);
3725 CopyRect(&infoPtr->marqueeRect, &rect);
3727 CopyRect(&infoPtr->marqueeDrawRect, &rect);
3728 OffsetRect(&infoPtr->marqueeDrawRect, offset->x, offset->y);
3730 /* Iterate over the items within our marquee rectangle */
3731 iterator_frameditems_absolute(&i, infoPtr, &infoPtr->marqueeRect);
3733 if (GetKeyState(VK_CONTROL) & 0x8000)
3736 while (iterator_next(&i))
3740 /* If CTRL is pressed, invert. If not, always select the item. */
3741 if ((controlDown) && (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED)))
3744 item.state = LVIS_SELECTED;
3746 item.stateMask = LVIS_SELECTED;
3748 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3752 iterator_destroy(&i);
3753 LISTVIEW_InvalidateRect(infoPtr, &rect);
3758 * Called when we are in a marquee selection that involves scrolling the listview (ie,
3759 * the cursor is outside the bounds of the client area). This is a TIMERPROC.
3762 * [I] hwnd : Handle to the listview
3763 * [I] uMsg : WM_TIMER (ignored)
3764 * [I] idEvent : The timer ID interpreted as a pointer to a LISTVIEW_INFO struct
3765 * [I] dwTimer : The elapsed time (ignored)
3770 static VOID CALLBACK LISTVIEW_ScrollTimer(HWND hWnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
3772 LISTVIEW_INFO *infoPtr;
3773 SCROLLINFO scrollInfo;
3779 infoPtr = (LISTVIEW_INFO *) idEvent;
3784 /* Get the current cursor position and convert to client coordinates */
3785 GetCursorPos(&coords_orig);
3786 ScreenToClient(hWnd, &coords_orig);
3788 /* Ensure coordinates are within client bounds */
3789 coords_offs.x = max(min(coords_orig.x, infoPtr->rcList.right), 0);
3790 coords_offs.y = max(min(coords_orig.y, infoPtr->rcList.bottom), 0);
3793 LISTVIEW_GetOrigin(infoPtr, &offset);
3795 /* Offset coordinates by the appropriate amount */
3796 coords_offs.x -= offset.x;
3797 coords_offs.y -= offset.y;
3799 scrollInfo.cbSize = sizeof(SCROLLINFO);
3800 scrollInfo.fMask = SIF_ALL;
3802 /* Work out in which directions we can scroll */
3803 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3805 if (scrollInfo.nPos != scrollInfo.nMin)
3806 scroll |= SCROLL_UP;
3808 if (((scrollInfo.nPage + scrollInfo.nPos) - 1) != scrollInfo.nMax)
3809 scroll |= SCROLL_DOWN;
3812 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3814 if (scrollInfo.nPos != scrollInfo.nMin)
3815 scroll |= SCROLL_LEFT;
3817 if (((scrollInfo.nPage + scrollInfo.nPos) - 1) != scrollInfo.nMax)
3818 scroll |= SCROLL_RIGHT;
3821 if (((coords_orig.x <= 0) && (scroll & SCROLL_LEFT)) ||
3822 ((coords_orig.y <= 0) && (scroll & SCROLL_UP)) ||
3823 ((coords_orig.x >= infoPtr->rcList.right) && (scroll & SCROLL_RIGHT)) ||
3824 ((coords_orig.y >= infoPtr->rcList.bottom) && (scroll & SCROLL_DOWN)))
3826 LISTVIEW_MarqueeHighlight(infoPtr, &coords_orig, &coords_offs, &offset, scroll);
3832 * Called whenever WM_MOUSEMOVE is received.
3835 * [I] infoPtr : valid pointer to the listview structure
3836 * [I] fwKeys : key indicator
3837 * [I] x,y : mouse position
3840 * 0 if the message is processed, non-zero if there was an error
3842 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3844 TRACKMOUSEEVENT trackinfo;
3846 if (!(fwKeys & MK_LBUTTON))
3847 infoPtr->bLButtonDown = FALSE;
3849 if (infoPtr->bLButtonDown)
3853 LVHITTESTINFO lvHitTestInfo;
3854 WORD wDragWidth = GetSystemMetrics(SM_CXDRAG);
3855 WORD wDragHeight= GetSystemMetrics(SM_CYDRAG);
3857 if (infoPtr->bMarqueeSelect)
3867 LISTVIEW_GetOrigin(infoPtr, &offset);
3869 /* Ensure coordinates are within client bounds */
3870 coords_offs.x = max(min(x, infoPtr->rcList.right), 0);
3871 coords_offs.y = max(min(y, infoPtr->rcList.bottom), 0);
3873 /* Offset coordinates by the appropriate amount */
3874 coords_offs.x -= offset.x;
3875 coords_offs.y -= offset.y;
3877 /* Enable the timer if we're going outside our bounds, in case the user doesn't
3878 move the mouse again */
3880 if ((x <= 0) || (y <= 0) || (x >= infoPtr->rcList.right) ||
3881 (y >= infoPtr->rcList.bottom))
3883 if (!infoPtr->bScrolling)
3885 infoPtr->bScrolling = TRUE;
3886 SetTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr, 1, LISTVIEW_ScrollTimer);
3891 infoPtr->bScrolling = FALSE;
3892 KillTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr);
3895 LISTVIEW_MarqueeHighlight(infoPtr, &coords_orig, &coords_offs, &offset, 0);
3899 rect.left = infoPtr->ptClickPos.x - wDragWidth;
3900 rect.right = infoPtr->ptClickPos.x + wDragWidth;
3901 rect.top = infoPtr->ptClickPos.y - wDragHeight;
3902 rect.bottom = infoPtr->ptClickPos.y + wDragHeight;
3907 lvHitTestInfo.pt = tmp;
3908 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3910 /* reset item marker */
3911 if (infoPtr->nLButtonDownItem != lvHitTestInfo.iItem)
3912 infoPtr->nLButtonDownItem = -1;
3914 if (!PtInRect(&rect, tmp))
3916 /* this path covers the following:
3917 1. WM_LBUTTONDOWN over selected item (sets focus on it)
3918 2. change focus with keys
3919 3. move mouse over item from step 1 selects it and moves focus on it */
3920 if (infoPtr->nLButtonDownItem != -1 &&
3921 !LISTVIEW_GetItemState(infoPtr, infoPtr->nLButtonDownItem, LVIS_SELECTED))
3925 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3926 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3928 LISTVIEW_SetItemState(infoPtr, infoPtr->nLButtonDownItem, &lvItem);
3929 infoPtr->nLButtonDownItem = -1;
3932 if (!infoPtr->bDragging)
3934 lvHitTestInfo.pt = infoPtr->ptClickPos;
3935 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3937 /* If the click is outside the range of an item, begin a
3938 highlight. If not, begin an item drag. */
3939 if (lvHitTestInfo.iItem == -1)
3943 /* If we're allowing multiple selections, send notification.
3944 If return value is non-zero, cancel. */
3945 if (!(infoPtr->dwStyle & LVS_SINGLESEL) && (notify_hdr(infoPtr, LVN_MARQUEEBEGIN, &hdr) == 0))
3947 /* Store the absolute coordinates of the click */
3949 LISTVIEW_GetOrigin(infoPtr, &offset);
3951 infoPtr->marqueeOrigin.x = infoPtr->ptClickPos.x - offset.x;
3952 infoPtr->marqueeOrigin.y = infoPtr->ptClickPos.y - offset.y;
3954 /* Begin selection and capture mouse */
3955 infoPtr->bMarqueeSelect = TRUE;
3956 SetCapture(infoPtr->hwndSelf);
3963 ZeroMemory(&nmlv, sizeof(nmlv));
3964 nmlv.iItem = lvHitTestInfo.iItem;
3965 nmlv.ptAction = infoPtr->ptClickPos;
3967 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3968 infoPtr->bDragging = TRUE;
3976 /* see if we are supposed to be tracking mouse hovering */
3977 if (LISTVIEW_isHotTracking(infoPtr)) {
3978 /* fill in the trackinfo struct */
3979 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3980 trackinfo.dwFlags = TME_QUERY;
3981 trackinfo.hwndTrack = infoPtr->hwndSelf;
3982 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3984 /* see if we are already tracking this hwnd */
3985 _TrackMouseEvent(&trackinfo);
3987 if(!(trackinfo.dwFlags & TME_HOVER)) {
3988 trackinfo.dwFlags = TME_HOVER;
3990 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3991 _TrackMouseEvent(&trackinfo);
4000 * Tests whether the item is assignable to a list with style lStyle
4002 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
4004 if ( (lpLVItem->mask & LVIF_TEXT) &&
4005 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
4006 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
4014 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
4017 * [I] infoPtr : valid pointer to the listview structure
4018 * [I] lpLVItem : valid pointer to new item attributes
4019 * [I] isNew : the item being set is being inserted
4020 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4021 * [O] bChanged : will be set to TRUE if the item really changed
4027 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
4033 /* stateMask is ignored for LVM_INSERTITEM */
4034 UINT stateMask = isNew ? ~0 : lpLVItem->stateMask;
4038 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
4040 if (lpLVItem->mask == 0) return TRUE;
4042 if (infoPtr->dwStyle & LVS_OWNERDATA)
4044 /* a virtual listview only stores selection and focus */
4045 if (lpLVItem->mask & ~LVIF_STATE)
4051 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4052 lpItem = DPA_GetPtr(hdpaSubItems, 0);
4056 /* we need to get the lParam and state of the item */
4057 item.iItem = lpLVItem->iItem;
4058 item.iSubItem = lpLVItem->iSubItem;
4059 item.mask = LVIF_STATE | LVIF_PARAM;
4060 item.stateMask = (infoPtr->dwStyle & LVS_OWNERDATA) ? LVIS_FOCUSED | LVIS_SELECTED : ~0;
4064 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
4066 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
4067 /* determine what fields will change */
4068 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & stateMask & ~infoPtr->uCallbackMask))
4069 uChanged |= LVIF_STATE;
4071 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
4072 uChanged |= LVIF_IMAGE;
4074 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
4075 uChanged |= LVIF_PARAM;
4077 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
4078 uChanged |= LVIF_INDENT;
4080 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
4081 uChanged |= LVIF_TEXT;
4083 TRACE("uChanged=0x%x\n", uChanged);
4084 if (!uChanged) return TRUE;
4087 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4088 nmlv.iItem = lpLVItem->iItem;
4089 nmlv.uNewState = (item.state & ~stateMask) | (lpLVItem->state & stateMask);
4090 nmlv.uOldState = item.state;
4091 nmlv.uChanged = uChanged;
4092 nmlv.lParam = item.lParam;
4094 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
4095 /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
4097 if(lpItem && !isNew && infoPtr->bDoChangeNotify)
4099 HWND hwndSelf = infoPtr->hwndSelf;
4101 if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
4103 if (!IsWindow(hwndSelf))
4107 /* copy information */
4108 if (lpLVItem->mask & LVIF_TEXT)
4109 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
4111 if (lpLVItem->mask & LVIF_IMAGE)
4112 lpItem->hdr.iImage = lpLVItem->iImage;
4114 if (lpLVItem->mask & LVIF_PARAM)
4115 lpItem->lParam = lpLVItem->lParam;
4117 if (lpLVItem->mask & LVIF_INDENT)
4118 lpItem->iIndent = lpLVItem->iIndent;
4120 if (uChanged & LVIF_STATE)
4122 if (lpItem && (stateMask & ~infoPtr->uCallbackMask))
4124 lpItem->state &= ~stateMask;
4125 lpItem->state |= (lpLVItem->state & stateMask);
4127 if (lpLVItem->state & stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
4129 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
4130 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
4132 else if (stateMask & LVIS_SELECTED)
4134 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
4136 /* if we are asked to change focus, and we manage it, do it */
4137 if (stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
4139 if (lpLVItem->state & LVIS_FOCUSED)
4141 if (infoPtr->nFocusedItem != -1)
4143 /* remove current focus */
4144 item.mask = LVIF_STATE;
4146 item.stateMask = LVIS_FOCUSED;
4148 /* recurse with redrawing an item */
4149 LISTVIEW_SetItemState(infoPtr, infoPtr->nFocusedItem, &item);
4152 infoPtr->nFocusedItem = lpLVItem->iItem;
4153 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, infoPtr->uView == LV_VIEW_LIST);
4155 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
4157 infoPtr->nFocusedItem = -1;
4162 /* if we're inserting the item, we're done */
4163 if (isNew) return TRUE;
4165 /* send LVN_ITEMCHANGED notification */
4166 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
4167 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
4174 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
4177 * [I] infoPtr : valid pointer to the listview structure
4178 * [I] lpLVItem : valid pointer to new subitem attributes
4179 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4180 * [O] bChanged : will be set to TRUE if the item really changed
4186 static BOOL set_sub_item(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
4189 SUBITEM_INFO *lpSubItem;
4191 /* we do not support subitems for virtual listviews */
4192 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
4194 /* set subitem only if column is present */
4195 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4197 /* First do some sanity checks */
4198 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
4199 particularly useful. We currently do not actually do anything with
4200 the flag on subitems.
4202 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)) return FALSE;
4203 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))) return TRUE;
4205 /* get the subitem structure, and create it if not there */
4206 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
4207 assert (hdpaSubItems);
4209 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
4212 SUBITEM_INFO *tmpSubItem;
4215 lpSubItem = Alloc(sizeof(SUBITEM_INFO));
4216 if (!lpSubItem) return FALSE;
4217 /* we could binary search here, if need be...*/
4218 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4220 tmpSubItem = DPA_GetPtr(hdpaSubItems, i);
4221 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
4223 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
4228 lpSubItem->iSubItem = lpLVItem->iSubItem;
4229 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
4233 if (lpLVItem->mask & LVIF_IMAGE)
4234 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
4236 lpSubItem->hdr.iImage = lpLVItem->iImage;
4240 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpSubItem->hdr.pszText, lpLVItem->pszText, isW))
4242 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
4251 * Sets item attributes.
4254 * [I] infoPtr : valid pointer to the listview structure
4255 * [I] lpLVItem : new item attributes
4256 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
4262 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem, BOOL isW)
4264 HWND hwndSelf = infoPtr->hwndSelf;
4265 LPWSTR pszText = NULL;
4266 BOOL bResult, bChanged = FALSE;
4268 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
4270 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
4273 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
4274 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
4276 pszText = lpLVItem->pszText;
4277 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
4280 /* actually set the fields */
4281 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
4283 if (lpLVItem->iSubItem)
4284 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
4286 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
4287 if (!IsWindow(hwndSelf))
4290 /* redraw item, if necessary */
4291 if (bChanged && !infoPtr->bIsDrawing)
4293 /* this little optimization eliminates some nasty flicker */
4294 if ( infoPtr->uView == LV_VIEW_DETAILS && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
4295 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
4296 lpLVItem->iSubItem > 0 && lpLVItem->iSubItem <= DPA_GetPtrCount(infoPtr->hdpaColumns) )
4297 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
4299 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
4304 textfreeT(lpLVItem->pszText, isW);
4305 lpLVItem->pszText = pszText;
4313 * Retrieves the index of the item at coordinate (0, 0) of the client area.
4316 * [I] infoPtr : valid pointer to the listview structure
4321 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *infoPtr)
4324 SCROLLINFO scrollInfo;
4326 scrollInfo.cbSize = sizeof(SCROLLINFO);
4327 scrollInfo.fMask = SIF_POS;
4329 if (infoPtr->uView == LV_VIEW_LIST)
4331 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
4332 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
4334 else if (infoPtr->uView == LV_VIEW_DETAILS)
4336 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
4337 nItem = scrollInfo.nPos;
4341 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
4342 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
4345 TRACE("nItem=%d\n", nItem);
4353 * Erases the background of the given rectangle
4356 * [I] infoPtr : valid pointer to the listview structure
4357 * [I] hdc : device context handle
4358 * [I] lprcBox : clipping rectangle
4364 static inline BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
4366 if (!infoPtr->hBkBrush) return FALSE;
4368 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
4370 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
4378 * [I] infoPtr : valid pointer to the listview structure
4379 * [I] hdc : device context handle
4380 * [I] nItem : item index
4381 * [I] nSubItem : subitem index
4382 * [I] pos : item position in client coordinates
4383 * [I] cdmode : custom draw mode
4389 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
4392 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4393 static WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
4394 DWORD cdsubitemmode = CDRF_DODEFAULT;
4396 RECT rcSelect, rcBox, rcIcon, rcLabel, rcStateIcon;
4397 NMLVCUSTOMDRAW nmlvcd;
4402 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
4404 /* get information needed for drawing the item */
4405 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
4406 if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
4407 if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
4408 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
4409 lvItem.iItem = nItem;
4410 lvItem.iSubItem = nSubItem;
4413 lvItem.cchTextMax = DISP_TEXT_SIZE;
4414 lvItem.pszText = szDispText;
4415 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
4416 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
4417 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
4418 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
4419 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
4421 /* now check if we need to update the focus rectangle */
4422 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
4424 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
4425 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
4426 OffsetRect(&rcBox, pos.x, pos.y);
4427 OffsetRect(&rcSelect, pos.x, pos.y);
4428 OffsetRect(&rcIcon, pos.x, pos.y);
4429 OffsetRect(&rcStateIcon, pos.x, pos.y);
4430 OffsetRect(&rcLabel, pos.x, pos.y);
4431 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
4432 wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect),
4433 wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
4435 /* fill in the custom draw structure */
4436 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
4438 hOldFont = GetCurrentObject(hdc, OBJ_FONT);
4439 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
4440 if (cdmode & CDRF_SKIPDEFAULT) goto postpaint;
4441 if (cdmode & CDRF_NOTIFYITEMDRAW)
4442 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4443 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
4444 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
4445 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
4446 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
4448 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
4449 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
4451 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
4452 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4453 else if ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) == FALSE)
4454 prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE);
4456 /* in full row select, subitems, will just use main item's colors */
4457 if (nSubItem && infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
4458 nmlvcd.clrTextBk = CLR_NONE;
4460 /* FIXME: temporary hack */
4461 rcSelect.left = rcLabel.left;
4463 /* draw the selection background, if we're drawing the main item */
4466 /* in icon mode, the label rect is really what we want to draw the
4468 if (infoPtr->uView == LV_VIEW_ICON)
4471 if (infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
4473 /* we have to update left focus bound too if item isn't in leftmost column
4474 and reduce right box bound */
4475 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
4479 if ((leftmost = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, 0, 0)))
4481 INT Originx = pos.x - LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left;
4482 INT index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
4483 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
4485 rcBox.right = LISTVIEW_GetColumnInfo(infoPtr, index)->rcHeader.right + Originx;
4486 rcSelect.left = LISTVIEW_GetColumnInfo(infoPtr, leftmost)->rcHeader.left + Originx;
4490 rcSelect.right = rcBox.right;
4493 if (nmlvcd.clrTextBk != CLR_NONE)
4494 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, NULL, 0, NULL);
4495 /* store new focus rectangle */
4496 if (infoPtr->nFocusedItem == nItem) infoPtr->rcFocus = rcSelect;
4500 if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && (nSubItem == 0))
4502 UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
4505 TRACE("uStateImage=%d\n", uStateImage);
4506 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
4507 rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
4512 himl = (infoPtr->uView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
4513 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
4515 TRACE("iImage=%d\n", lvItem.iImage);
4516 ImageList_DrawEx(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
4517 rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk, CLR_DEFAULT,
4518 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
4521 /* Don't bother painting item being edited */
4522 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
4524 /* figure out the text drawing flags */
4525 uFormat = (infoPtr->uView == LV_VIEW_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
4526 if (infoPtr->uView == LV_VIEW_ICON)
4527 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
4530 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
4532 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
4533 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
4534 default: uFormat |= DT_LEFT;
4537 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
4539 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
4540 else rcLabel.left += LABEL_HOR_PADDING;
4542 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
4544 /* for GRIDLINES reduce the bottom so the text formats correctly */
4545 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
4548 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
4551 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
4552 notify_postpaint(infoPtr, &nmlvcd);
4553 if (cdsubitemmode & CDRF_NEWFONT)
4554 SelectObject(hdc, hOldFont);
4560 * Draws listview items when in owner draw mode.
4563 * [I] infoPtr : valid pointer to the listview structure
4564 * [I] hdc : device context handle
4569 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4571 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
4572 DWORD cditemmode = CDRF_DODEFAULT;
4573 NMLVCUSTOMDRAW nmlvcd;
4574 POINT Origin, Position;
4580 ZeroMemory(&dis, sizeof(dis));
4582 /* Get scroll info once before loop */
4583 LISTVIEW_GetOrigin(infoPtr, &Origin);
4585 /* iterate through the invalidated rows */
4586 while(iterator_next(i))
4588 item.iItem = i->nItem;
4590 item.mask = LVIF_PARAM | LVIF_STATE;
4591 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4592 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
4594 dis.CtlType = ODT_LISTVIEW;
4596 dis.itemID = item.iItem;
4597 dis.itemAction = ODA_DRAWENTIRE;
4599 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
4600 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
4601 dis.hwndItem = infoPtr->hwndSelf;
4603 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
4604 dis.rcItem.left = Position.x + Origin.x;
4605 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
4606 dis.rcItem.top = Position.y + Origin.y;
4607 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
4608 dis.itemData = item.lParam;
4610 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
4613 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
4614 * structure for the rest. of the paint cycle
4616 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
4617 if (cdmode & CDRF_NOTIFYITEMDRAW)
4618 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4620 if (!(cditemmode & CDRF_SKIPDEFAULT))
4622 prepaint_setup (infoPtr, hdc, &nmlvcd, FALSE);
4623 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
4626 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
4627 notify_postpaint(infoPtr, &nmlvcd);
4633 * Draws listview items when in report display mode.
4636 * [I] infoPtr : valid pointer to the listview structure
4637 * [I] hdc : device context handle
4638 * [I] cdmode : custom draw mode
4643 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4646 RECT rcClip, rcItem;
4647 POINT Origin, Position;
4654 /* figure out what to draw */
4655 rgntype = GetClipBox(hdc, &rcClip);
4656 if (rgntype == NULLREGION) return;
4658 /* Get scroll info once before loop */
4659 LISTVIEW_GetOrigin(infoPtr, &Origin);
4661 colRanges = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns));
4663 /* narrow down the columns we need to paint */
4664 for(col = 0; col < DPA_GetPtrCount(infoPtr->hdpaColumns); col++)
4666 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, col, 0);
4668 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
4669 if ((rcItem.right + Origin.x >= rcClip.left) && (rcItem.left + Origin.x < rcClip.right))
4670 ranges_additem(colRanges, index);
4672 iterator_rangesitems(&j, colRanges);
4674 /* in full row select, we _have_ to draw the main item */
4675 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
4678 /* iterate through the invalidated rows */
4679 while(iterator_next(i))
4681 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4682 Position.y += Origin.y;
4684 /* iterate through the invalidated columns */
4685 while(iterator_next(&j))
4687 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4688 Position.x = (j.nItem == 0) ? rcItem.left + Origin.x : Origin.x;
4690 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
4693 rcItem.bottom = infoPtr->nItemHeight;
4694 OffsetRect(&rcItem, Origin.x, Position.y);
4695 if (!RectVisible(hdc, &rcItem)) continue;
4698 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
4701 iterator_destroy(&j);
4706 * Draws the gridlines if necessary when in report display mode.
4709 * [I] infoPtr : valid pointer to the listview structure
4710 * [I] hdc : device context handle
4715 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO *infoPtr, HDC hdc)
4721 RECT rcClip, rcItem = {0};
4729 /* figure out what to draw */
4730 rgntype = GetClipBox(hdc, &rcClip);
4731 if (rgntype == NULLREGION) return;
4733 /* Get scroll info once before loop */
4734 LISTVIEW_GetOrigin(infoPtr, &Origin);
4736 colRanges = ranges_create(DPA_GetPtrCount(infoPtr->hdpaColumns));
4738 /* narrow down the columns we need to paint */
4739 for(col = 0; col < DPA_GetPtrCount(infoPtr->hdpaColumns); col++)
4741 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, col, 0);
4743 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
4744 if ((rcItem.right + Origin.x >= rcClip.left) && (rcItem.left + Origin.x < rcClip.right))
4745 ranges_additem(colRanges, index);
4748 /* is right most vertical line visible? */
4749 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
4751 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
4752 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
4753 rmost = (rcItem.right + Origin.x < rcClip.right);
4756 if ((hPen = CreatePen( PS_SOLID, 1, comctl32_color.clr3dFace )))
4758 hOldPen = SelectObject ( hdc, hPen );
4760 /* draw the vertical lines for the columns */
4761 iterator_rangesitems(&j, colRanges);
4762 while(iterator_next(&j))
4764 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4765 if (rcItem.left == 0) continue; /* skip leftmost column */
4766 rcItem.left += Origin.x;
4767 rcItem.right += Origin.x;
4768 rcItem.top = infoPtr->rcList.top;
4769 rcItem.bottom = infoPtr->rcList.bottom;
4770 TRACE("vert col=%d, rcItem=%s\n", j.nItem, wine_dbgstr_rect(&rcItem));
4771 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4772 LineTo (hdc, rcItem.left, rcItem.bottom);
4774 iterator_destroy(&j);
4775 /* draw rightmost grid line if visible */
4778 index = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
4779 DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, 0);
4780 LISTVIEW_GetHeaderRect(infoPtr, index, &rcItem);
4782 rcItem.right += Origin.x;
4784 MoveToEx (hdc, rcItem.right, infoPtr->rcList.top, NULL);
4785 LineTo (hdc, rcItem.right, infoPtr->rcList.bottom);
4788 /* draw the horizontial lines for the rows */
4789 itemheight = LISTVIEW_CalculateItemHeight(infoPtr);
4790 rcItem.left = infoPtr->rcList.left;
4791 rcItem.right = infoPtr->rcList.right;
4792 rcItem.bottom = rcItem.top = Origin.y - 1;
4793 MoveToEx(hdc, rcItem.left, rcItem.top, NULL);
4794 LineTo(hdc, rcItem.right, rcItem.top);
4795 for(y=itemheight-1+Origin.y; y<=infoPtr->rcList.bottom; y+=itemheight)
4797 rcItem.bottom = rcItem.top = y;
4798 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem));
4799 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4800 LineTo (hdc, rcItem.right, rcItem.top);
4803 SelectObject( hdc, hOldPen );
4804 DeleteObject( hPen );
4810 * Draws listview items when in list display mode.
4813 * [I] infoPtr : valid pointer to the listview structure
4814 * [I] hdc : device context handle
4815 * [I] cdmode : custom draw mode
4820 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4822 POINT Origin, Position;
4824 /* Get scroll info once before loop */
4825 LISTVIEW_GetOrigin(infoPtr, &Origin);
4827 while(iterator_prev(i))
4829 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4830 Position.x += Origin.x;
4831 Position.y += Origin.y;
4833 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
4840 * Draws listview items.
4843 * [I] infoPtr : valid pointer to the listview structure
4844 * [I] hdc : device context handle
4845 * [I] prcErase : rect to be erased before refresh (may be NULL)
4850 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcErase)
4852 COLORREF oldTextColor = 0, oldBkColor = 0, oldClrTextBk, oldClrText;
4853 NMLVCUSTOMDRAW nmlvcd;
4860 HBITMAP hbmp = NULL;
4863 LISTVIEW_DUMP(infoPtr);
4865 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4866 TRACE("double buffering\n");
4868 hdc = CreateCompatibleDC(hdcOrig);
4870 ERR("Failed to create DC for backbuffer\n");
4873 hbmp = CreateCompatibleBitmap(hdcOrig, infoPtr->rcList.right,
4874 infoPtr->rcList.bottom);
4876 ERR("Failed to create bitmap for backbuffer\n");
4881 SelectObject(hdc, hbmp);
4882 SelectObject(hdc, infoPtr->hFont);
4884 /* Save dc values we're gonna trash while drawing
4885 * FIXME: Should be done in LISTVIEW_DrawItem() */
4886 hOldFont = SelectObject(hdc, infoPtr->hFont);
4887 oldBkMode = GetBkMode(hdc);
4888 oldBkColor = GetBkColor(hdc);
4889 oldTextColor = GetTextColor(hdc);
4892 infoPtr->bIsDrawing = TRUE;
4895 LISTVIEW_FillBkgnd(infoPtr, hdc, prcErase);
4896 } else if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4897 /* If no erasing was done (usually because RedrawWindow was called
4898 * with RDW_INVALIDATE only) we need to copy the old contents into
4899 * the backbuffer before continuing. */
4900 BitBlt(hdc, infoPtr->rcList.left, infoPtr->rcList.top,
4901 infoPtr->rcList.right - infoPtr->rcList.left,
4902 infoPtr->rcList.bottom - infoPtr->rcList.top,
4903 hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4906 /* FIXME: Shouldn't need to do this */
4907 oldClrTextBk = infoPtr->clrTextBk;
4908 oldClrText = infoPtr->clrText;
4910 infoPtr->cditemmode = CDRF_DODEFAULT;
4912 GetClientRect(infoPtr->hwndSelf, &rcClient);
4913 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4914 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4915 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4916 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4918 /* Use these colors to draw the items */
4919 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4920 infoPtr->clrText = nmlvcd.clrText;
4922 /* nothing to draw */
4923 if(infoPtr->nItemCount == 0) goto enddraw;
4925 /* figure out what we need to draw */
4926 iterator_visibleitems(&i, infoPtr, hdc);
4927 range = iterator_range(&i);
4929 /* send cache hint notification */
4930 if (infoPtr->dwStyle & LVS_OWNERDATA)
4934 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4935 nmlv.iFrom = range.lower;
4936 nmlv.iTo = range.upper - 1;
4937 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4940 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
4941 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4944 if (infoPtr->uView == LV_VIEW_DETAILS)
4945 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4946 else /* LV_VIEW_LIST, LV_VIEW_ICON or LV_VIEW_SMALLICON */
4947 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4949 /* if we have a focus rect and it's visible, draw it */
4950 if (infoPtr->bFocus && range.lower <= infoPtr->nFocusedItem &&
4951 (range.upper - 1) >= infoPtr->nFocusedItem)
4952 LISTVIEW_DrawFocusRect(infoPtr, hdc);
4954 iterator_destroy(&i);
4957 /* For LVS_EX_GRIDLINES go and draw lines */
4958 /* This includes the case where there were *no* items */
4959 if ((infoPtr->uView == LV_VIEW_DETAILS) && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
4960 LISTVIEW_RefreshReportGrid(infoPtr, hdc);
4962 /* Draw marquee rectangle if appropriate */
4963 if (infoPtr->bMarqueeSelect)
4964 DrawFocusRect(hdc, &infoPtr->marqueeDrawRect);
4966 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4967 notify_postpaint(infoPtr, &nmlvcd);
4969 infoPtr->clrTextBk = oldClrTextBk;
4970 infoPtr->clrText = oldClrText;
4973 BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top,
4974 infoPtr->rcList.right - infoPtr->rcList.left,
4975 infoPtr->rcList.bottom - infoPtr->rcList.top,
4976 hdc, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4981 SelectObject(hdc, hOldFont);
4982 SetBkMode(hdc, oldBkMode);
4983 SetBkColor(hdc, oldBkColor);
4984 SetTextColor(hdc, oldTextColor);
4987 infoPtr->bIsDrawing = FALSE;
4993 * Calculates the approximate width and height of a given number of items.
4996 * [I] infoPtr : valid pointer to the listview structure
4997 * [I] nItemCount : number of items
4998 * [I] wWidth : width
4999 * [I] wHeight : height
5002 * Returns a DWORD. The width in the low word and the height in high word.
5004 static DWORD LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO *infoPtr, INT nItemCount,
5005 WORD wWidth, WORD wHeight)
5007 INT nItemCountPerColumn = 1;
5008 INT nColumnCount = 0;
5009 DWORD dwViewRect = 0;
5011 if (nItemCount == -1)
5012 nItemCount = infoPtr->nItemCount;
5014 if (infoPtr->uView == LV_VIEW_LIST)
5016 if (wHeight == 0xFFFF)
5018 /* use current height */
5019 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
5022 if (wHeight < infoPtr->nItemHeight)
5023 wHeight = infoPtr->nItemHeight;
5027 if (infoPtr->nItemHeight > 0)
5029 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
5030 if (nItemCountPerColumn == 0)
5031 nItemCountPerColumn = 1;
5033 if (nItemCount % nItemCountPerColumn != 0)
5034 nColumnCount = nItemCount / nItemCountPerColumn;
5036 nColumnCount = nItemCount / nItemCountPerColumn + 1;
5040 /* Microsoft padding magic */
5041 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
5042 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
5044 dwViewRect = MAKELONG(wWidth, wHeight);
5046 else if (infoPtr->uView == LV_VIEW_DETAILS)
5050 if (infoPtr->nItemCount > 0)
5052 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
5053 wWidth = rcBox.right - rcBox.left;
5054 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
5058 /* use current height and width */
5059 if (wHeight == 0xffff)
5060 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
5061 if (wWidth == 0xffff)
5062 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
5065 dwViewRect = MAKELONG(wWidth, wHeight);
5067 else if (infoPtr->uView == LV_VIEW_ICON)
5073 nItemWidth = infoPtr->iconSpacing.cx;
5074 nItemHeight = infoPtr->iconSpacing.cy;
5076 if (nItemCount == -1)
5077 nItemCount = infoPtr->nItemCount;
5079 if (wWidth == 0xffff)
5080 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
5082 if (wWidth < nItemWidth)
5083 wWidth = nItemWidth;
5085 cols = wWidth / nItemWidth;
5086 if (cols > nItemCount)
5093 rows = nItemCount / cols;
5094 if (nItemCount % cols)
5100 wHeight = (nItemHeight * rows)+2;
5101 wWidth = (nItemWidth * cols)+2;
5103 dwViewRect = MAKELONG(wWidth, wHeight);
5105 else if (infoPtr->uView == LV_VIEW_SMALLICON)
5106 FIXME("uView == LV_VIEW_SMALLICON: not implemented\n");
5113 * Cancel edit label with saving item text.
5116 * [I] infoPtr : valid pointer to the listview structure
5119 * Always returns TRUE.
5121 static LRESULT LISTVIEW_CancelEditLabel(LISTVIEW_INFO *infoPtr)
5123 if (infoPtr->hwndEdit)
5125 /* handle value will be lost after LISTVIEW_EndEditLabelT */
5126 HWND edit = infoPtr->hwndEdit;
5128 LISTVIEW_EndEditLabelT(infoPtr, TRUE, IsWindowUnicode(infoPtr->hwndEdit));
5129 SendMessageW(edit, WM_CLOSE, 0, 0);
5137 * Create a drag image list for the specified item.
5140 * [I] infoPtr : valid pointer to the listview structure
5141 * [I] iItem : index of item
5142 * [O] lppt : Upper-left corner of the image
5145 * Returns a handle to the image list if successful, NULL otherwise.
5147 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
5153 HBITMAP hbmp, hOldbmp;
5154 HIMAGELIST dragList = 0;
5155 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
5157 if (iItem < 0 || iItem >= infoPtr->nItemCount)
5160 rcItem.left = LVIR_BOUNDS;
5161 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
5164 lppt->x = rcItem.left;
5165 lppt->y = rcItem.top;
5167 size.cx = rcItem.right - rcItem.left;
5168 size.cy = rcItem.bottom - rcItem.top;
5170 hdcOrig = GetDC(infoPtr->hwndSelf);
5171 hdc = CreateCompatibleDC(hdcOrig);
5172 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
5173 hOldbmp = SelectObject(hdc, hbmp);
5175 rcItem.left = rcItem.top = 0;
5176 rcItem.right = size.cx;
5177 rcItem.bottom = size.cy;
5178 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
5181 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
5183 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
5184 SelectObject(hdc, hOldbmp);
5185 ImageList_Add(dragList, hbmp, 0);
5188 SelectObject(hdc, hOldbmp);
5192 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
5194 TRACE("ret=%p\n", dragList);
5202 * Removes all listview items and subitems.
5205 * [I] infoPtr : valid pointer to the listview structure
5211 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr, BOOL destroy)
5214 HDPA hdpaSubItems = NULL;
5223 /* we do it directly, to avoid notifications */
5224 ranges_clear(infoPtr->selectionRanges);
5225 infoPtr->nSelectionMark = -1;
5226 infoPtr->nFocusedItem = -1;
5227 SetRectEmpty(&infoPtr->rcFocus);
5228 /* But we are supposed to leave nHotItem as is! */
5231 /* send LVN_DELETEALLITEMS notification */
5232 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
5234 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
5236 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
5238 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
5240 /* send LVN_DELETEITEM notification, if not suppressed
5241 and if it is not a virtual listview */
5242 if (!bSuppress) notify_deleteitem(infoPtr, i);
5243 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
5244 lpItem = DPA_GetPtr(hdpaSubItems, 0);
5245 /* free id struct */
5246 j = DPA_GetPtrIndex(infoPtr->hdpaItemIds, lpItem->id);
5247 lpID = DPA_GetPtr(infoPtr->hdpaItemIds, j);
5248 DPA_DeletePtr(infoPtr->hdpaItemIds, j);
5250 /* both item and subitem start with ITEMHDR header */
5251 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
5253 hdrItem = DPA_GetPtr(hdpaSubItems, j);
5254 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
5257 DPA_Destroy(hdpaSubItems);
5258 DPA_DeletePtr(infoPtr->hdpaItems, i);
5260 DPA_DeletePtr(infoPtr->hdpaPosX, i);
5261 DPA_DeletePtr(infoPtr->hdpaPosY, i);
5262 infoPtr->nItemCount --;
5267 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
5268 LISTVIEW_UpdateScroll(infoPtr);
5270 LISTVIEW_InvalidateList(infoPtr);
5277 * Scrolls, and updates the columns, when a column is changing width.
5280 * [I] infoPtr : valid pointer to the listview structure
5281 * [I] nColumn : column to scroll
5282 * [I] dx : amount of scroll, in pixels
5287 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
5289 COLUMN_INFO *lpColumnInfo;
5295 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
5296 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
5297 rcCol = lpColumnInfo->rcHeader;
5298 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
5299 rcCol.left = rcCol.right;
5301 /* adjust the other columns */
5302 hdi.mask = HDI_ORDER;
5303 if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdi))
5305 INT nOrder = hdi.iOrder;
5306 for (nCol = 0; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
5308 hdi.mask = HDI_ORDER;
5309 SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nCol, (LPARAM)&hdi);
5310 if (hdi.iOrder >= nOrder) {
5311 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
5312 lpColumnInfo->rcHeader.left += dx;
5313 lpColumnInfo->rcHeader.right += dx;
5318 /* do not update screen if not in report mode */
5319 if (!is_redrawing(infoPtr) || infoPtr->uView != LV_VIEW_DETAILS) return;
5321 /* Need to reset the item width when inserting a new column */
5322 infoPtr->nItemWidth += dx;
5324 LISTVIEW_UpdateScroll(infoPtr);
5325 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
5327 /* scroll to cover the deleted column, and invalidate for redraw */
5328 rcOld = infoPtr->rcList;
5329 rcOld.left = ptOrigin.x + rcCol.left + dx;
5330 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
5335 * Removes a column from the listview control.
5338 * [I] infoPtr : valid pointer to the listview structure
5339 * [I] nColumn : column index
5345 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
5349 TRACE("nColumn=%d\n", nColumn);
5351 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
5352 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5354 /* While the MSDN specifically says that column zero should not be deleted,
5355 what actually happens is that the column itself is deleted but no items or subitems
5359 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
5361 if (!SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nColumn, 0))
5364 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
5365 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
5367 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
5369 SUBITEM_INFO *lpSubItem, *lpDelItem;
5371 INT nItem, nSubItem, i;
5373 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
5375 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
5378 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
5380 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
5381 if (lpSubItem->iSubItem == nColumn)
5384 lpDelItem = lpSubItem;
5386 else if (lpSubItem->iSubItem > nColumn)
5388 lpSubItem->iSubItem--;
5392 /* if we found our subitem, zapp it */
5396 if (is_textW(lpDelItem->hdr.pszText))
5397 Free(lpDelItem->hdr.pszText);
5402 /* free dpa memory */
5403 DPA_DeletePtr(hdpaSubItems, nSubItem);
5408 /* update the other column info */
5409 LISTVIEW_UpdateItemSize(infoPtr);
5410 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
5411 LISTVIEW_InvalidateList(infoPtr);
5413 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
5420 * Invalidates the listview after an item's insertion or deletion.
5423 * [I] infoPtr : valid pointer to the listview structure
5424 * [I] nItem : item index
5425 * [I] dir : -1 if deleting, 1 if inserting
5430 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
5432 INT nPerCol, nItemCol, nItemRow;
5436 /* if we don't refresh, what's the point of scrolling? */
5437 if (!is_redrawing(infoPtr)) return;
5439 assert (abs(dir) == 1);
5441 /* arrange icons if autoarrange is on */
5442 if (is_autoarrange(infoPtr))
5444 BOOL arrange = TRUE;
5445 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
5446 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
5447 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
5450 /* scrollbars need updating */
5451 LISTVIEW_UpdateScroll(infoPtr);
5453 /* figure out the item's position */
5454 if (infoPtr->uView == LV_VIEW_DETAILS)
5455 nPerCol = infoPtr->nItemCount + 1;
5456 else if (infoPtr->uView == LV_VIEW_LIST)
5457 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
5458 else /* LV_VIEW_ICON, or LV_VIEW_SMALLICON */
5461 nItemCol = nItem / nPerCol;
5462 nItemRow = nItem % nPerCol;
5463 LISTVIEW_GetOrigin(infoPtr, &Origin);
5465 /* move the items below up a slot */
5466 rcScroll.left = nItemCol * infoPtr->nItemWidth;
5467 rcScroll.top = nItemRow * infoPtr->nItemHeight;
5468 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
5469 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
5470 OffsetRect(&rcScroll, Origin.x, Origin.y);
5471 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
5472 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
5474 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
5475 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
5476 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
5479 /* report has only that column, so we're done */
5480 if (infoPtr->uView == LV_VIEW_DETAILS) return;
5482 /* now for LISTs, we have to deal with the columns to the right */
5483 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
5485 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
5486 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
5487 OffsetRect(&rcScroll, Origin.x, Origin.y);
5488 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
5489 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
5490 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
5495 * Removes an item from the listview control.
5498 * [I] infoPtr : valid pointer to the listview structure
5499 * [I] nItem : item index
5505 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
5508 const BOOL is_icon = (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON);
5510 TRACE("(nItem=%d)\n", nItem);
5512 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5514 /* remove selection, and focus */
5516 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
5517 LISTVIEW_SetItemState(infoPtr, nItem, &item);
5519 /* send LVN_DELETEITEM notification. */
5520 if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
5522 /* we need to do this here, because we'll be deleting stuff */
5524 LISTVIEW_InvalidateItem(infoPtr, nItem);
5526 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
5534 hdpaSubItems = DPA_DeletePtr(infoPtr->hdpaItems, nItem);
5535 lpItem = DPA_GetPtr(hdpaSubItems, 0);
5537 /* free id struct */
5538 i = DPA_GetPtrIndex(infoPtr->hdpaItemIds, lpItem->id);
5539 lpID = DPA_GetPtr(infoPtr->hdpaItemIds, i);
5540 DPA_DeletePtr(infoPtr->hdpaItemIds, i);
5542 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
5544 hdrItem = DPA_GetPtr(hdpaSubItems, i);
5545 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
5548 DPA_Destroy(hdpaSubItems);
5553 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
5554 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
5557 infoPtr->nItemCount--;
5558 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
5560 /* now is the invalidation fun */
5562 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
5569 * Callback implementation for editlabel control
5572 * [I] infoPtr : valid pointer to the listview structure
5573 * [I] storeText : store edit box text as item text
5574 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
5580 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, BOOL storeText, BOOL isW)
5582 HWND hwndSelf = infoPtr->hwndSelf;
5583 NMLVDISPINFOW dispInfo;
5584 INT editedItem = infoPtr->nEditLabelItem;
5586 WCHAR *pszText = NULL;
5591 DWORD len = isW ? GetWindowTextLengthW(infoPtr->hwndEdit) : GetWindowTextLengthA(infoPtr->hwndEdit);
5595 if ((pszText = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))))
5597 if (isW) GetWindowTextW(infoPtr->hwndEdit, pszText, len+1);
5598 else GetWindowTextA(infoPtr->hwndEdit, (CHAR*)pszText, len+1);
5603 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
5605 infoPtr->nEditLabelItem = -1;
5606 infoPtr->hwndEdit = 0;
5608 ZeroMemory(&dispInfo, sizeof(dispInfo));
5609 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
5610 dispInfo.item.iItem = editedItem;
5611 dispInfo.item.iSubItem = 0;
5612 dispInfo.item.stateMask = ~0;
5613 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item))
5620 bSame = (lstrcmpW(dispInfo.item.pszText, pszText) == 0);
5623 LPWSTR tmp = textdupTtoW(pszText, FALSE);
5624 bSame = (lstrcmpW(dispInfo.item.pszText, tmp) == 0);
5625 textfreeT(tmp, FALSE);
5628 /* add the text from the edit in */
5629 dispInfo.item.mask |= LVIF_TEXT;
5630 dispInfo.item.pszText = bSame ? NULL : pszText;
5631 dispInfo.item.cchTextMax = bSame ? 0 : textlenT(pszText, isW);
5633 /* Do we need to update the Item Text */
5634 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW))
5639 if (!IsWindow(hwndSelf))
5644 if (!pszText) return TRUE;
5651 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
5653 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, editedItem);
5654 ITEM_INFO* lpItem = DPA_GetPtr(hdpaSubItems, 0);
5655 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
5657 LISTVIEW_InvalidateItem(infoPtr, editedItem);
5663 ZeroMemory(&dispInfo, sizeof(dispInfo));
5664 dispInfo.item.mask = LVIF_TEXT;
5665 dispInfo.item.iItem = editedItem;
5666 dispInfo.item.iSubItem = 0;
5667 dispInfo.item.pszText = pszText;
5668 dispInfo.item.cchTextMax = textlenT(pszText, isW);
5669 res = LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
5679 * Begin in place editing of specified list view item
5682 * [I] infoPtr : valid pointer to the listview structure
5683 * [I] nItem : item index
5684 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
5690 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
5692 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
5693 NMLVDISPINFOW dispInfo;
5696 HWND hwndSelf = infoPtr->hwndSelf;
5698 HFONT hOldFont = NULL;
5699 TEXTMETRICW textMetric;
5701 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
5703 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
5705 /* Is the EditBox still there, if so remove it */
5706 if(infoPtr->hwndEdit != 0)
5708 SetFocus(infoPtr->hwndSelf);
5709 infoPtr->hwndEdit = 0;
5712 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5714 infoPtr->nEditLabelItem = nItem;
5716 LISTVIEW_SetSelection(infoPtr, nItem);
5717 LISTVIEW_SetItemFocus(infoPtr, nItem);
5718 LISTVIEW_InvalidateItem(infoPtr, nItem);
5720 rect.left = LVIR_LABEL;
5721 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
5723 ZeroMemory(&dispInfo, sizeof(dispInfo));
5724 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
5725 dispInfo.item.iItem = nItem;
5726 dispInfo.item.iSubItem = 0;
5727 dispInfo.item.stateMask = ~0;
5728 dispInfo.item.pszText = szDispText;
5729 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
5730 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
5732 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE, isW);
5733 if (!infoPtr->hwndEdit) return 0;
5735 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
5737 if (!IsWindow(hwndSelf))
5739 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
5740 infoPtr->hwndEdit = 0;
5744 /* Now position and display edit box */
5745 hdc = GetDC(infoPtr->hwndSelf);
5747 /* Select the font to get appropriate metric dimensions */
5748 if(infoPtr->hFont != 0)
5749 hOldFont = SelectObject(hdc, infoPtr->hFont);
5751 /* Get String Length in pixels */
5752 GetTextExtentPoint32W(hdc, dispInfo.item.pszText, lstrlenW(dispInfo.item.pszText), &sz);
5754 /* Add Extra spacing for the next character */
5755 GetTextMetricsW(hdc, &textMetric);
5756 sz.cx += (textMetric.tmMaxCharWidth * 2);
5758 if(infoPtr->hFont != 0)
5759 SelectObject(hdc, hOldFont);
5761 ReleaseDC(infoPtr->hwndSelf, hdc);
5763 MoveWindow(infoPtr->hwndEdit, rect.left - 2, rect.top - 1, sz.cx,
5764 rect.bottom - rect.top + 2, FALSE);
5765 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
5766 SetFocus(infoPtr->hwndEdit);
5767 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
5768 return infoPtr->hwndEdit;
5774 * Ensures the specified item is visible, scrolling into view if necessary.
5777 * [I] infoPtr : valid pointer to the listview structure
5778 * [I] nItem : item index
5779 * [I] bPartial : partially or entirely visible
5785 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
5787 INT nScrollPosHeight = 0;
5788 INT nScrollPosWidth = 0;
5789 INT nHorzAdjust = 0;
5790 INT nVertAdjust = 0;
5793 RECT rcItem, rcTemp;
5795 rcItem.left = LVIR_BOUNDS;
5796 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
5798 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
5800 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
5802 /* scroll left/right, but in LV_VIEW_DETAILS mode */
5803 if (infoPtr->uView == LV_VIEW_LIST)
5804 nScrollPosWidth = infoPtr->nItemWidth;
5805 else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
5806 nScrollPosWidth = 1;
5808 if (rcItem.left < infoPtr->rcList.left)
5811 if (infoPtr->uView != LV_VIEW_DETAILS) nHorzDiff = rcItem.left - infoPtr->rcList.left;
5816 if (infoPtr->uView != LV_VIEW_DETAILS) nHorzDiff = rcItem.right - infoPtr->rcList.right;
5820 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
5822 /* scroll up/down, but not in LVS_LIST mode */
5823 if (infoPtr->uView == LV_VIEW_DETAILS)
5824 nScrollPosHeight = infoPtr->nItemHeight;
5825 else if ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON))
5826 nScrollPosHeight = 1;
5828 if (rcItem.top < infoPtr->rcList.top)
5831 if (infoPtr->uView != LV_VIEW_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
5836 if (infoPtr->uView != LV_VIEW_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
5840 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
5842 if (nScrollPosWidth)
5844 INT diff = nHorzDiff / nScrollPosWidth;
5845 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
5846 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
5849 if (nScrollPosHeight)
5851 INT diff = nVertDiff / nScrollPosHeight;
5852 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
5853 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
5861 * Searches for an item with specific characteristics.
5864 * [I] hwnd : window handle
5865 * [I] nStart : base item index
5866 * [I] lpFindInfo : item information to look for
5869 * SUCCESS : index of item
5872 static INT LISTVIEW_FindItemW(const LISTVIEW_INFO *infoPtr, INT nStart,
5873 const LVFINDINFOW *lpFindInfo)
5875 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5876 BOOL bWrap = FALSE, bNearest = FALSE;
5877 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
5878 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
5879 POINT Position, Destination;
5882 /* Search in virtual listviews should be done by application, not by
5883 listview control, so we just send LVN_ODFINDITEMW and return the result */
5884 if (infoPtr->dwStyle & LVS_OWNERDATA)
5888 nmlv.iStart = nStart;
5889 nmlv.lvfi = *lpFindInfo;
5890 return notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
5893 if (!lpFindInfo || nItem < 0) return -1;
5896 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL) ||
5897 lpFindInfo->flags & LVFI_SUBSTRING)
5899 lvItem.mask |= LVIF_TEXT;
5900 lvItem.pszText = szDispText;
5901 lvItem.cchTextMax = DISP_TEXT_SIZE;
5904 if (lpFindInfo->flags & LVFI_WRAP)
5907 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
5908 (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON))
5913 LISTVIEW_GetOrigin(infoPtr, &Origin);
5914 Destination.x = lpFindInfo->pt.x - Origin.x;
5915 Destination.y = lpFindInfo->pt.y - Origin.y;
5916 switch(lpFindInfo->vkDirection)
5918 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
5919 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
5920 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
5921 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
5922 case VK_HOME: Destination.x = Destination.y = 0; break;
5923 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5924 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5926 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
5927 Destination.x = rcArea.right;
5928 Destination.y = rcArea.bottom;
5930 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
5934 else Destination.x = Destination.y = 0;
5936 /* if LVFI_PARAM is specified, all other flags are ignored */
5937 if (lpFindInfo->flags & LVFI_PARAM)
5939 lvItem.mask |= LVIF_PARAM;
5941 lvItem.mask &= ~LVIF_TEXT;
5945 for (; nItem < nLast; nItem++)
5947 lvItem.iItem = nItem;
5948 lvItem.iSubItem = 0;
5949 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
5951 if (lvItem.mask & LVIF_PARAM)
5953 if (lpFindInfo->lParam == lvItem.lParam)
5959 if (lvItem.mask & LVIF_TEXT)
5961 if (lpFindInfo->flags & (LVFI_PARTIAL | LVFI_SUBSTRING))
5963 WCHAR *p = strstrW(lvItem.pszText, lpFindInfo->psz);
5964 if (!p || p != lvItem.pszText) continue;
5968 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
5972 if (!bNearest) return nItem;
5974 /* This is very inefficient. To do a good job here,
5975 * we need a sorted array of (x,y) item positions */
5976 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5978 /* compute the distance^2 to the destination */
5979 xdist = Destination.x - Position.x;
5980 ydist = Destination.y - Position.y;
5981 dist = xdist * xdist + ydist * ydist;
5983 /* remember the distance, and item if it's closer */
5987 nNearestItem = nItem;
5994 nLast = min(nStart + 1, infoPtr->nItemCount);
5999 return nNearestItem;
6004 * Searches for an item with specific characteristics.
6007 * [I] hwnd : window handle
6008 * [I] nStart : base item index
6009 * [I] lpFindInfo : item information to look for
6012 * SUCCESS : index of item
6015 static INT LISTVIEW_FindItemA(const LISTVIEW_INFO *infoPtr, INT nStart,
6016 const LVFINDINFOA *lpFindInfo)
6018 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL) ||
6019 lpFindInfo->flags & LVFI_SUBSTRING;
6024 memcpy(&fiw, lpFindInfo, sizeof(fiw));
6025 if (hasText) fiw.psz = strW = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
6026 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
6027 textfreeT(strW, FALSE);
6033 * Retrieves the background image of the listview control.
6036 * [I] infoPtr : valid pointer to the listview structure
6037 * [O] lpBkImage : background image attributes
6043 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
6045 /* FIXME (listview, "empty stub!\n"); */
6051 * Retrieves column attributes.
6054 * [I] infoPtr : valid pointer to the listview structure
6055 * [I] nColumn : column index
6056 * [IO] lpColumn : column information
6057 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
6058 * otherwise it is in fact a LPLVCOLUMNA
6064 static BOOL LISTVIEW_GetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
6066 COLUMN_INFO *lpColumnInfo;
6069 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6070 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6072 /* initialize memory */
6073 ZeroMemory(&hdi, sizeof(hdi));
6075 if (lpColumn->mask & LVCF_TEXT)
6077 hdi.mask |= HDI_TEXT;
6078 hdi.pszText = lpColumn->pszText;
6079 hdi.cchTextMax = lpColumn->cchTextMax;
6082 if (lpColumn->mask & LVCF_IMAGE)
6083 hdi.mask |= HDI_IMAGE;
6085 if (lpColumn->mask & LVCF_ORDER)
6086 hdi.mask |= HDI_ORDER;
6088 if (lpColumn->mask & LVCF_SUBITEM)
6089 hdi.mask |= HDI_LPARAM;
6091 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
6093 if (lpColumn->mask & LVCF_FMT)
6094 lpColumn->fmt = lpColumnInfo->fmt;
6096 if (lpColumn->mask & LVCF_WIDTH)
6097 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
6099 if (lpColumn->mask & LVCF_IMAGE)
6100 lpColumn->iImage = hdi.iImage;
6102 if (lpColumn->mask & LVCF_ORDER)
6103 lpColumn->iOrder = hdi.iOrder;
6105 if (lpColumn->mask & LVCF_SUBITEM)
6106 lpColumn->iSubItem = hdi.lParam;
6108 if (lpColumn->mask & LVCF_MINWIDTH)
6109 lpColumn->cxMin = lpColumnInfo->cxMin;
6115 static BOOL LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
6117 TRACE("iCount=%d, lpiArray=%p\n", iCount, lpiArray);
6122 return SendMessageW(infoPtr->hwndHeader, HDM_GETORDERARRAY, iCount, (LPARAM)lpiArray);
6127 * Retrieves the column width.
6130 * [I] infoPtr : valid pointer to the listview structure
6131 * [I] int : column index
6134 * SUCCESS : column width
6137 static INT LISTVIEW_GetColumnWidth(const LISTVIEW_INFO *infoPtr, INT nColumn)
6139 INT nColumnWidth = 0;
6142 TRACE("nColumn=%d\n", nColumn);
6144 /* we have a 'column' in LIST and REPORT mode only */
6145 switch(infoPtr->uView)
6148 nColumnWidth = infoPtr->nItemWidth;
6150 case LV_VIEW_DETAILS:
6151 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDN_ITEMCHANGED.
6152 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
6153 * HDN_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
6155 * TODO: should we do the same in LVM_GETCOLUMN?
6157 hdItem.mask = HDI_WIDTH;
6158 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdItem))
6160 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr->hwndSelf, nColumn);
6163 nColumnWidth = hdItem.cxy;
6167 TRACE("nColumnWidth=%d\n", nColumnWidth);
6168 return nColumnWidth;
6173 * In list or report display mode, retrieves the number of items that can fit
6174 * vertically in the visible area. In icon or small icon display mode,
6175 * retrieves the total number of visible items.
6178 * [I] infoPtr : valid pointer to the listview structure
6181 * Number of fully visible items.
6183 static INT LISTVIEW_GetCountPerPage(const LISTVIEW_INFO *infoPtr)
6185 switch (infoPtr->uView)
6188 case LV_VIEW_SMALLICON:
6189 return infoPtr->nItemCount;
6190 case LV_VIEW_DETAILS:
6191 return LISTVIEW_GetCountPerColumn(infoPtr);
6193 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
6201 * Retrieves an image list handle.
6204 * [I] infoPtr : valid pointer to the listview structure
6205 * [I] nImageList : image list identifier
6208 * SUCCESS : image list handle
6211 static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImageList)
6215 case LVSIL_NORMAL: return infoPtr->himlNormal;
6216 case LVSIL_SMALL: return infoPtr->himlSmall;
6217 case LVSIL_STATE: return infoPtr->himlState;
6218 case LVSIL_GROUPHEADER:
6219 FIXME("LVSIL_GROUPHEADER not supported\n");
6222 WARN("got unknown imagelist index - %d\n", nImageList);
6227 /* LISTVIEW_GetISearchString */
6231 * Retrieves item attributes.
6234 * [I] hwnd : window handle
6235 * [IO] lpLVItem : item info
6236 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
6237 * if FALSE, then lpLVItem is a LPLVITEMA.
6240 * This is the internal 'GetItem' interface -- it tries to
6241 * be smart and avoid text copies, if possible, by modifying
6242 * lpLVItem->pszText to point to the text string. Please note
6243 * that this is not always possible (e.g. OWNERDATA), so on
6244 * entry you *must* supply valid values for pszText, and cchTextMax.
6245 * The only difference to the documented interface is that upon
6246 * return, you should use *only* the lpLVItem->pszText, rather than
6247 * the buffer pointer you provided on input. Most code already does
6248 * that, so it's not a problem.
6249 * For the two cases when the text must be copied (that is,
6250 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
6256 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
6258 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
6259 NMLVDISPINFOW dispInfo;
6265 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6267 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
6270 if (lpLVItem->mask == 0) return TRUE;
6272 /* make a local copy */
6273 isubitem = lpLVItem->iSubItem;
6275 /* a quick optimization if all we're asked is the focus state
6276 * these queries are worth optimising since they are common,
6277 * and can be answered in constant time, without the heavy accesses */
6278 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
6279 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
6281 lpLVItem->state = 0;
6282 if (infoPtr->nFocusedItem == lpLVItem->iItem)
6283 lpLVItem->state |= LVIS_FOCUSED;
6287 ZeroMemory(&dispInfo, sizeof(dispInfo));
6289 /* if the app stores all the data, handle it separately */
6290 if (infoPtr->dwStyle & LVS_OWNERDATA)
6292 dispInfo.item.state = 0;
6294 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
6295 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) ||
6296 ((lpLVItem->mask & LVIF_STATE) && (infoPtr->uCallbackMask & lpLVItem->stateMask)))
6298 UINT mask = lpLVItem->mask;
6300 /* NOTE: copy only fields which we _know_ are initialized, some apps
6301 * depend on the uninitialized fields being 0 */
6302 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
6303 dispInfo.item.iItem = lpLVItem->iItem;
6304 dispInfo.item.iSubItem = isubitem;
6305 if (lpLVItem->mask & LVIF_TEXT)
6307 if (lpLVItem->mask & LVIF_NORECOMPUTE)
6309 dispInfo.item.mask &= ~(LVIF_TEXT | LVIF_NORECOMPUTE);
6312 dispInfo.item.pszText = lpLVItem->pszText;
6313 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
6316 if (lpLVItem->mask & LVIF_STATE)
6317 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
6318 /* could be zeroed on LVIF_NORECOMPUTE case */
6319 if (dispInfo.item.mask != 0)
6321 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
6322 dispInfo.item.stateMask = lpLVItem->stateMask;
6323 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6325 /* full size structure expected - _WIN32IE >= 0x560 */
6326 *lpLVItem = dispInfo.item;
6328 else if (lpLVItem->mask & LVIF_INDENT)
6330 /* indent member expected - _WIN32IE >= 0x300 */
6331 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
6335 /* minimal structure expected */
6336 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
6338 lpLVItem->mask = mask;
6339 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
6343 /* make sure lParam is zeroed out */
6344 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
6346 /* callback marked pointer required here */
6347 if ((lpLVItem->mask & LVIF_TEXT) && (lpLVItem->mask & LVIF_NORECOMPUTE))
6348 lpLVItem->pszText = LPSTR_TEXTCALLBACKW;
6350 /* we store only a little state, so if we're not asked, we're done */
6351 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
6353 /* if focus is handled by us, report it */
6354 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
6356 lpLVItem->state &= ~LVIS_FOCUSED;
6357 if (infoPtr->nFocusedItem == lpLVItem->iItem)
6358 lpLVItem->state |= LVIS_FOCUSED;
6361 /* and do the same for selection, if we handle it */
6362 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
6364 lpLVItem->state &= ~LVIS_SELECTED;
6365 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
6366 lpLVItem->state |= LVIS_SELECTED;
6372 /* find the item and subitem structures before we proceed */
6373 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
6374 lpItem = DPA_GetPtr(hdpaSubItems, 0);
6379 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
6380 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
6383 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
6388 pItemHdr = &lpItem->hdr;
6390 /* Do we need to query the state from the app? */
6391 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
6393 dispInfo.item.mask |= LVIF_STATE;
6394 dispInfo.item.stateMask = infoPtr->uCallbackMask;
6397 /* Do we need to enquire about the image? */
6398 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
6399 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
6401 dispInfo.item.mask |= LVIF_IMAGE;
6402 dispInfo.item.iImage = I_IMAGECALLBACK;
6405 /* Only items support indentation */
6406 if ((lpLVItem->mask & LVIF_INDENT) && lpItem->iIndent == I_INDENTCALLBACK &&
6409 dispInfo.item.mask |= LVIF_INDENT;
6410 dispInfo.item.iIndent = I_INDENTCALLBACK;
6413 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
6414 if ((lpLVItem->mask & LVIF_TEXT) && !(lpLVItem->mask & LVIF_NORECOMPUTE) &&
6415 !is_textW(pItemHdr->pszText))
6417 dispInfo.item.mask |= LVIF_TEXT;
6418 dispInfo.item.pszText = lpLVItem->pszText;
6419 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
6420 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
6421 *dispInfo.item.pszText = '\0';
6424 /* If we don't have all the requested info, query the application */
6425 if (dispInfo.item.mask != 0)
6427 dispInfo.item.iItem = lpLVItem->iItem;
6428 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
6429 dispInfo.item.lParam = lpItem->lParam;
6430 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
6431 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
6434 /* we should not store values for subitems */
6435 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
6437 /* Now, handle the iImage field */
6438 if (dispInfo.item.mask & LVIF_IMAGE)
6440 lpLVItem->iImage = dispInfo.item.iImage;
6441 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
6442 pItemHdr->iImage = dispInfo.item.iImage;
6444 else if (lpLVItem->mask & LVIF_IMAGE)
6446 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
6447 lpLVItem->iImage = pItemHdr->iImage;
6449 lpLVItem->iImage = 0;
6452 /* The pszText field */
6453 if (dispInfo.item.mask & LVIF_TEXT)
6455 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
6456 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
6458 lpLVItem->pszText = dispInfo.item.pszText;
6460 else if (lpLVItem->mask & LVIF_TEXT)
6462 /* if LVN_GETDISPINFO's disabled with LVIF_NORECOMPUTE return callback placeholder */
6463 if (isW || !is_textW(pItemHdr->pszText)) lpLVItem->pszText = pItemHdr->pszText;
6464 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
6467 /* Next is the lParam field */
6468 if (dispInfo.item.mask & LVIF_PARAM)
6470 lpLVItem->lParam = dispInfo.item.lParam;
6471 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
6472 lpItem->lParam = dispInfo.item.lParam;
6474 else if (lpLVItem->mask & LVIF_PARAM)
6475 lpLVItem->lParam = lpItem->lParam;
6477 /* if this is a subitem, we're done */
6478 if (isubitem) return TRUE;
6480 /* ... the state field (this one is different due to uCallbackmask) */
6481 if (lpLVItem->mask & LVIF_STATE)
6483 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
6484 if (dispInfo.item.mask & LVIF_STATE)
6486 lpLVItem->state &= ~dispInfo.item.stateMask;
6487 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
6489 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
6491 lpLVItem->state &= ~LVIS_FOCUSED;
6492 if (infoPtr->nFocusedItem == lpLVItem->iItem)
6493 lpLVItem->state |= LVIS_FOCUSED;
6495 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
6497 lpLVItem->state &= ~LVIS_SELECTED;
6498 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
6499 lpLVItem->state |= LVIS_SELECTED;
6503 /* and last, but not least, the indent field */
6504 if (dispInfo.item.mask & LVIF_INDENT)
6506 lpLVItem->iIndent = dispInfo.item.iIndent;
6507 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && lpItem->iIndent == I_INDENTCALLBACK)
6508 lpItem->iIndent = dispInfo.item.iIndent;
6510 else if (lpLVItem->mask & LVIF_INDENT)
6512 lpLVItem->iIndent = lpItem->iIndent;
6520 * Retrieves item attributes.
6523 * [I] hwnd : window handle
6524 * [IO] lpLVItem : item info
6525 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
6526 * if FALSE, then lpLVItem is a LPLVITEMA.
6529 * This is the external 'GetItem' interface -- it properly copies
6530 * the text in the provided buffer.
6536 static BOOL LISTVIEW_GetItemExtT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
6541 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
6544 pszText = lpLVItem->pszText;
6545 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
6546 if (bResult && lpLVItem->pszText != pszText)
6548 if (lpLVItem->pszText != LPSTR_TEXTCALLBACKW)
6549 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
6551 pszText = LPSTR_TEXTCALLBACKW;
6553 lpLVItem->pszText = pszText;
6561 * Retrieves the position (upper-left) of the listview control item.
6562 * Note that for LVS_ICON style, the upper-left is that of the icon
6563 * and not the bounding box.
6566 * [I] infoPtr : valid pointer to the listview structure
6567 * [I] nItem : item index
6568 * [O] lpptPosition : coordinate information
6574 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
6578 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
6580 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
6582 LISTVIEW_GetOrigin(infoPtr, &Origin);
6583 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
6585 if (infoPtr->uView == LV_VIEW_ICON)
6587 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
6588 lpptPosition->y += ICON_TOP_PADDING;
6590 lpptPosition->x += Origin.x;
6591 lpptPosition->y += Origin.y;
6593 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
6600 * Retrieves the bounding rectangle for a listview control item.
6603 * [I] infoPtr : valid pointer to the listview structure
6604 * [I] nItem : item index
6605 * [IO] lprc : bounding rectangle coordinates
6606 * lprc->left specifies the portion of the item for which the bounding
6607 * rectangle will be retrieved.
6609 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
6610 * including the icon and label.
6613 * * Experiment shows that native control returns:
6614 * * width = min (48, length of text line)
6615 * * .left = position.x - (width - iconsize.cx)/2
6616 * * .right = .left + width
6617 * * height = #lines of text * ntmHeight + icon height + 8
6618 * * .top = position.y - 2
6619 * * .bottom = .top + height
6620 * * separation between items .y = itemSpacing.cy - height
6621 * * .x = itemSpacing.cx - width
6622 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
6625 * * Experiment shows that native control returns:
6626 * * width = iconSize.cx + 16
6627 * * .left = position.x - (width - iconsize.cx)/2
6628 * * .right = .left + width
6629 * * height = iconSize.cy + 4
6630 * * .top = position.y - 2
6631 * * .bottom = .top + height
6632 * * separation between items .y = itemSpacing.cy - height
6633 * * .x = itemSpacing.cx - width
6634 * LVIR_LABEL Returns the bounding rectangle of the item text.
6637 * * Experiment shows that native control returns:
6638 * * width = text length
6639 * * .left = position.x - width/2
6640 * * .right = .left + width
6641 * * height = ntmH * linecount + 2
6642 * * .top = position.y + iconSize.cy + 6
6643 * * .bottom = .top + height
6644 * * separation between items .y = itemSpacing.cy - height
6645 * * .x = itemSpacing.cx - width
6646 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
6647 * rectangles, but excludes columns in report view.
6654 * Note that the bounding rectangle of the label in the LVS_ICON view depends
6655 * upon whether the window has the focus currently and on whether the item
6656 * is the one with the focus. Ensure that the control's record of which
6657 * item has the focus agrees with the items' records.
6659 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
6661 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6662 BOOL doLabel = TRUE, oversizedBox = FALSE;
6663 POINT Position, Origin;
6667 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
6669 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
6671 LISTVIEW_GetOrigin(infoPtr, &Origin);
6672 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
6674 /* Be smart and try to figure out the minimum we have to do */
6675 if (lprc->left == LVIR_ICON) doLabel = FALSE;
6676 if (infoPtr->uView == LV_VIEW_DETAILS && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
6677 if (infoPtr->uView == LV_VIEW_ICON && lprc->left != LVIR_ICON &&
6678 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
6679 oversizedBox = TRUE;
6681 /* get what we need from the item before hand, so we make
6682 * only one request. This can speed up things, if data
6683 * is stored on the app side */
6685 if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
6686 if (doLabel) lvItem.mask |= LVIF_TEXT;
6687 lvItem.iItem = nItem;
6688 lvItem.iSubItem = 0;
6689 lvItem.pszText = szDispText;
6690 lvItem.cchTextMax = DISP_TEXT_SIZE;
6691 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
6692 /* we got the state already up, simulate it here, to avoid a reget */
6693 if (infoPtr->uView == LV_VIEW_ICON && (lprc->left != LVIR_ICON))
6695 lvItem.mask |= LVIF_STATE;
6696 lvItem.stateMask = LVIS_FOCUSED;
6697 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
6700 if (infoPtr->uView == LV_VIEW_DETAILS && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
6701 lprc->left = LVIR_BOUNDS;
6707 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
6711 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc);
6715 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
6718 case LVIR_SELECTBOUNDS:
6719 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL);
6723 WARN("Unknown value: %d\n", lprc->left);
6727 if (infoPtr->uView == LV_VIEW_DETAILS)
6729 if (mode != LVIR_BOUNDS)
6730 OffsetRect(lprc, Origin.x + LISTVIEW_GetColumnInfo(infoPtr, 0)->rcHeader.left,
6731 Position.y + Origin.y);
6733 OffsetRect(lprc, Origin.x, Position.y + Origin.y);
6736 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
6738 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
6745 * Retrieves the spacing between listview control items.
6748 * [I] infoPtr : valid pointer to the listview structure
6749 * [IO] lprc : rectangle to receive the output
6750 * on input, lprc->top = nSubItem
6751 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
6753 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
6754 * not only those of the first column.
6755 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
6761 static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
6767 if (!lprc) return FALSE;
6769 nColumn = lprc->top;
6771 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
6772 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
6774 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
6776 if (infoPtr->uView != LV_VIEW_DETAILS) return FALSE;
6778 /* special case for header items */
6781 if (lprc->left != LVIR_BOUNDS)
6783 FIXME("Only LVIR_BOUNDS is implemented for header, got %d\n", lprc->left);
6787 if (infoPtr->hwndHeader)
6788 return SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, lprc->top, (LPARAM)lprc);
6791 memset(lprc, 0, sizeof(RECT));
6796 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
6798 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6801 lvItem.iItem = nItem;
6802 lvItem.iSubItem = nColumn;
6804 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
6808 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
6813 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
6817 ERR("Unknown bounds=%d\n", lprc->left);
6821 OffsetRect(lprc, 0, Position.y);
6828 * Retrieves the width of a label.
6831 * [I] infoPtr : valid pointer to the listview structure
6834 * SUCCESS : string width (in pixels)
6837 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *infoPtr, INT nItem)
6839 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6842 TRACE("(nItem=%d)\n", nItem);
6844 lvItem.mask = LVIF_TEXT;
6845 lvItem.iItem = nItem;
6846 lvItem.iSubItem = 0;
6847 lvItem.pszText = szDispText;
6848 lvItem.cchTextMax = DISP_TEXT_SIZE;
6849 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6851 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6856 * Retrieves the spacing between listview control items.
6859 * [I] infoPtr : valid pointer to the listview structure
6860 * [I] bSmall : flag for small or large icon
6863 * Horizontal + vertical spacing
6865 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall)
6871 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6875 if (infoPtr->uView == LV_VIEW_ICON)
6876 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6878 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6885 * Retrieves the state of a listview control item.
6888 * [I] infoPtr : valid pointer to the listview structure
6889 * [I] nItem : item index
6890 * [I] uMask : state mask
6893 * State specified by the mask.
6895 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
6899 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6901 lvItem.iItem = nItem;
6902 lvItem.iSubItem = 0;
6903 lvItem.mask = LVIF_STATE;
6904 lvItem.stateMask = uMask;
6905 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6907 return lvItem.state & uMask;
6912 * Retrieves the text of a listview control item or subitem.
6915 * [I] hwnd : window handle
6916 * [I] nItem : item index
6917 * [IO] lpLVItem : item information
6918 * [I] isW : TRUE if lpLVItem is Unicode
6921 * SUCCESS : string length
6924 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6926 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6928 lpLVItem->mask = LVIF_TEXT;
6929 lpLVItem->iItem = nItem;
6930 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
6932 return textlenT(lpLVItem->pszText, isW);
6937 * Searches for an item based on properties + relationships.
6940 * [I] infoPtr : valid pointer to the listview structure
6941 * [I] nItem : item index
6942 * [I] uFlags : relationship flag
6945 * SUCCESS : item index
6948 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
6951 LVFINDINFOW lvFindInfo;
6952 INT nCountPerColumn;
6956 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
6957 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
6959 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6961 if (uFlags & LVNI_CUT)
6964 if (uFlags & LVNI_DROPHILITED)
6965 uMask |= LVIS_DROPHILITED;
6967 if (uFlags & LVNI_FOCUSED)
6968 uMask |= LVIS_FOCUSED;
6970 if (uFlags & LVNI_SELECTED)
6971 uMask |= LVIS_SELECTED;
6973 /* if we're asked for the focused item, that's only one,
6974 * so it's worth optimizing */
6975 if (uFlags & LVNI_FOCUSED)
6977 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
6978 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
6981 if (uFlags & LVNI_ABOVE)
6983 if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
6988 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6994 /* Special case for autoarrange - move 'til the top of a list */
6995 if (is_autoarrange(infoPtr))
6997 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6998 while (nItem - nCountPerRow >= 0)
7000 nItem -= nCountPerRow;
7001 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7006 lvFindInfo.flags = LVFI_NEARESTXY;
7007 lvFindInfo.vkDirection = VK_UP;
7008 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
7009 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
7011 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7016 else if (uFlags & LVNI_BELOW)
7018 if ((infoPtr->uView == LV_VIEW_LIST) || (infoPtr->uView == LV_VIEW_DETAILS))
7020 while (nItem < infoPtr->nItemCount)
7023 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7029 /* Special case for autoarrange - move 'til the bottom of a list */
7030 if (is_autoarrange(infoPtr))
7032 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
7033 while (nItem + nCountPerRow < infoPtr->nItemCount )
7035 nItem += nCountPerRow;
7036 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7041 lvFindInfo.flags = LVFI_NEARESTXY;
7042 lvFindInfo.vkDirection = VK_DOWN;
7043 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
7044 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
7046 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7051 else if (uFlags & LVNI_TOLEFT)
7053 if (infoPtr->uView == LV_VIEW_LIST)
7055 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
7056 while (nItem - nCountPerColumn >= 0)
7058 nItem -= nCountPerColumn;
7059 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7063 else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
7065 /* Special case for autoarrange - move 'til the beginning of a row */
7066 if (is_autoarrange(infoPtr))
7068 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
7069 while (nItem % nCountPerRow > 0)
7072 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7077 lvFindInfo.flags = LVFI_NEARESTXY;
7078 lvFindInfo.vkDirection = VK_LEFT;
7079 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
7080 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
7082 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7087 else if (uFlags & LVNI_TORIGHT)
7089 if (infoPtr->uView == LV_VIEW_LIST)
7091 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
7092 while (nItem + nCountPerColumn < infoPtr->nItemCount)
7094 nItem += nCountPerColumn;
7095 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7099 else if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
7101 /* Special case for autoarrange - move 'til the end of a row */
7102 if (is_autoarrange(infoPtr))
7104 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
7105 while (nItem % nCountPerRow < nCountPerRow - 1 )
7108 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7113 lvFindInfo.flags = LVFI_NEARESTXY;
7114 lvFindInfo.vkDirection = VK_RIGHT;
7115 LISTVIEW_GetItemPosition(infoPtr, nItem, &lvFindInfo.pt);
7116 while ((nItem = LISTVIEW_FindItemW(infoPtr, nItem, &lvFindInfo)) != -1)
7118 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
7127 /* search by index */
7128 for (i = nItem; i < infoPtr->nItemCount; i++)
7130 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
7138 /* LISTVIEW_GetNumberOfWorkAreas */
7142 * Retrieves the origin coordinates when in icon or small icon display mode.
7145 * [I] infoPtr : valid pointer to the listview structure
7146 * [O] lpptOrigin : coordinate information
7151 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
7153 INT nHorzPos = 0, nVertPos = 0;
7154 SCROLLINFO scrollInfo;
7156 scrollInfo.cbSize = sizeof(SCROLLINFO);
7157 scrollInfo.fMask = SIF_POS;
7159 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
7160 nHorzPos = scrollInfo.nPos;
7161 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
7162 nVertPos = scrollInfo.nPos;
7164 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
7166 lpptOrigin->x = infoPtr->rcList.left;
7167 lpptOrigin->y = infoPtr->rcList.top;
7168 if (infoPtr->uView == LV_VIEW_LIST)
7169 nHorzPos *= infoPtr->nItemWidth;
7170 else if (infoPtr->uView == LV_VIEW_DETAILS)
7171 nVertPos *= infoPtr->nItemHeight;
7173 lpptOrigin->x -= nHorzPos;
7174 lpptOrigin->y -= nVertPos;
7176 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
7181 * Retrieves the width of a string.
7184 * [I] hwnd : window handle
7185 * [I] lpszText : text string to process
7186 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
7189 * SUCCESS : string width (in pixels)
7192 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
7197 if (is_textT(lpszText, isW))
7199 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
7200 HDC hdc = GetDC(infoPtr->hwndSelf);
7201 HFONT hOldFont = SelectObject(hdc, hFont);
7204 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
7206 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
7207 SelectObject(hdc, hOldFont);
7208 ReleaseDC(infoPtr->hwndSelf, hdc);
7210 return stringSize.cx;
7215 * Determines which listview item is located at the specified position.
7218 * [I] infoPtr : valid pointer to the listview structure
7219 * [IO] lpht : hit test information
7220 * [I] subitem : fill out iSubItem.
7221 * [I] select : return the index only if the hit selects the item
7224 * (mm 20001022): We must not allow iSubItem to be touched, for
7225 * an app might pass only a structure with space up to iItem!
7226 * (MS Office 97 does that for instance in the file open dialog)
7229 * SUCCESS : item index
7232 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
7234 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
7235 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
7236 POINT Origin, Position, opt;
7241 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
7245 if (subitem) lpht->iSubItem = 0;
7247 LISTVIEW_GetOrigin(infoPtr, &Origin);
7249 /* set whole list relation flags */
7250 if (subitem && infoPtr->uView == LV_VIEW_DETAILS)
7252 /* LVM_SUBITEMHITTEST checks left bound of possible client area */
7253 if (infoPtr->rcList.left > lpht->pt.x && Origin.x < lpht->pt.x)
7254 lpht->flags |= LVHT_TOLEFT;
7256 if (lpht->pt.y < infoPtr->rcList.top && lpht->pt.y >= 0)
7257 opt.y = lpht->pt.y + infoPtr->rcList.top;
7261 if (infoPtr->rcList.bottom < opt.y)
7262 lpht->flags |= LVHT_BELOW;
7266 if (infoPtr->rcList.left > lpht->pt.x)
7267 lpht->flags |= LVHT_TOLEFT;
7268 else if (infoPtr->rcList.right < lpht->pt.x)
7269 lpht->flags |= LVHT_TORIGHT;
7271 if (infoPtr->rcList.top > lpht->pt.y)
7272 lpht->flags |= LVHT_ABOVE;
7273 else if (infoPtr->rcList.bottom < lpht->pt.y)
7274 lpht->flags |= LVHT_BELOW;
7277 /* even if item is invalid try to find subitem */
7278 if (infoPtr->uView == LV_VIEW_DETAILS && subitem)
7283 opt.x = lpht->pt.x - Origin.x;
7285 lpht->iSubItem = -1;
7286 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
7288 pRect = &LISTVIEW_GetColumnInfo(infoPtr, j)->rcHeader;
7290 if ((opt.x >= pRect->left) && (opt.x < pRect->right))
7296 TRACE("lpht->iSubItem=%d\n", lpht->iSubItem);
7299 TRACE("lpht->flags=0x%x\n", lpht->flags);
7300 if (lpht->flags) return -1;
7302 lpht->flags |= LVHT_NOWHERE;
7304 /* first deal with the large items */
7305 rcSearch.left = lpht->pt.x;
7306 rcSearch.top = lpht->pt.y;
7307 rcSearch.right = rcSearch.left + 1;
7308 rcSearch.bottom = rcSearch.top + 1;
7310 iterator_frameditems(&i, infoPtr, &rcSearch);
7311 iterator_next(&i); /* go to first item in the sequence */
7313 iterator_destroy(&i);
7315 TRACE("lpht->iItem=%d\n", iItem);
7316 if (iItem == -1) return -1;
7318 lvItem.mask = LVIF_STATE | LVIF_TEXT;
7319 if (infoPtr->uView == LV_VIEW_DETAILS) lvItem.mask |= LVIF_INDENT;
7320 lvItem.stateMask = LVIS_STATEIMAGEMASK;
7321 if (infoPtr->uView == LV_VIEW_ICON) lvItem.stateMask |= LVIS_FOCUSED;
7322 lvItem.iItem = iItem;
7323 lvItem.iSubItem = subitem ? lpht->iSubItem : 0;
7324 lvItem.pszText = szDispText;
7325 lvItem.cchTextMax = DISP_TEXT_SIZE;
7326 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
7327 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
7329 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
7330 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
7331 opt.x = lpht->pt.x - Position.x - Origin.x;
7333 if (lpht->pt.y < infoPtr->rcList.top && lpht->pt.y >= 0)
7334 opt.y = lpht->pt.y - Position.y - Origin.y + infoPtr->rcList.top;
7336 opt.y = lpht->pt.y - Position.y - Origin.y;
7338 if (infoPtr->uView == LV_VIEW_DETAILS)
7341 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
7342 opt.x = lpht->pt.x - Origin.x;
7346 UnionRect(&rcBounds, &rcIcon, &rcLabel);
7347 UnionRect(&rcBounds, &rcBounds, &rcState);
7349 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
7350 if (!PtInRect(&rcBounds, opt)) return -1;
7352 if (PtInRect(&rcIcon, opt))
7353 lpht->flags |= LVHT_ONITEMICON;
7354 else if (PtInRect(&rcLabel, opt))
7355 lpht->flags |= LVHT_ONITEMLABEL;
7356 else if (infoPtr->himlState && PtInRect(&rcState, opt))
7357 lpht->flags |= LVHT_ONITEMSTATEICON;
7358 /* special case for LVS_EX_FULLROWSELECT */
7359 if (infoPtr->uView == LV_VIEW_DETAILS && infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT &&
7360 !(lpht->flags & LVHT_ONITEM))
7362 lpht->flags = LVHT_ONITEM | LVHT_ABOVE;
7364 if (lpht->flags & LVHT_ONITEM)
7365 lpht->flags &= ~LVHT_NOWHERE;
7366 TRACE("lpht->flags=0x%x\n", lpht->flags);
7368 if (select && !(infoPtr->uView == LV_VIEW_DETAILS &&
7369 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
7370 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
7372 if (infoPtr->uView == LV_VIEW_DETAILS)
7374 /* get main item bounds */
7375 lvItem.iSubItem = 0;
7376 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
7377 UnionRect(&rcBounds, &rcIcon, &rcLabel);
7378 UnionRect(&rcBounds, &rcBounds, &rcState);
7380 if (!PtInRect(&rcBounds, opt)) iItem = -1;
7382 return lpht->iItem = iItem;
7387 * Inserts a new item in the listview control.
7390 * [I] infoPtr : valid pointer to the listview structure
7391 * [I] lpLVItem : item information
7392 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
7395 * SUCCESS : new item index
7398 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
7405 BOOL is_sorted, has_changed;
7407 HWND hwndSelf = infoPtr->hwndSelf;
7409 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
7411 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
7413 /* make sure it's an item, and not a subitem; cannot insert a subitem */
7414 if (!lpLVItem || lpLVItem->iSubItem) return -1;
7416 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
7418 if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
7420 /* insert item in listview control data structure */
7421 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
7422 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
7424 /* link with id struct */
7425 if (!(lpID = Alloc(sizeof(ITEM_ID)))) goto fail;
7427 lpID->item = hdpaSubItems;
7428 lpID->id = get_next_itemid(infoPtr);
7429 if ( DPA_InsertPtr(infoPtr->hdpaItemIds, infoPtr->nItemCount, lpID) == -1) goto fail;
7431 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
7432 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
7434 if (lpLVItem->iItem < 0 && !is_sorted) return -1;
7436 /* calculate new item index */
7443 while (i < infoPtr->nItemCount)
7445 hItem = DPA_GetPtr( infoPtr->hdpaItems, i);
7446 item_s = (ITEM_INFO*)DPA_GetPtr(hItem, 0);
7448 cmpv = textcmpWT(item_s->hdr.pszText, lpLVItem->pszText, isW);
7449 if (infoPtr->dwStyle & LVS_SORTDESCENDING) cmpv *= -1;
7451 if (cmpv >= 0) break;
7457 nItem = min(lpLVItem->iItem, infoPtr->nItemCount);
7459 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
7460 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
7461 if (nItem == -1) goto fail;
7462 if (infoPtr->nItemCount++ == 0) LISTVIEW_UpdateItemSize(infoPtr);
7464 /* shift indices first so they don't get tangled */
7465 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
7467 /* set the item attributes */
7468 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
7470 /* full size structure expected - _WIN32IE >= 0x560 */
7473 else if (lpLVItem->mask & LVIF_INDENT)
7475 /* indent member expected - _WIN32IE >= 0x300 */
7476 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
7480 /* minimal structure expected */
7481 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
7484 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7486 item.mask |= LVIF_STATE;
7487 item.stateMask |= LVIS_STATEIMAGEMASK;
7488 item.state &= ~LVIS_STATEIMAGEMASK;
7489 item.state |= INDEXTOSTATEIMAGEMASK(1);
7491 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
7493 /* make room for the position, if we are in the right mode */
7494 if ((infoPtr->uView == LV_VIEW_SMALLICON) || (infoPtr->uView == LV_VIEW_ICON))
7496 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
7498 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
7500 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
7505 /* send LVN_INSERTITEM notification */
7506 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
7508 nmlv.lParam = lpItem->lParam;
7509 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
7510 if (!IsWindow(hwndSelf))
7513 /* align items (set position of each item) */
7514 if (infoPtr->uView == LV_VIEW_SMALLICON || infoPtr->uView == LV_VIEW_ICON)
7518 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
7519 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
7521 LISTVIEW_NextIconPosTop(infoPtr, &pt);
7523 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
7526 /* now is the invalidation fun */
7527 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
7531 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
7532 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
7533 infoPtr->nItemCount--;
7535 DPA_DeletePtr(hdpaSubItems, 0);
7536 DPA_Destroy (hdpaSubItems);
7543 * Checks item visibility.
7546 * [I] infoPtr : valid pointer to the listview structure
7547 * [I] nFirst : item index to check for
7550 * Item visible : TRUE
7551 * Item invisible or failure : FALSE
7553 static BOOL LISTVIEW_IsItemVisible(const LISTVIEW_INFO *infoPtr, INT nItem)
7555 POINT Origin, Position;
7560 TRACE("nItem=%d\n", nItem);
7562 if (nItem < 0 || nItem >= DPA_GetPtrCount(infoPtr->hdpaItems)) return FALSE;
7564 LISTVIEW_GetOrigin(infoPtr, &Origin);
7565 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
7566 rcItem.left = Position.x + Origin.x;
7567 rcItem.top = Position.y + Origin.y;
7568 rcItem.right = rcItem.left + infoPtr->nItemWidth;
7569 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
7571 hdc = GetDC(infoPtr->hwndSelf);
7572 if (!hdc) return FALSE;
7573 ret = RectVisible(hdc, &rcItem);
7574 ReleaseDC(infoPtr->hwndSelf, hdc);
7581 * Redraws a range of items.
7584 * [I] infoPtr : valid pointer to the listview structure
7585 * [I] nFirst : first item
7586 * [I] nLast : last item
7592 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
7596 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
7597 max(nFirst, nLast) >= infoPtr->nItemCount)
7600 for (i = nFirst; i <= nLast; i++)
7601 LISTVIEW_InvalidateItem(infoPtr, i);
7608 * Scroll the content of a listview.
7611 * [I] infoPtr : valid pointer to the listview structure
7612 * [I] dx : horizontal scroll amount in pixels
7613 * [I] dy : vertical scroll amount in pixels
7620 * If the control is in report view (LV_VIEW_DETAILS) the control can
7621 * be scrolled only in line increments. "dy" will be rounded to the
7622 * nearest number of pixels that are a whole line. Ex: if line height
7623 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
7624 * is passed, then the scroll will be 0. (per MSDN 7/2002)
7626 * For: (per experimentation with native control and CSpy ListView)
7627 * LV_VIEW_ICON dy=1 = 1 pixel (vertical only)
7629 * LV_VIEW_SMALLICON dy=1 = 1 pixel (vertical only)
7631 * LV_VIEW_LIST dx=1 = 1 column (horizontal only)
7632 * but will only scroll 1 column per message
7633 * no matter what the value.
7634 * dy must be 0 or FALSE returned.
7635 * LV_VIEW_DETAILS dx=1 = 1 pixel
7639 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7641 switch(infoPtr->uView) {
7642 case LV_VIEW_DETAILS:
7643 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
7644 dy /= infoPtr->nItemHeight;
7647 if (dy != 0) return FALSE;
7654 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
7655 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
7662 * Sets the background color.
7665 * [I] infoPtr : valid pointer to the listview structure
7666 * [I] clrBk : background color
7672 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
7674 TRACE("(clrBk=%x)\n", clrBk);
7676 if(infoPtr->clrBk != clrBk) {
7677 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
7678 infoPtr->clrBk = clrBk;
7679 if (clrBk == CLR_NONE)
7680 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
7683 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
7684 infoPtr->dwLvExStyle &= ~LVS_EX_TRANSPARENTBKGND;
7686 LISTVIEW_InvalidateList(infoPtr);
7692 /* LISTVIEW_SetBkImage */
7694 /*** Helper for {Insert,Set}ColumnT *only* */
7695 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
7696 const LVCOLUMNW *lpColumn, BOOL isW)
7698 if (lpColumn->mask & LVCF_FMT)
7700 /* format member is valid */
7701 lphdi->mask |= HDI_FORMAT;
7703 /* set text alignment (leftmost column must be left-aligned) */
7704 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
7705 lphdi->fmt |= HDF_LEFT;
7706 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
7707 lphdi->fmt |= HDF_RIGHT;
7708 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
7709 lphdi->fmt |= HDF_CENTER;
7711 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
7712 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
7714 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
7716 lphdi->fmt |= HDF_IMAGE;
7717 lphdi->iImage = I_IMAGECALLBACK;
7720 if (lpColumn->fmt & LVCFMT_FIXED_WIDTH)
7721 lphdi->fmt |= HDF_FIXEDWIDTH;
7724 if (lpColumn->mask & LVCF_WIDTH)
7726 lphdi->mask |= HDI_WIDTH;
7727 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
7729 /* make it fill the remainder of the controls width */
7733 for(item_index = 0; item_index < (nColumn - 1); item_index++)
7735 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
7736 lphdi->cxy += rcHeader.right - rcHeader.left;
7739 /* retrieve the layout of the header */
7740 GetClientRect(infoPtr->hwndSelf, &rcHeader);
7741 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
7743 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
7746 lphdi->cxy = lpColumn->cx;
7749 if (lpColumn->mask & LVCF_TEXT)
7751 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
7752 lphdi->fmt |= HDF_STRING;
7753 lphdi->pszText = lpColumn->pszText;
7754 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
7757 if (lpColumn->mask & LVCF_IMAGE)
7759 lphdi->mask |= HDI_IMAGE;
7760 lphdi->iImage = lpColumn->iImage;
7763 if (lpColumn->mask & LVCF_ORDER)
7765 lphdi->mask |= HDI_ORDER;
7766 lphdi->iOrder = lpColumn->iOrder;
7773 * Inserts a new column.
7776 * [I] infoPtr : valid pointer to the listview structure
7777 * [I] nColumn : column index
7778 * [I] lpColumn : column information
7779 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
7782 * SUCCESS : new column index
7785 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
7786 const LVCOLUMNW *lpColumn, BOOL isW)
7788 COLUMN_INFO *lpColumnInfo;
7792 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
7794 if (!lpColumn || nColumn < 0) return -1;
7795 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
7797 ZeroMemory(&hdi, sizeof(HDITEMW));
7798 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
7801 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
7802 * (can be seen in SPY) otherwise column never gets added.
7804 if (!(lpColumn->mask & LVCF_WIDTH)) {
7805 hdi.mask |= HDI_WIDTH;
7810 * when the iSubItem is available Windows copies it to the header lParam. It seems
7811 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
7813 if (lpColumn->mask & LVCF_SUBITEM)
7815 hdi.mask |= HDI_LPARAM;
7816 hdi.lParam = lpColumn->iSubItem;
7819 /* create header if not present */
7820 LISTVIEW_CreateHeader(infoPtr);
7821 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle) &&
7822 (infoPtr->uView == LV_VIEW_DETAILS) && (WS_VISIBLE & infoPtr->dwStyle))
7824 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7827 /* insert item in header control */
7828 nNewColumn = SendMessageW(infoPtr->hwndHeader,
7829 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
7830 (WPARAM)nColumn, (LPARAM)&hdi);
7831 if (nNewColumn == -1) return -1;
7832 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
7834 /* create our own column info */
7835 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
7836 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
7838 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
7839 if (lpColumn->mask & LVCF_MINWIDTH) lpColumnInfo->cxMin = lpColumn->cxMin;
7840 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMRECT, nNewColumn, (LPARAM)&lpColumnInfo->rcHeader))
7843 /* now we have to actually adjust the data */
7844 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
7846 SUBITEM_INFO *lpSubItem;
7850 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
7852 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
7853 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
7855 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
7856 if (lpSubItem->iSubItem >= nNewColumn)
7857 lpSubItem->iSubItem++;
7862 /* make space for the new column */
7863 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
7864 LISTVIEW_UpdateItemSize(infoPtr);
7869 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
7872 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
7880 * Sets the attributes of a header item.
7883 * [I] infoPtr : valid pointer to the listview structure
7884 * [I] nColumn : column index
7885 * [I] lpColumn : column attributes
7886 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
7892 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
7893 const LVCOLUMNW *lpColumn, BOOL isW)
7895 HDITEMW hdi, hdiget;
7898 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
7900 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7902 ZeroMemory(&hdi, sizeof(HDITEMW));
7903 if (lpColumn->mask & LVCF_FMT)
7905 hdi.mask |= HDI_FORMAT;
7906 hdiget.mask = HDI_FORMAT;
7907 if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdiget))
7908 hdi.fmt = hdiget.fmt & HDF_STRING;
7910 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
7912 /* set header item attributes */
7913 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
7914 if (!bResult) return FALSE;
7916 if (lpColumn->mask & LVCF_FMT)
7918 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
7919 INT oldFmt = lpColumnInfo->fmt;
7921 lpColumnInfo->fmt = lpColumn->fmt;
7922 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
7924 if (infoPtr->uView == LV_VIEW_DETAILS) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
7928 if (lpColumn->mask & LVCF_MINWIDTH)
7929 LISTVIEW_GetColumnInfo(infoPtr, nColumn)->cxMin = lpColumn->cxMin;
7936 * Sets the column order array
7939 * [I] infoPtr : valid pointer to the listview structure
7940 * [I] iCount : number of elements in column order array
7941 * [I] lpiArray : pointer to column order array
7947 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
7949 TRACE("iCount %d lpiArray %p\n", iCount, lpiArray);
7951 if (!lpiArray || !IsWindow(infoPtr->hwndHeader)) return FALSE;
7953 infoPtr->colRectsDirty = TRUE;
7955 return SendMessageW(infoPtr->hwndHeader, HDM_SETORDERARRAY, iCount, (LPARAM)lpiArray);
7960 * Sets the width of a column
7963 * [I] infoPtr : valid pointer to the listview structure
7964 * [I] nColumn : column index
7965 * [I] cx : column width
7971 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
7973 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
7977 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
7979 /* set column width only if in report or list mode */
7980 if (infoPtr->uView != LV_VIEW_DETAILS && infoPtr->uView != LV_VIEW_LIST) return FALSE;
7982 /* take care of invalid cx values */
7983 if(infoPtr->uView == LV_VIEW_DETAILS && cx < -2) cx = LVSCW_AUTOSIZE;
7984 else if (infoPtr->uView == LV_VIEW_LIST && cx < 1) return FALSE;
7986 /* resize all columns if in LV_VIEW_LIST mode */
7987 if(infoPtr->uView == LV_VIEW_LIST)
7989 infoPtr->nItemWidth = cx;
7990 LISTVIEW_InvalidateList(infoPtr);
7994 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7996 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
8001 lvItem.mask = LVIF_TEXT;
8003 lvItem.iSubItem = nColumn;
8004 lvItem.pszText = szDispText;
8005 lvItem.cchTextMax = DISP_TEXT_SIZE;
8006 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
8008 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
8009 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
8010 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
8012 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
8013 max_cx += infoPtr->iconSize.cx;
8014 max_cx += TRAILING_LABEL_PADDING;
8017 /* autosize based on listview items width */
8018 if(cx == LVSCW_AUTOSIZE)
8020 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
8022 /* if iCol is the last column make it fill the remainder of the controls width */
8023 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
8028 LISTVIEW_GetOrigin(infoPtr, &Origin);
8029 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
8031 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
8035 /* Despite what the MS docs say, if this is not the last
8036 column, then MS resizes the column to the width of the
8037 largest text string in the column, including headers
8038 and items. This is different from LVSCW_AUTOSIZE in that
8039 LVSCW_AUTOSIZE ignores the header string length. */
8042 /* retrieve header text */
8043 hdi.mask = HDI_TEXT;
8044 hdi.cchTextMax = DISP_TEXT_SIZE;
8045 hdi.pszText = szDispText;
8046 if (SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdi))
8048 HDC hdc = GetDC(infoPtr->hwndSelf);
8049 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
8052 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
8053 cx = size.cx + TRAILING_HEADER_PADDING;
8054 /* FIXME: Take into account the header image, if one is present */
8055 SelectObject(hdc, old_font);
8056 ReleaseDC(infoPtr->hwndSelf, hdc);
8058 cx = max (cx, max_cx);
8062 if (cx < 0) return FALSE;
8064 /* call header to update the column change */
8065 hdi.mask = HDI_WIDTH;
8066 hdi.cxy = max(cx, LISTVIEW_GetColumnInfo(infoPtr, nColumn)->cxMin);
8067 TRACE("hdi.cxy=%d\n", hdi.cxy);
8068 return SendMessageW(infoPtr->hwndHeader, HDM_SETITEMW, nColumn, (LPARAM)&hdi);
8072 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
8075 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
8078 HBITMAP hbm_im, hbm_mask, hbm_orig;
8080 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
8081 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
8084 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
8085 ILC_COLOR | ILC_MASK, 2, 2);
8086 hdc_wnd = GetDC(infoPtr->hwndSelf);
8087 hdc = CreateCompatibleDC(hdc_wnd);
8088 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
8089 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
8090 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
8092 rc.left = rc.top = 0;
8093 rc.right = GetSystemMetrics(SM_CXSMICON);
8094 rc.bottom = GetSystemMetrics(SM_CYSMICON);
8096 hbm_orig = SelectObject(hdc, hbm_mask);
8097 FillRect(hdc, &rc, hbr_white);
8098 InflateRect(&rc, -2, -2);
8099 FillRect(hdc, &rc, hbr_black);
8101 SelectObject(hdc, hbm_im);
8102 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
8103 SelectObject(hdc, hbm_orig);
8104 ImageList_Add(himl, hbm_im, hbm_mask);
8106 SelectObject(hdc, hbm_im);
8107 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
8108 SelectObject(hdc, hbm_orig);
8109 ImageList_Add(himl, hbm_im, hbm_mask);
8111 DeleteObject(hbm_mask);
8112 DeleteObject(hbm_im);
8120 * Sets the extended listview style.
8123 * [I] infoPtr : valid pointer to the listview structure
8125 * [I] dwStyle : style
8128 * SUCCESS : previous style
8131 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwExStyle)
8133 DWORD dwOldExStyle = infoPtr->dwLvExStyle;
8137 infoPtr->dwLvExStyle = (dwOldExStyle & ~dwMask) | (dwExStyle & dwMask);
8139 infoPtr->dwLvExStyle = dwExStyle;
8141 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_CHECKBOXES)
8143 HIMAGELIST himl = 0;
8144 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
8147 item.mask = LVIF_STATE;
8148 item.stateMask = LVIS_STATEIMAGEMASK;
8149 item.state = INDEXTOSTATEIMAGEMASK(1);
8150 LISTVIEW_SetItemState(infoPtr, -1, &item);
8152 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
8153 if(!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8154 ImageList_Destroy(infoPtr->himlState);
8156 himl = LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
8157 /* checkbox list replaces prevous custom list or... */
8158 if(((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) &&
8159 !(infoPtr->dwStyle & LVS_SHAREIMAGELISTS)) ||
8160 /* ...previous was checkbox list */
8161 (dwOldExStyle & LVS_EX_CHECKBOXES))
8162 ImageList_Destroy(himl);
8165 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_HEADERDRAGDROP)
8169 /* if not already created */
8170 LISTVIEW_CreateHeader(infoPtr);
8172 dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
8173 if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
8174 dwStyle |= HDS_DRAGDROP;
8176 dwStyle &= ~HDS_DRAGDROP;
8177 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
8180 /* GRIDLINES adds decoration at top so changes sizes */
8181 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_GRIDLINES)
8183 LISTVIEW_UpdateSize(infoPtr);
8186 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_TRANSPARENTBKGND)
8188 if (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTBKGND)
8189 LISTVIEW_SetBkColor(infoPtr, CLR_NONE);
8192 LISTVIEW_InvalidateList(infoPtr);
8193 return dwOldExStyle;
8198 * Sets the new hot cursor used during hot tracking and hover selection.
8201 * [I] infoPtr : valid pointer to the listview structure
8202 * [I] hCursor : the new hot cursor handle
8205 * Returns the previous hot cursor
8207 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
8209 HCURSOR oldCursor = infoPtr->hHotCursor;
8211 infoPtr->hHotCursor = hCursor;
8219 * Sets the hot item index.
8222 * [I] infoPtr : valid pointer to the listview structure
8223 * [I] iIndex : index
8226 * SUCCESS : previous hot item index
8227 * FAILURE : -1 (no hot item)
8229 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
8231 INT iOldIndex = infoPtr->nHotItem;
8233 infoPtr->nHotItem = iIndex;
8241 * Sets the amount of time the cursor must hover over an item before it is selected.
8244 * [I] infoPtr : valid pointer to the listview structure
8245 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
8248 * Returns the previous hover time
8250 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
8252 DWORD oldHoverTime = infoPtr->dwHoverTime;
8254 infoPtr->dwHoverTime = dwHoverTime;
8256 return oldHoverTime;
8261 * Sets spacing for icons of LVS_ICON style.
8264 * [I] infoPtr : valid pointer to the listview structure
8265 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
8266 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
8269 * MAKELONG(oldcx, oldcy)
8271 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
8273 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
8275 TRACE("requested=(%d,%d)\n", cx, cy);
8277 /* this is supported only for LVS_ICON style */
8278 if (infoPtr->uView != LV_VIEW_ICON) return oldspacing;
8280 /* set to defaults, if instructed to */
8281 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
8282 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
8284 /* if 0 then compute width
8285 * FIXME: Should scan each item and determine max width of
8286 * icon or label, then make that the width */
8288 cx = infoPtr->iconSpacing.cx;
8290 /* if 0 then compute height */
8292 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
8293 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
8296 infoPtr->iconSpacing.cx = cx;
8297 infoPtr->iconSpacing.cy = cy;
8299 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
8300 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
8301 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
8302 infoPtr->ntmHeight);
8304 /* these depend on the iconSpacing */
8305 LISTVIEW_UpdateItemSize(infoPtr);
8310 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
8314 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
8321 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
8322 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
8331 * [I] infoPtr : valid pointer to the listview structure
8332 * [I] nType : image list type
8333 * [I] himl : image list handle
8336 * SUCCESS : old image list
8339 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
8341 INT oldHeight = infoPtr->nItemHeight;
8342 HIMAGELIST himlOld = 0;
8344 TRACE("(nType=%d, himl=%p\n", nType, himl);
8349 himlOld = infoPtr->himlNormal;
8350 infoPtr->himlNormal = himl;
8351 if (infoPtr->uView == LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
8352 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8356 himlOld = infoPtr->himlSmall;
8357 infoPtr->himlSmall = himl;
8358 if (infoPtr->uView != LV_VIEW_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
8362 himlOld = infoPtr->himlState;
8363 infoPtr->himlState = himl;
8364 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
8365 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
8369 ERR("Unknown icon type=%d\n", nType);
8373 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
8374 if (infoPtr->nItemHeight != oldHeight)
8375 LISTVIEW_UpdateScroll(infoPtr);
8382 * Preallocates memory (does *not* set the actual count of items !)
8385 * [I] infoPtr : valid pointer to the listview structure
8386 * [I] nItems : item count (projected number of items to allocate)
8387 * [I] dwFlags : update flags
8393 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
8395 TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
8397 if (infoPtr->dwStyle & LVS_OWNERDATA)
8399 INT nOldCount = infoPtr->nItemCount;
8401 if (nItems < nOldCount)
8403 RANGE range = { nItems, nOldCount };
8404 ranges_del(infoPtr->selectionRanges, range);
8405 if (infoPtr->nFocusedItem >= nItems)
8407 LISTVIEW_SetItemFocus(infoPtr, -1);
8408 SetRectEmpty(&infoPtr->rcFocus);
8412 infoPtr->nItemCount = nItems;
8413 LISTVIEW_UpdateScroll(infoPtr);
8415 /* the flags are valid only in ownerdata report and list modes */
8416 if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON) dwFlags = 0;
8418 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
8419 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
8421 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
8422 LISTVIEW_InvalidateList(infoPtr);
8429 LISTVIEW_GetOrigin(infoPtr, &Origin);
8430 nFrom = min(nOldCount, nItems);
8431 nTo = max(nOldCount, nItems);
8433 if (infoPtr->uView == LV_VIEW_DETAILS)
8436 rcErase.top = nFrom * infoPtr->nItemHeight;
8437 rcErase.right = infoPtr->nItemWidth;
8438 rcErase.bottom = nTo * infoPtr->nItemHeight;
8439 OffsetRect(&rcErase, Origin.x, Origin.y);
8440 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
8441 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
8443 else /* LV_VIEW_LIST */
8445 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
8447 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
8448 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
8449 rcErase.right = rcErase.left + infoPtr->nItemWidth;
8450 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
8451 OffsetRect(&rcErase, Origin.x, Origin.y);
8452 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
8453 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
8455 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
8457 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
8458 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
8459 OffsetRect(&rcErase, Origin.x, Origin.y);
8460 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
8461 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
8467 /* According to MSDN for non-LVS_OWNERDATA this is just
8468 * a performance issue. The control allocates its internal
8469 * data structures for the number of items specified. It
8470 * cuts down on the number of memory allocations. Therefore
8471 * we will just issue a WARN here
8473 WARN("for non-ownerdata performance option not implemented.\n");
8481 * Sets the position of an item.
8484 * [I] infoPtr : valid pointer to the listview structure
8485 * [I] nItem : item index
8486 * [I] pt : coordinate
8492 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT *pt)
8496 TRACE("(nItem=%d, pt=%s\n", nItem, wine_dbgstr_point(pt));
8498 if (!pt || nItem < 0 || nItem >= infoPtr->nItemCount ||
8499 !(infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)) return FALSE;
8502 LISTVIEW_GetOrigin(infoPtr, &Origin);
8504 /* This point value seems to be an undocumented feature.
8505 * The best guess is that it means either at the origin,
8506 * or at true beginning of the list. I will assume the origin. */
8507 if ((Pt.x == -1) && (Pt.y == -1))
8510 if (infoPtr->uView == LV_VIEW_ICON)
8512 Pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
8513 Pt.y -= ICON_TOP_PADDING;
8518 infoPtr->bAutoarrange = FALSE;
8520 return LISTVIEW_MoveIconTo(infoPtr, nItem, &Pt, FALSE);
8525 * Sets the state of one or many items.
8528 * [I] infoPtr : valid pointer to the listview structure
8529 * [I] nItem : item index
8530 * [I] lpLVItem : item or subitem info
8536 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
8538 BOOL bResult = TRUE;
8541 lvItem.iItem = nItem;
8542 lvItem.iSubItem = 0;
8543 lvItem.mask = LVIF_STATE;
8544 lvItem.state = lpLVItem->state;
8545 lvItem.stateMask = lpLVItem->stateMask;
8546 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
8550 /* select all isn't allowed in LVS_SINGLESEL */
8551 if ((lvItem.state & lvItem.stateMask & LVIS_SELECTED) && (infoPtr->dwStyle & LVS_SINGLESEL))
8554 /* focus all isn't allowed */
8555 if (lvItem.state & lvItem.stateMask & LVIS_FOCUSED) return FALSE;
8557 /* apply to all items */
8558 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
8559 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
8562 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
8569 * Sets the text of an item or subitem.
8572 * [I] hwnd : window handle
8573 * [I] nItem : item index
8574 * [I] lpLVItem : item or subitem info
8575 * [I] isW : TRUE if input is Unicode
8581 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
8585 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
8587 lvItem.iItem = nItem;
8588 lvItem.iSubItem = lpLVItem->iSubItem;
8589 lvItem.mask = LVIF_TEXT;
8590 lvItem.pszText = lpLVItem->pszText;
8591 lvItem.cchTextMax = lpLVItem->cchTextMax;
8593 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
8595 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
8600 * Set item index that marks the start of a multiple selection.
8603 * [I] infoPtr : valid pointer to the listview structure
8604 * [I] nIndex : index
8607 * Index number or -1 if there is no selection mark.
8609 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
8611 INT nOldIndex = infoPtr->nSelectionMark;
8613 TRACE("(nIndex=%d)\n", nIndex);
8615 infoPtr->nSelectionMark = nIndex;
8622 * Sets the text background color.
8625 * [I] infoPtr : valid pointer to the listview structure
8626 * [I] clrTextBk : text background color
8632 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
8634 TRACE("(clrTextBk=%x)\n", clrTextBk);
8636 if (infoPtr->clrTextBk != clrTextBk)
8638 infoPtr->clrTextBk = clrTextBk;
8639 LISTVIEW_InvalidateList(infoPtr);
8647 * Sets the text foreground color.
8650 * [I] infoPtr : valid pointer to the listview structure
8651 * [I] clrText : text color
8657 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
8659 TRACE("(clrText=%x)\n", clrText);
8661 if (infoPtr->clrText != clrText)
8663 infoPtr->clrText = clrText;
8664 LISTVIEW_InvalidateList(infoPtr);
8672 * Sets new ToolTip window to ListView control.
8675 * [I] infoPtr : valid pointer to the listview structure
8676 * [I] hwndNewToolTip : handle to new ToolTip
8681 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
8683 HWND hwndOldToolTip = infoPtr->hwndToolTip;
8684 infoPtr->hwndToolTip = hwndNewToolTip;
8685 return hwndOldToolTip;
8690 * sets the Unicode character format flag for the control
8692 * [I] infoPtr :valid pointer to the listview structure
8693 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
8696 * Old Unicode Format
8698 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL fUnicode)
8700 SHORT rc = infoPtr->notifyFormat;
8701 infoPtr->notifyFormat = (fUnicode) ? NFR_UNICODE : NFR_ANSI;
8702 return rc == NFR_UNICODE;
8707 * sets the control view mode
8709 * [I] infoPtr :valid pointer to the listview structure
8710 * [I] nView :new view mode value
8716 static INT LISTVIEW_SetView(LISTVIEW_INFO *infoPtr, DWORD nView)
8718 SIZE oldIconSize = infoPtr->iconSize;
8721 if (infoPtr->uView == nView) return 1;
8723 if ((INT)nView < 0 || nView > LV_VIEW_MAX) return -1;
8724 if (nView == LV_VIEW_TILE)
8726 FIXME("View LV_VIEW_TILE unimplemented\n");
8730 infoPtr->uView = nView;
8732 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8733 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
8735 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
8736 SetRectEmpty(&infoPtr->rcFocus);
8738 himl = (nView == LV_VIEW_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
8739 set_icon_size(&infoPtr->iconSize, himl, nView != LV_VIEW_ICON);
8744 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
8746 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
8747 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
8748 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
8750 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8752 case LV_VIEW_SMALLICON:
8753 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8755 case LV_VIEW_DETAILS:
8760 LISTVIEW_CreateHeader( infoPtr );
8762 hl.prc = &infoPtr->rcList;
8764 SendMessageW(infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl);
8765 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
8766 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
8773 LISTVIEW_UpdateItemSize(infoPtr);
8774 LISTVIEW_UpdateSize(infoPtr);
8775 LISTVIEW_UpdateScroll(infoPtr);
8776 LISTVIEW_InvalidateList(infoPtr);
8778 TRACE("nView=%d\n", nView);
8783 /* LISTVIEW_SetWorkAreas */
8787 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMS
8790 * [I] first : pointer to first ITEM_INFO to compare
8791 * [I] second : pointer to second ITEM_INFO to compare
8792 * [I] lParam : HWND of control
8795 * if first comes before second : negative
8796 * if first comes after second : positive
8797 * if first and second are equivalent : zero
8799 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
8801 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
8802 ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
8803 ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
8805 /* Forward the call to the client defined callback */
8806 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
8811 * Callback internally used by LISTVIEW_SortItems() in response of LVM_SORTITEMSEX
8814 * [I] first : pointer to first ITEM_INFO to compare
8815 * [I] second : pointer to second ITEM_INFO to compare
8816 * [I] lParam : HWND of control
8819 * if first comes before second : negative
8820 * if first comes after second : positive
8821 * if first and second are equivalent : zero
8823 static INT WINAPI LISTVIEW_CallBackCompareEx(LPVOID first, LPVOID second, LPARAM lParam)
8825 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
8826 INT first_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, first );
8827 INT second_idx = DPA_GetPtrIndex( infoPtr->hdpaItems, second );
8829 /* Forward the call to the client defined callback */
8830 return (infoPtr->pfnCompare)( first_idx, second_idx, infoPtr->lParamSort );
8835 * Sorts the listview items.
8838 * [I] infoPtr : valid pointer to the listview structure
8839 * [I] pfnCompare : application-defined value
8840 * [I] lParamSort : pointer to comparison callback
8841 * [I] IsEx : TRUE when LVM_SORTITEMSEX used
8847 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare,
8848 LPARAM lParamSort, BOOL IsEx)
8852 LPVOID selectionMarkItem = NULL;
8853 LPVOID focusedItem = NULL;
8856 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
8858 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
8860 if (!pfnCompare) return FALSE;
8861 if (!infoPtr->hdpaItems) return FALSE;
8863 /* if there are 0 or 1 items, there is no need to sort */
8864 if (infoPtr->nItemCount < 2) return TRUE;
8866 /* clear selection */
8867 ranges_clear(infoPtr->selectionRanges);
8869 /* save selection mark and focused item */
8870 if (infoPtr->nSelectionMark >= 0)
8871 selectionMarkItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark);
8872 if (infoPtr->nFocusedItem >= 0)
8873 focusedItem = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
8875 infoPtr->pfnCompare = pfnCompare;
8876 infoPtr->lParamSort = lParamSort;
8878 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompareEx, (LPARAM)infoPtr);
8880 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
8882 /* restore selection ranges */
8883 for (i=0; i < infoPtr->nItemCount; i++)
8885 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
8886 lpItem = DPA_GetPtr(hdpaSubItems, 0);
8888 if (lpItem->state & LVIS_SELECTED)
8889 ranges_additem(infoPtr->selectionRanges, i);
8891 /* restore selection mark and focused item */
8892 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
8893 infoPtr->nFocusedItem = DPA_GetPtrIndex(infoPtr->hdpaItems, focusedItem);
8895 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
8897 /* refresh the display */
8898 if (infoPtr->uView != LV_VIEW_ICON && infoPtr->uView != LV_VIEW_SMALLICON)
8899 LISTVIEW_InvalidateList(infoPtr);
8906 * Update theme handle after a theme change.
8909 * [I] infoPtr : valid pointer to the listview structure
8913 * FAILURE : something else
8915 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr)
8917 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8918 CloseThemeData(theme);
8919 OpenThemeData(infoPtr->hwndSelf, themeClass);
8925 * Updates an items or rearranges the listview control.
8928 * [I] infoPtr : valid pointer to the listview structure
8929 * [I] nItem : item index
8935 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
8937 TRACE("(nItem=%d)\n", nItem);
8939 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
8941 /* rearrange with default alignment style */
8942 if (is_autoarrange(infoPtr))
8943 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8945 LISTVIEW_InvalidateItem(infoPtr, nItem);
8952 * Draw the track line at the place defined in the infoPtr structure.
8953 * The line is drawn with a XOR pen so drawing the line for the second time
8954 * in the same place erases the line.
8957 * [I] infoPtr : valid pointer to the listview structure
8963 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
8969 if (infoPtr->xTrackLine == -1)
8972 if (!(hdc = GetDC(infoPtr->hwndSelf)))
8974 hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
8975 oldROP = SetROP2(hdc, R2_XORPEN);
8976 MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
8977 LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
8978 SetROP2(hdc, oldROP);
8979 SelectObject(hdc, hOldPen);
8980 ReleaseDC(infoPtr->hwndSelf, hdc);
8986 * Called when an edit control should be displayed. This function is called after
8987 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
8990 * [I] hwnd : Handle to the listview
8991 * [I] uMsg : WM_TIMER (ignored)
8992 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
8993 * [I] dwTimer : The elapsed time (ignored)
8998 static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
9000 DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
9001 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9003 KillTimer(hwnd, idEvent);
9004 editItem->fEnabled = FALSE;
9005 /* check if the item is still selected */
9006 if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
9007 LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
9012 * Creates the listview control - the WM_NCCREATE phase.
9015 * [I] hwnd : window handle
9016 * [I] lpcs : the create parameters
9022 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
9024 LISTVIEW_INFO *infoPtr;
9027 TRACE("(lpcs=%p)\n", lpcs);
9029 /* initialize info pointer */
9030 infoPtr = Alloc(sizeof(LISTVIEW_INFO));
9031 if (!infoPtr) return FALSE;
9033 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
9035 infoPtr->hwndSelf = hwnd;
9036 infoPtr->dwStyle = lpcs->style; /* Note: may be changed in WM_CREATE */
9037 map_style_view(infoPtr);
9038 /* determine the type of structures to use */
9039 infoPtr->hwndNotify = lpcs->hwndParent;
9040 /* infoPtr->notifyFormat will be filled in WM_CREATE */
9042 /* initialize color information */
9043 infoPtr->clrBk = CLR_NONE;
9044 infoPtr->clrText = CLR_DEFAULT;
9045 infoPtr->clrTextBk = CLR_DEFAULT;
9046 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
9048 /* set default values */
9049 infoPtr->nFocusedItem = -1;
9050 infoPtr->nSelectionMark = -1;
9051 infoPtr->nHotItem = -1;
9052 infoPtr->bRedraw = TRUE;
9053 infoPtr->bNoItemMetrics = TRUE;
9054 infoPtr->bDoChangeNotify = TRUE;
9055 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
9056 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
9057 infoPtr->nEditLabelItem = -1;
9058 infoPtr->nLButtonDownItem = -1;
9059 infoPtr->dwHoverTime = -1; /* default system hover time */
9060 infoPtr->nMeasureItemHeight = 0;
9061 infoPtr->xTrackLine = -1; /* no track line */
9062 infoPtr->itemEdit.fEnabled = FALSE;
9063 infoPtr->iVersion = COMCTL32_VERSION;
9064 infoPtr->colRectsDirty = FALSE;
9066 /* get default font (icon title) */
9067 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
9068 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
9069 infoPtr->hFont = infoPtr->hDefaultFont;
9070 LISTVIEW_SaveTextMetrics(infoPtr);
9072 /* allocate memory for the data structure */
9073 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
9074 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
9075 if (!(infoPtr->hdpaItemIds = DPA_Create(10))) goto fail;
9076 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
9077 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
9078 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
9082 DestroyWindow(infoPtr->hwndHeader);
9083 ranges_destroy(infoPtr->selectionRanges);
9084 DPA_Destroy(infoPtr->hdpaItems);
9085 DPA_Destroy(infoPtr->hdpaItemIds);
9086 DPA_Destroy(infoPtr->hdpaPosX);
9087 DPA_Destroy(infoPtr->hdpaPosY);
9088 DPA_Destroy(infoPtr->hdpaColumns);
9095 * Creates the listview control - the WM_CREATE phase. Most of the data is
9096 * already set up in LISTVIEW_NCCreate
9099 * [I] hwnd : window handle
9100 * [I] lpcs : the create parameters
9106 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
9108 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9110 TRACE("(lpcs=%p)\n", lpcs);
9112 infoPtr->dwStyle = lpcs->style;
9113 map_style_view(infoPtr);
9115 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
9116 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
9117 /* on error defaulting to ANSI notifications */
9118 if (infoPtr->notifyFormat == 0) infoPtr->notifyFormat = NFR_ANSI;
9120 if ((infoPtr->uView == LV_VIEW_DETAILS) && (lpcs->style & WS_VISIBLE))
9122 if (LISTVIEW_CreateHeader(infoPtr) < 0) return -1;
9125 infoPtr->hwndHeader = 0;
9127 /* init item size to avoid division by 0 */
9128 LISTVIEW_UpdateItemSize (infoPtr);
9130 if (infoPtr->uView == LV_VIEW_DETAILS)
9132 if (!(LVS_NOCOLUMNHEADER & lpcs->style) && (WS_VISIBLE & lpcs->style))
9134 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9136 LISTVIEW_UpdateScroll(infoPtr);
9137 /* send WM_MEASUREITEM notification */
9138 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED) notify_measureitem(infoPtr);
9141 OpenThemeData(hwnd, themeClass);
9143 /* initialize the icon sizes */
9144 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, infoPtr->uView != LV_VIEW_ICON);
9145 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
9151 * Destroys the listview control.
9154 * [I] infoPtr : valid pointer to the listview structure
9160 static LRESULT LISTVIEW_Destroy(const LISTVIEW_INFO *infoPtr)
9162 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
9163 CloseThemeData(theme);
9169 * Enables the listview control.
9172 * [I] infoPtr : valid pointer to the listview structure
9173 * [I] bEnable : specifies whether to enable or disable the window
9179 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr, BOOL bEnable)
9181 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
9182 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
9188 * Erases the background of the listview control.
9191 * [I] infoPtr : valid pointer to the listview structure
9192 * [I] hdc : device context handle
9198 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
9202 TRACE("(hdc=%p)\n", hdc);
9204 if (!GetClipBox(hdc, &rc)) return FALSE;
9206 if (infoPtr->clrBk == CLR_NONE)
9208 if (infoPtr->dwLvExStyle & LVS_EX_TRANSPARENTBKGND)
9209 return SendMessageW(infoPtr->hwndNotify, WM_PRINTCLIENT,
9210 (WPARAM)hdc, PRF_ERASEBKGND);
9212 return SendMessageW(infoPtr->hwndNotify, WM_ERASEBKGND, (WPARAM)hdc, 0);
9215 /* for double buffered controls we need to do this during refresh */
9216 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
9218 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
9224 * Helper function for LISTVIEW_[HV]Scroll *only*.
9225 * Performs vertical/horizontal scrolling by a give amount.
9228 * [I] infoPtr : valid pointer to the listview structure
9229 * [I] dx : amount of horizontal scroll
9230 * [I] dy : amount of vertical scroll
9232 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
9234 /* now we can scroll the list */
9235 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
9236 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
9237 /* if we have focus, adjust rect */
9238 OffsetRect(&infoPtr->rcFocus, dx, dy);
9239 UpdateWindow(infoPtr->hwndSelf);
9244 * Performs vertical scrolling.
9247 * [I] infoPtr : valid pointer to the listview structure
9248 * [I] nScrollCode : scroll code
9249 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
9250 * [I] hScrollWnd : scrollbar control window handle
9256 * SB_LINEUP/SB_LINEDOWN:
9257 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
9258 * for LVS_REPORT is 1 line
9259 * for LVS_LIST cannot occur
9262 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
9263 INT nScrollDiff, HWND hScrollWnd)
9265 INT nOldScrollPos, nNewScrollPos;
9266 SCROLLINFO scrollInfo;
9269 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
9270 debugscrollcode(nScrollCode), nScrollDiff);
9272 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9274 scrollInfo.cbSize = sizeof(SCROLLINFO);
9275 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
9277 is_an_icon = ((infoPtr->uView == LV_VIEW_ICON) || (infoPtr->uView == LV_VIEW_SMALLICON));
9279 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
9281 nOldScrollPos = scrollInfo.nPos;
9282 switch (nScrollCode)
9288 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
9292 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
9296 nScrollDiff = -scrollInfo.nPage;
9300 nScrollDiff = scrollInfo.nPage;
9303 case SB_THUMBPOSITION:
9305 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
9312 /* quit right away if pos isn't changing */
9313 if (nScrollDiff == 0) return 0;
9315 /* calculate new position, and handle overflows */
9316 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
9317 if (nScrollDiff > 0) {
9318 if (nNewScrollPos < nOldScrollPos ||
9319 nNewScrollPos > scrollInfo.nMax)
9320 nNewScrollPos = scrollInfo.nMax;
9322 if (nNewScrollPos > nOldScrollPos ||
9323 nNewScrollPos < scrollInfo.nMin)
9324 nNewScrollPos = scrollInfo.nMin;
9327 /* set the new position, and reread in case it changed */
9328 scrollInfo.fMask = SIF_POS;
9329 scrollInfo.nPos = nNewScrollPos;
9330 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
9332 /* carry on only if it really changed */
9333 if (nNewScrollPos == nOldScrollPos) return 0;
9335 /* now adjust to client coordinates */
9336 nScrollDiff = nOldScrollPos - nNewScrollPos;
9337 if (infoPtr->uView == LV_VIEW_DETAILS) nScrollDiff *= infoPtr->nItemHeight;
9339 /* and scroll the window */
9340 scroll_list(infoPtr, 0, nScrollDiff);
9347 * Performs horizontal scrolling.
9350 * [I] infoPtr : valid pointer to the listview structure
9351 * [I] nScrollCode : scroll code
9352 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
9353 * [I] hScrollWnd : scrollbar control window handle
9359 * SB_LINELEFT/SB_LINERIGHT:
9360 * for LVS_ICON, LVS_SMALLICON 1 pixel
9361 * for LVS_REPORT is 1 pixel
9362 * for LVS_LIST is 1 column --> which is a 1 because the
9363 * scroll is based on columns not pixels
9366 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
9367 INT nScrollDiff, HWND hScrollWnd)
9369 INT nOldScrollPos, nNewScrollPos;
9370 SCROLLINFO scrollInfo;
9372 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
9373 debugscrollcode(nScrollCode), nScrollDiff);
9375 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9377 scrollInfo.cbSize = sizeof(SCROLLINFO);
9378 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
9380 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
9382 nOldScrollPos = scrollInfo.nPos;
9384 switch (nScrollCode)
9398 nScrollDiff = -scrollInfo.nPage;
9402 nScrollDiff = scrollInfo.nPage;
9405 case SB_THUMBPOSITION:
9407 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
9414 /* quit right away if pos isn't changing */
9415 if (nScrollDiff == 0) return 0;
9417 /* calculate new position, and handle overflows */
9418 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
9419 if (nScrollDiff > 0) {
9420 if (nNewScrollPos < nOldScrollPos ||
9421 nNewScrollPos > scrollInfo.nMax)
9422 nNewScrollPos = scrollInfo.nMax;
9424 if (nNewScrollPos > nOldScrollPos ||
9425 nNewScrollPos < scrollInfo.nMin)
9426 nNewScrollPos = scrollInfo.nMin;
9429 /* set the new position, and reread in case it changed */
9430 scrollInfo.fMask = SIF_POS;
9431 scrollInfo.nPos = nNewScrollPos;
9432 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
9434 /* carry on only if it really changed */
9435 if (nNewScrollPos == nOldScrollPos) return 0;
9437 if (infoPtr->uView == LV_VIEW_DETAILS)
9438 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
9440 /* now adjust to client coordinates */
9441 nScrollDiff = nOldScrollPos - nNewScrollPos;
9442 if (infoPtr->uView == LV_VIEW_LIST) nScrollDiff *= infoPtr->nItemWidth;
9444 /* and scroll the window */
9445 scroll_list(infoPtr, nScrollDiff, 0);
9450 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
9452 INT gcWheelDelta = 0;
9453 INT pulScrollLines = 3;
9454 SCROLLINFO scrollInfo;
9456 TRACE("(wheelDelta=%d)\n", wheelDelta);
9458 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
9459 gcWheelDelta -= wheelDelta;
9461 scrollInfo.cbSize = sizeof(SCROLLINFO);
9462 scrollInfo.fMask = SIF_POS;
9464 switch(infoPtr->uView)
9467 case LV_VIEW_SMALLICON:
9469 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
9470 * should be fixed in the future.
9472 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
9473 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
9476 case LV_VIEW_DETAILS:
9477 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
9479 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
9480 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
9481 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
9486 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
9497 * [I] infoPtr : valid pointer to the listview structure
9498 * [I] nVirtualKey : virtual key
9499 * [I] lKeyData : key data
9504 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
9506 HWND hwndSelf = infoPtr->hwndSelf;
9508 NMLVKEYDOWN nmKeyDown;
9510 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
9512 /* send LVN_KEYDOWN notification */
9513 nmKeyDown.wVKey = nVirtualKey;
9514 nmKeyDown.flags = 0;
9515 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
9516 if (!IsWindow(hwndSelf))
9519 switch (nVirtualKey)
9522 nItem = infoPtr->nFocusedItem;
9523 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
9524 toggle_checkbox_state(infoPtr, infoPtr->nFocusedItem);
9528 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
9530 if (!notify(infoPtr, NM_RETURN)) return 0;
9531 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
9536 if (infoPtr->nItemCount > 0)
9541 if (infoPtr->nItemCount > 0)
9542 nItem = infoPtr->nItemCount - 1;
9546 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TOLEFT);
9550 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_ABOVE);
9554 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_TORIGHT);
9558 nItem = LISTVIEW_GetNextItem(infoPtr, infoPtr->nFocusedItem, LVNI_BELOW);
9562 if (infoPtr->uView == LV_VIEW_DETAILS)
9564 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
9565 if (infoPtr->nFocusedItem == topidx)
9566 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
9571 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
9572 * LISTVIEW_GetCountPerRow(infoPtr);
9573 if(nItem < 0) nItem = 0;
9577 if (infoPtr->uView == LV_VIEW_DETAILS)
9579 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
9580 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
9581 if (infoPtr->nFocusedItem == topidx + cnt - 1)
9582 nItem = infoPtr->nFocusedItem + cnt - 1;
9584 nItem = topidx + cnt - 1;
9587 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
9588 * LISTVIEW_GetCountPerRow(infoPtr);
9589 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
9593 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
9594 LISTVIEW_KeySelection(infoPtr, nItem, nVirtualKey == VK_SPACE);
9604 * [I] infoPtr : valid pointer to the listview structure
9609 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
9613 /* if we did not have the focus, there's nothing to do */
9614 if (!infoPtr->bFocus) return 0;
9616 /* send NM_KILLFOCUS notification */
9617 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
9619 /* if we have a focus rectangle, get rid of it */
9620 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
9622 /* if have a marquee selection, stop it */
9623 if (infoPtr->bMarqueeSelect)
9625 /* Remove the marquee rectangle and release our mouse capture */
9626 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeRect);
9629 SetRect(&infoPtr->marqueeRect, 0, 0, 0, 0);
9631 infoPtr->bMarqueeSelect = FALSE;
9632 infoPtr->bScrolling = FALSE;
9633 KillTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr);
9636 /* set window focus flag */
9637 infoPtr->bFocus = FALSE;
9639 /* invalidate the selected items before resetting focus flag */
9640 LISTVIEW_InvalidateSelectedItems(infoPtr);
9647 * Processes double click messages (left mouse button).
9650 * [I] infoPtr : valid pointer to the listview structure
9651 * [I] wKey : key flag
9652 * [I] x,y : mouse coordinate
9657 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9659 LVHITTESTINFO htInfo;
9661 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
9663 /* Cancel the item edition if any */
9664 if (infoPtr->itemEdit.fEnabled)
9666 KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
9667 infoPtr->itemEdit.fEnabled = FALSE;
9670 /* send NM_RELEASEDCAPTURE notification */
9671 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9676 /* send NM_DBLCLK notification */
9677 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
9678 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
9680 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
9681 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
9688 * Processes mouse down messages (left mouse button).
9691 * infoPtr [I ] valid pointer to the listview structure
9692 * wKey [I ] key flag
9693 * x,y [I ] mouse coordinate
9698 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9700 LVHITTESTINFO lvHitTestInfo;
9701 static BOOL bGroupSelect = TRUE;
9702 POINT pt = { x, y };
9705 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
9707 /* send NM_RELEASEDCAPTURE notification */
9708 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9710 /* set left button down flag and record the click position */
9711 infoPtr->bLButtonDown = TRUE;
9712 infoPtr->ptClickPos = pt;
9713 infoPtr->bDragging = FALSE;
9714 infoPtr->bMarqueeSelect = FALSE;
9715 infoPtr->bScrolling = FALSE;
9717 lvHitTestInfo.pt.x = x;
9718 lvHitTestInfo.pt.y = y;
9720 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
9721 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
9722 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9724 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
9726 toggle_checkbox_state(infoPtr, nItem);
9730 if (infoPtr->dwStyle & LVS_SINGLESEL)
9732 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9733 infoPtr->nEditLabelItem = nItem;
9735 LISTVIEW_SetSelection(infoPtr, nItem);
9739 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
9743 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
9744 LISTVIEW_SetItemFocus(infoPtr, nItem);
9745 infoPtr->nSelectionMark = nItem;
9751 item.state = LVIS_SELECTED | LVIS_FOCUSED;
9752 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
9754 LISTVIEW_SetItemState(infoPtr,nItem,&item);
9755 infoPtr->nSelectionMark = nItem;
9758 else if (wKey & MK_CONTROL)
9762 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
9764 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
9765 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
9766 LISTVIEW_SetItemState(infoPtr, nItem, &item);
9767 infoPtr->nSelectionMark = nItem;
9769 else if (wKey & MK_SHIFT)
9771 LISTVIEW_SetGroupSelection(infoPtr, nItem);
9775 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9777 infoPtr->nEditLabelItem = nItem;
9778 infoPtr->nLButtonDownItem = nItem;
9780 LISTVIEW_SetItemFocus(infoPtr, nItem);
9783 /* set selection (clears other pre-existing selections) */
9784 LISTVIEW_SetSelection(infoPtr, nItem);
9788 if (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE)
9789 if(lvHitTestInfo.iItem != -1) notify_itemactivate(infoPtr,&lvHitTestInfo);
9793 if (!infoPtr->bFocus)
9794 SetFocus(infoPtr->hwndSelf);
9796 /* remove all selections */
9797 if (!(wKey & MK_CONTROL) && !(wKey & MK_SHIFT))
9798 LISTVIEW_DeselectAll(infoPtr);
9807 * Processes mouse up messages (left mouse button).
9810 * infoPtr [I ] valid pointer to the listview structure
9811 * wKey [I ] key flag
9812 * x,y [I ] mouse coordinate
9817 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9819 LVHITTESTINFO lvHitTestInfo;
9821 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
9823 if (!infoPtr->bLButtonDown) return 0;
9825 lvHitTestInfo.pt.x = x;
9826 lvHitTestInfo.pt.y = y;
9828 /* send NM_CLICK notification */
9829 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9830 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
9832 /* set left button flag */
9833 infoPtr->bLButtonDown = FALSE;
9835 /* set a single selection, reset others */
9836 if(lvHitTestInfo.iItem == infoPtr->nLButtonDownItem && lvHitTestInfo.iItem != -1)
9837 LISTVIEW_SetSelection(infoPtr, infoPtr->nLButtonDownItem);
9838 infoPtr->nLButtonDownItem = -1;
9840 if (infoPtr->bDragging || infoPtr->bMarqueeSelect)
9842 /* Remove the marquee rectangle and release our mouse capture */
9843 if (infoPtr->bMarqueeSelect)
9845 LISTVIEW_InvalidateRect(infoPtr, &infoPtr->marqueeDrawRect);
9849 SetRect(&infoPtr->marqueeRect, 0, 0, 0, 0);
9850 SetRect(&infoPtr->marqueeDrawRect, 0, 0, 0, 0);
9852 infoPtr->bDragging = FALSE;
9853 infoPtr->bMarqueeSelect = FALSE;
9854 infoPtr->bScrolling = FALSE;
9856 KillTimer(infoPtr->hwndSelf, (UINT_PTR) infoPtr);
9860 /* if we clicked on a selected item, edit the label */
9861 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
9863 /* we want to make sure the user doesn't want to do a double click. So we will
9864 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
9866 infoPtr->itemEdit.fEnabled = TRUE;
9867 infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
9868 SetTimer(infoPtr->hwndSelf,
9869 (UINT_PTR)&infoPtr->itemEdit,
9870 GetDoubleClickTime(),
9871 LISTVIEW_DelayedEditItem);
9874 if (!infoPtr->bFocus)
9875 SetFocus(infoPtr->hwndSelf);
9882 * Destroys the listview control (called after WM_DESTROY).
9885 * [I] infoPtr : valid pointer to the listview structure
9890 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
9896 /* delete all items */
9897 LISTVIEW_DeleteAllItems(infoPtr, TRUE);
9899 /* destroy data structure */
9900 DPA_Destroy(infoPtr->hdpaItems);
9901 DPA_Destroy(infoPtr->hdpaItemIds);
9902 DPA_Destroy(infoPtr->hdpaPosX);
9903 DPA_Destroy(infoPtr->hdpaPosY);
9905 for (i = 0; i < DPA_GetPtrCount(infoPtr->hdpaColumns); i++)
9906 Free(DPA_GetPtr(infoPtr->hdpaColumns, i));
9907 DPA_Destroy(infoPtr->hdpaColumns);
9908 ranges_destroy(infoPtr->selectionRanges);
9910 /* destroy image lists */
9911 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
9913 ImageList_Destroy(infoPtr->himlNormal);
9914 ImageList_Destroy(infoPtr->himlSmall);
9915 ImageList_Destroy(infoPtr->himlState);
9918 /* destroy font, bkgnd brush */
9920 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
9921 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
9923 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
9925 /* free listview info pointer*/
9933 * Handles notifications from header.
9936 * [I] infoPtr : valid pointer to the listview structure
9937 * [I] nCtrlId : control identifier
9938 * [I] lpnmh : notification information
9943 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
9945 HWND hwndSelf = infoPtr->hwndSelf;
9947 TRACE("(lpnmh=%p)\n", lpnmh);
9949 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
9951 switch (lpnmh->hdr.code)
9956 COLUMN_INFO *lpColumnInfo;
9960 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
9963 /* remove the old line (if any) */
9964 LISTVIEW_DrawTrackLine(infoPtr);
9966 /* compute & draw the new line */
9967 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
9968 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
9969 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
9970 infoPtr->xTrackLine = x + ptOrigin.x;
9971 LISTVIEW_DrawTrackLine(infoPtr);
9977 /* remove the track line (if any) */
9978 LISTVIEW_DrawTrackLine(infoPtr);
9979 infoPtr->xTrackLine = -1;
9983 notify_forward_header(infoPtr, lpnmh);
9984 return (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP) == 0;
9987 infoPtr->colRectsDirty = TRUE;
9988 LISTVIEW_InvalidateList(infoPtr);
9989 notify_forward_header(infoPtr, lpnmh);
9992 case HDN_ITEMCHANGINGW:
9993 case HDN_ITEMCHANGINGA:
9994 return notify_forward_header(infoPtr, lpnmh);
9996 case HDN_ITEMCHANGEDW:
9997 case HDN_ITEMCHANGEDA:
9999 COLUMN_INFO *lpColumnInfo;
10003 notify_forward_header(infoPtr, lpnmh);
10004 if (!IsWindow(hwndSelf))
10007 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
10009 hdi.mask = HDI_WIDTH;
10010 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, lpnmh->iItem, (LPARAM)&hdi)) return 0;
10014 cxy = lpnmh->pitem->cxy;
10016 /* determine how much we change since the last know position */
10017 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
10018 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
10021 lpColumnInfo->rcHeader.right += dx;
10023 hdi.mask = HDI_ORDER;
10024 SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, lpnmh->iItem, (LPARAM)&hdi);
10026 /* not the rightmost one */
10027 if (hdi.iOrder + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
10029 INT nIndex = SendMessageW(infoPtr->hwndHeader, HDM_ORDERTOINDEX,
10030 hdi.iOrder + 1, 0);
10031 LISTVIEW_ScrollColumns(infoPtr, nIndex, dx);
10035 /* only needs to update the scrolls */
10036 infoPtr->nItemWidth += dx;
10037 LISTVIEW_UpdateScroll(infoPtr);
10039 LISTVIEW_UpdateItemSize(infoPtr);
10040 if (infoPtr->uView == LV_VIEW_DETAILS && is_redrawing(infoPtr))
10043 RECT rcCol = lpColumnInfo->rcHeader;
10045 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
10046 OffsetRect(&rcCol, ptOrigin.x, 0);
10048 rcCol.top = infoPtr->rcList.top;
10049 rcCol.bottom = infoPtr->rcList.bottom;
10051 /* resizing left-aligned columns leaves most of the left side untouched */
10052 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
10054 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
10057 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
10060 /* when shrinking the last column clear the now unused field */
10061 if (hdi.iOrder == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
10067 /* deal with right from rightmost column area */
10068 right.left = rcCol.right;
10069 right.top = rcCol.top;
10070 right.bottom = rcCol.bottom;
10071 right.right = infoPtr->rcList.right;
10073 LISTVIEW_InvalidateRect(infoPtr, &right);
10076 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
10082 case HDN_ITEMCLICKW:
10083 case HDN_ITEMCLICKA:
10085 /* Handle sorting by Header Column */
10088 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
10090 nmlv.iSubItem = lpnmh->iItem;
10091 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
10092 notify_forward_header(infoPtr, lpnmh);
10096 case HDN_DIVIDERDBLCLICKW:
10097 case HDN_DIVIDERDBLCLICKA:
10098 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
10099 notify_forward_header(infoPtr, lpnmh);
10108 * Paint non-client area of control.
10111 * [I] infoPtr : valid pointer to the listview structureof the sender
10112 * [I] region : update region
10115 * TRUE - frame was painted
10116 * FALSE - call default window proc
10118 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
10120 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
10124 int cxEdge = GetSystemMetrics (SM_CXEDGE),
10125 cyEdge = GetSystemMetrics (SM_CYEDGE);
10128 return DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)region, 0);
10130 GetWindowRect(infoPtr->hwndSelf, &r);
10132 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
10133 r.right - cxEdge, r.bottom - cyEdge);
10134 if (region != (HRGN)1)
10135 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
10136 OffsetRect(&r, -r.left, -r.top);
10138 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
10139 OffsetRect(&r, -r.left, -r.top);
10141 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
10142 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
10143 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
10144 ReleaseDC(infoPtr->hwndSelf, dc);
10146 /* Call default proc to get the scrollbars etc. painted */
10147 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
10154 * Determines the type of structure to use.
10157 * [I] infoPtr : valid pointer to the listview structureof the sender
10158 * [I] hwndFrom : listview window handle
10159 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
10164 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
10166 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
10168 if (nCommand == NF_REQUERY)
10169 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
10171 return infoPtr->notifyFormat;
10176 * Paints/Repaints the listview control. Internal use.
10179 * [I] infoPtr : valid pointer to the listview structure
10180 * [I] hdc : device context handle
10185 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
10187 TRACE("(hdc=%p)\n", hdc);
10189 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
10191 infoPtr->bNoItemMetrics = FALSE;
10192 LISTVIEW_UpdateItemSize(infoPtr);
10193 if (infoPtr->uView == LV_VIEW_ICON || infoPtr->uView == LV_VIEW_SMALLICON)
10194 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
10195 LISTVIEW_UpdateScroll(infoPtr);
10198 if (infoPtr->hwndHeader) UpdateWindow(infoPtr->hwndHeader);
10201 LISTVIEW_Refresh(infoPtr, hdc, NULL);
10206 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
10207 if (!hdc) return 1;
10208 LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
10209 EndPaint(infoPtr->hwndSelf, &ps);
10217 * Paints/Repaints the listview control, WM_PAINT handler.
10220 * [I] infoPtr : valid pointer to the listview structure
10221 * [I] hdc : device context handle
10226 static inline LRESULT LISTVIEW_WMPaint(LISTVIEW_INFO *infoPtr, HDC hdc)
10228 TRACE("(hdc=%p)\n", hdc);
10230 if (!is_redrawing(infoPtr))
10231 return DefWindowProcW (infoPtr->hwndSelf, WM_PAINT, (WPARAM)hdc, 0);
10233 return LISTVIEW_Paint(infoPtr, hdc);
10238 * Paints/Repaints the listview control.
10241 * [I] infoPtr : valid pointer to the listview structure
10242 * [I] hdc : device context handle
10243 * [I] options : drawing options
10248 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
10250 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
10252 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
10255 if (options & PRF_ERASEBKGND)
10256 LISTVIEW_EraseBkgnd(infoPtr, hdc);
10258 if (options & PRF_CLIENT)
10259 LISTVIEW_Paint(infoPtr, hdc);
10267 * Processes double click messages (right mouse button).
10270 * [I] infoPtr : valid pointer to the listview structure
10271 * [I] wKey : key flag
10272 * [I] x,y : mouse coordinate
10277 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10279 LVHITTESTINFO lvHitTestInfo;
10281 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
10283 /* send NM_RELEASEDCAPTURE notification */
10284 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
10286 /* send NM_RDBLCLK notification */
10287 lvHitTestInfo.pt.x = x;
10288 lvHitTestInfo.pt.y = y;
10289 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
10290 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
10297 * Processes mouse down messages (right mouse button).
10300 * [I] infoPtr : valid pointer to the listview structure
10301 * [I] wKey : key flag
10302 * [I] x,y : mouse coordinate
10307 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10309 LVHITTESTINFO lvHitTestInfo;
10312 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
10314 /* send NM_RELEASEDCAPTURE notification */
10315 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
10317 /* make sure the listview control window has the focus */
10318 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
10320 /* set right button down flag */
10321 infoPtr->bRButtonDown = TRUE;
10323 /* determine the index of the selected item */
10324 lvHitTestInfo.pt.x = x;
10325 lvHitTestInfo.pt.y = y;
10326 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
10328 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
10330 LISTVIEW_SetItemFocus(infoPtr, nItem);
10331 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
10332 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
10333 LISTVIEW_SetSelection(infoPtr, nItem);
10337 LISTVIEW_DeselectAll(infoPtr);
10345 * Processes mouse up messages (right mouse button).
10348 * [I] infoPtr : valid pointer to the listview structure
10349 * [I] wKey : key flag
10350 * [I] x,y : mouse coordinate
10355 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
10357 LVHITTESTINFO lvHitTestInfo;
10360 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
10362 if (!infoPtr->bRButtonDown) return 0;
10364 /* set button flag */
10365 infoPtr->bRButtonDown = FALSE;
10367 /* Send NM_RCLICK notification */
10368 lvHitTestInfo.pt.x = x;
10369 lvHitTestInfo.pt.y = y;
10370 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
10371 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
10373 /* Change to screen coordinate for WM_CONTEXTMENU */
10374 pt = lvHitTestInfo.pt;
10375 ClientToScreen(infoPtr->hwndSelf, &pt);
10377 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
10378 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
10379 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
10390 * [I] infoPtr : valid pointer to the listview structure
10391 * [I] hwnd : window handle of window containing the cursor
10392 * [I] nHittest : hit-test code
10393 * [I] wMouseMsg : ideintifier of the mouse message
10396 * TRUE if cursor is set
10399 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
10401 LVHITTESTINFO lvHitTestInfo;
10403 if (!(LISTVIEW_isHotTracking(infoPtr))) goto forward;
10405 if (!infoPtr->hHotCursor) goto forward;
10407 GetCursorPos(&lvHitTestInfo.pt);
10408 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) goto forward;
10410 SetCursor(infoPtr->hHotCursor);
10416 return DefWindowProcW(infoPtr->hwndSelf, WM_SETCURSOR, wParam, lParam);
10424 * [I] infoPtr : valid pointer to the listview structure
10425 * [I] hwndLoseFocus : handle of previously focused window
10430 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
10432 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
10434 /* if we have the focus already, there's nothing to do */
10435 if (infoPtr->bFocus) return 0;
10437 /* send NM_SETFOCUS notification */
10438 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
10440 /* set window focus flag */
10441 infoPtr->bFocus = TRUE;
10443 /* put the focus rect back on */
10444 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
10446 /* redraw all visible selected items */
10447 LISTVIEW_InvalidateSelectedItems(infoPtr);
10457 * [I] infoPtr : valid pointer to the listview structure
10458 * [I] fRedraw : font handle
10459 * [I] fRedraw : redraw flag
10464 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
10466 HFONT oldFont = infoPtr->hFont;
10468 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
10470 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
10471 if (infoPtr->hFont == oldFont) return 0;
10473 LISTVIEW_SaveTextMetrics(infoPtr);
10475 if (infoPtr->uView == LV_VIEW_DETAILS)
10477 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
10478 LISTVIEW_UpdateSize(infoPtr);
10479 LISTVIEW_UpdateScroll(infoPtr);
10482 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
10489 * Message handling for WM_SETREDRAW.
10490 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
10493 * [I] infoPtr : valid pointer to the listview structure
10494 * [I] bRedraw: state of redraw flag
10497 * DefWinProc return value
10499 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
10501 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
10503 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
10504 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
10506 infoPtr->bRedraw = bRedraw;
10508 if(!bRedraw) return 0;
10510 if (is_autoarrange(infoPtr))
10511 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
10512 LISTVIEW_UpdateScroll(infoPtr);
10514 /* despite what the WM_SETREDRAW docs says, apps expect us
10515 * to invalidate the listview here... stupid! */
10516 LISTVIEW_InvalidateList(infoPtr);
10523 * Resizes the listview control. This function processes WM_SIZE
10524 * messages. At this time, the width and height are not used.
10527 * [I] infoPtr : valid pointer to the listview structure
10528 * [I] Width : new width
10529 * [I] Height : new height
10534 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
10536 RECT rcOld = infoPtr->rcList;
10538 TRACE("(width=%d, height=%d)\n", Width, Height);
10540 LISTVIEW_UpdateSize(infoPtr);
10541 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
10543 /* do not bother with display related stuff if we're not redrawing */
10544 if (!is_redrawing(infoPtr)) return 0;
10546 if (is_autoarrange(infoPtr))
10547 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
10549 LISTVIEW_UpdateScroll(infoPtr);
10551 /* refresh all only for lists whose height changed significantly */
10552 if ((infoPtr->uView == LV_VIEW_LIST) &&
10553 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
10554 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
10555 LISTVIEW_InvalidateList(infoPtr);
10562 * Sets the size information.
10565 * [I] infoPtr : valid pointer to the listview structure
10570 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
10572 TRACE("uView=%d, rcList(old)=%s\n", infoPtr->uView, wine_dbgstr_rect(&infoPtr->rcList));
10574 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
10576 if (infoPtr->uView == LV_VIEW_LIST)
10578 /* Apparently the "LIST" style is supposed to have the same
10579 * number of items in a column even if there is no scroll bar.
10580 * Since if a scroll bar already exists then the bottom is already
10581 * reduced, only reduce if the scroll bar does not currently exist.
10582 * The "2" is there to mimic the native control. I think it may be
10583 * related to either padding or edges. (GLA 7/2002)
10585 if (!(infoPtr->dwStyle & WS_HSCROLL))
10586 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
10587 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
10589 else if (infoPtr->uView == LV_VIEW_DETAILS)
10591 /* if control created invisible header isn't created */
10592 if (infoPtr->hwndHeader)
10597 hl.prc = &infoPtr->rcList;
10599 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
10600 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp.flags, wp.x, wp.y, wp.cx, wp.cy);
10601 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy,
10602 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
10603 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
10604 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy);
10606 infoPtr->rcList.top = max(wp.cy, 0);
10608 infoPtr->rcList.top += (infoPtr->dwLvExStyle & LVS_EX_GRIDLINES) ? 2 : 0;
10611 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
10616 * Processes WM_STYLECHANGED messages.
10619 * [I] infoPtr : valid pointer to the listview structure
10620 * [I] wStyleType : window style type (normal or extended)
10621 * [I] lpss : window style information
10626 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
10627 const STYLESTRUCT *lpss)
10629 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
10630 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
10633 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
10634 wStyleType, lpss->styleOld, lpss->styleNew);
10636 if (wStyleType != GWL_STYLE) return 0;
10638 infoPtr->dwStyle = lpss->styleNew;
10639 map_style_view(infoPtr);
10641 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
10642 ((lpss->styleNew & WS_HSCROLL) == 0))
10643 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
10645 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
10646 ((lpss->styleNew & WS_VSCROLL) == 0))
10647 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
10649 if (uNewView != uOldView)
10651 SIZE oldIconSize = infoPtr->iconSize;
10654 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
10655 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
10657 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
10658 SetRectEmpty(&infoPtr->rcFocus);
10660 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
10661 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
10663 if (uNewView == LVS_ICON)
10665 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
10667 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
10668 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
10669 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
10672 else if (uNewView == LVS_REPORT)
10677 LISTVIEW_CreateHeader( infoPtr );
10679 hl.prc = &infoPtr->rcList;
10681 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
10682 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
10683 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
10684 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
10687 LISTVIEW_UpdateItemSize(infoPtr);
10690 if (uNewView == LVS_REPORT)
10692 if ((lpss->styleOld ^ lpss->styleNew) & LVS_NOCOLUMNHEADER)
10694 if (lpss->styleNew & LVS_NOCOLUMNHEADER)
10696 /* Turn off the header control */
10697 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
10698 TRACE("Hide header control, was 0x%08x\n", style);
10699 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style | HDS_HIDDEN);
10701 /* Turn on the header control */
10702 if ((style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE)) & HDS_HIDDEN)
10704 TRACE("Show header control, was 0x%08x\n", style);
10705 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, (style & ~HDS_HIDDEN) | WS_VISIBLE);
10711 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
10712 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
10713 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
10715 /* update the size of the client area */
10716 LISTVIEW_UpdateSize(infoPtr);
10718 /* add scrollbars if needed */
10719 LISTVIEW_UpdateScroll(infoPtr);
10721 /* invalidate client area + erase background */
10722 LISTVIEW_InvalidateList(infoPtr);
10729 * Processes WM_STYLECHANGING messages.
10732 * [I] infoPtr : valid pointer to the listview structure
10733 * [I] wStyleType : window style type (normal or extended)
10734 * [I0] lpss : window style information
10739 static INT LISTVIEW_StyleChanging(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
10742 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
10743 wStyleType, lpss->styleOld, lpss->styleNew);
10745 /* don't forward LVS_OWNERDATA only if not already set to */
10746 if ((lpss->styleNew ^ lpss->styleOld) & LVS_OWNERDATA)
10748 if (lpss->styleOld & LVS_OWNERDATA)
10749 lpss->styleNew |= LVS_OWNERDATA;
10751 lpss->styleNew &= ~LVS_OWNERDATA;
10759 * Processes WM_SHOWWINDOW messages.
10762 * [I] infoPtr : valid pointer to the listview structure
10763 * [I] bShown : window is being shown (FALSE when hidden)
10764 * [I] iStatus : window show status
10769 static LRESULT LISTVIEW_ShowWindow(LISTVIEW_INFO *infoPtr, WPARAM bShown, LPARAM iStatus)
10771 /* header delayed creation */
10772 if ((infoPtr->uView == LV_VIEW_DETAILS) && bShown)
10774 LISTVIEW_CreateHeader(infoPtr);
10776 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle))
10777 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
10780 return DefWindowProcW(infoPtr->hwndSelf, WM_SHOWWINDOW, bShown, iStatus);
10785 * Processes CCM_GETVERSION messages.
10788 * [I] infoPtr : valid pointer to the listview structure
10793 static inline LRESULT LISTVIEW_GetVersion(const LISTVIEW_INFO *infoPtr)
10795 return infoPtr->iVersion;
10800 * Processes CCM_SETVERSION messages.
10803 * [I] infoPtr : valid pointer to the listview structure
10804 * [I] iVersion : version to be set
10807 * -1 when requested version is greater than DLL version;
10808 * previous version otherwise
10810 static LRESULT LISTVIEW_SetVersion(LISTVIEW_INFO *infoPtr, DWORD iVersion)
10812 INT iOldVersion = infoPtr->iVersion;
10814 if (iVersion > COMCTL32_VERSION)
10817 infoPtr->iVersion = iVersion;
10819 TRACE("new version %d\n", iVersion);
10821 return iOldVersion;
10826 * Window procedure of the listview control.
10829 static LRESULT WINAPI
10830 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10832 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
10834 TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg, wParam, lParam);
10836 if (!infoPtr && (uMsg != WM_NCCREATE))
10837 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10841 case LVM_APPROXIMATEVIEWRECT:
10842 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
10843 LOWORD(lParam), HIWORD(lParam));
10845 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
10847 case LVM_CANCELEDITLABEL:
10848 return LISTVIEW_CancelEditLabel(infoPtr);
10850 case LVM_CREATEDRAGIMAGE:
10851 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
10853 case LVM_DELETEALLITEMS:
10854 return LISTVIEW_DeleteAllItems(infoPtr, FALSE);
10856 case LVM_DELETECOLUMN:
10857 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
10859 case LVM_DELETEITEM:
10860 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
10862 case LVM_EDITLABELA:
10863 case LVM_EDITLABELW:
10864 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam,
10865 uMsg == LVM_EDITLABELW);
10866 /* case LVM_ENABLEGROUPVIEW: */
10868 case LVM_ENSUREVISIBLE:
10869 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
10871 case LVM_FINDITEMW:
10872 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
10874 case LVM_FINDITEMA:
10875 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
10877 case LVM_GETBKCOLOR:
10878 return infoPtr->clrBk;
10880 /* case LVM_GETBKIMAGE: */
10882 case LVM_GETCALLBACKMASK:
10883 return infoPtr->uCallbackMask;
10885 case LVM_GETCOLUMNA:
10886 case LVM_GETCOLUMNW:
10887 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam,
10888 uMsg == LVM_GETCOLUMNW);
10890 case LVM_GETCOLUMNORDERARRAY:
10891 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
10893 case LVM_GETCOLUMNWIDTH:
10894 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
10896 case LVM_GETCOUNTPERPAGE:
10897 return LISTVIEW_GetCountPerPage(infoPtr);
10899 case LVM_GETEDITCONTROL:
10900 return (LRESULT)infoPtr->hwndEdit;
10902 case LVM_GETEXTENDEDLISTVIEWSTYLE:
10903 return infoPtr->dwLvExStyle;
10905 /* case LVM_GETGROUPINFO: */
10907 /* case LVM_GETGROUPMETRICS: */
10909 case LVM_GETHEADER:
10910 return (LRESULT)infoPtr->hwndHeader;
10912 case LVM_GETHOTCURSOR:
10913 return (LRESULT)infoPtr->hHotCursor;
10915 case LVM_GETHOTITEM:
10916 return infoPtr->nHotItem;
10918 case LVM_GETHOVERTIME:
10919 return infoPtr->dwHoverTime;
10921 case LVM_GETIMAGELIST:
10922 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
10924 /* case LVM_GETINSERTMARK: */
10926 /* case LVM_GETINSERTMARKCOLOR: */
10928 /* case LVM_GETINSERTMARKRECT: */
10930 case LVM_GETISEARCHSTRINGA:
10931 case LVM_GETISEARCHSTRINGW:
10932 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
10937 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, uMsg == LVM_GETITEMW);
10939 case LVM_GETITEMCOUNT:
10940 return infoPtr->nItemCount;
10942 case LVM_GETITEMPOSITION:
10943 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
10945 case LVM_GETITEMRECT:
10946 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
10948 case LVM_GETITEMSPACING:
10949 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
10951 case LVM_GETITEMSTATE:
10952 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
10954 case LVM_GETITEMTEXTA:
10955 case LVM_GETITEMTEXTW:
10956 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam,
10957 uMsg == LVM_GETITEMTEXTW);
10959 case LVM_GETNEXTITEM:
10960 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
10962 case LVM_GETNUMBEROFWORKAREAS:
10963 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
10966 case LVM_GETORIGIN:
10967 if (!lParam) return FALSE;
10968 if (infoPtr->uView == LV_VIEW_DETAILS ||
10969 infoPtr->uView == LV_VIEW_LIST) return FALSE;
10970 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
10973 /* case LVM_GETOUTLINECOLOR: */
10975 /* case LVM_GETSELECTEDCOLUMN: */
10977 case LVM_GETSELECTEDCOUNT:
10978 return LISTVIEW_GetSelectedCount(infoPtr);
10980 case LVM_GETSELECTIONMARK:
10981 return infoPtr->nSelectionMark;
10983 case LVM_GETSTRINGWIDTHA:
10984 case LVM_GETSTRINGWIDTHW:
10985 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam,
10986 uMsg == LVM_GETSTRINGWIDTHW);
10988 case LVM_GETSUBITEMRECT:
10989 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
10991 case LVM_GETTEXTBKCOLOR:
10992 return infoPtr->clrTextBk;
10994 case LVM_GETTEXTCOLOR:
10995 return infoPtr->clrText;
10997 /* case LVM_GETTILEINFO: */
10999 /* case LVM_GETTILEVIEWINFO: */
11001 case LVM_GETTOOLTIPS:
11002 if( !infoPtr->hwndToolTip )
11003 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
11004 return (LRESULT)infoPtr->hwndToolTip;
11006 case LVM_GETTOPINDEX:
11007 return LISTVIEW_GetTopIndex(infoPtr);
11009 case LVM_GETUNICODEFORMAT:
11010 return (infoPtr->notifyFormat == NFR_UNICODE);
11013 return infoPtr->uView;
11015 case LVM_GETVIEWRECT:
11016 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
11018 case LVM_GETWORKAREAS:
11019 FIXME("LVM_GETWORKAREAS: unimplemented\n");
11022 /* case LVM_HASGROUP: */
11025 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, TRUE);
11027 case LVM_INSERTCOLUMNA:
11028 case LVM_INSERTCOLUMNW:
11029 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam,
11030 uMsg == LVM_INSERTCOLUMNW);
11032 /* case LVM_INSERTGROUP: */
11034 /* case LVM_INSERTGROUPSORTED: */
11036 case LVM_INSERTITEMA:
11037 case LVM_INSERTITEMW:
11038 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, uMsg == LVM_INSERTITEMW);
11040 /* case LVM_INSERTMARKHITTEST: */
11042 /* case LVM_ISGROUPVIEWENABLED: */
11044 case LVM_ISITEMVISIBLE:
11045 return LISTVIEW_IsItemVisible(infoPtr, (INT)wParam);
11047 case LVM_MAPIDTOINDEX:
11048 return LISTVIEW_MapIdToIndex(infoPtr, (UINT)wParam);
11050 case LVM_MAPINDEXTOID:
11051 return LISTVIEW_MapIndexToId(infoPtr, (INT)wParam);
11053 /* case LVM_MOVEGROUP: */
11055 /* case LVM_MOVEITEMTOGROUP: */
11057 case LVM_REDRAWITEMS:
11058 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
11060 /* case LVM_REMOVEALLGROUPS: */
11062 /* case LVM_REMOVEGROUP: */
11065 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
11067 case LVM_SETBKCOLOR:
11068 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
11070 /* case LVM_SETBKIMAGE: */
11072 case LVM_SETCALLBACKMASK:
11073 infoPtr->uCallbackMask = (UINT)wParam;
11076 case LVM_SETCOLUMNA:
11077 case LVM_SETCOLUMNW:
11078 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam,
11079 uMsg == LVM_SETCOLUMNW);
11081 case LVM_SETCOLUMNORDERARRAY:
11082 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
11084 case LVM_SETCOLUMNWIDTH:
11085 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
11087 case LVM_SETEXTENDEDLISTVIEWSTYLE:
11088 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
11090 /* case LVM_SETGROUPINFO: */
11092 /* case LVM_SETGROUPMETRICS: */
11094 case LVM_SETHOTCURSOR:
11095 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
11097 case LVM_SETHOTITEM:
11098 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
11100 case LVM_SETHOVERTIME:
11101 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
11103 case LVM_SETICONSPACING:
11104 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
11106 case LVM_SETIMAGELIST:
11107 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
11109 /* case LVM_SETINFOTIP: */
11111 /* case LVM_SETINSERTMARK: */
11113 /* case LVM_SETINSERTMARKCOLOR: */
11118 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
11119 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, (uMsg == LVM_SETITEMW));
11122 case LVM_SETITEMCOUNT:
11123 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
11125 case LVM_SETITEMPOSITION:
11128 pt.x = (short)LOWORD(lParam);
11129 pt.y = (short)HIWORD(lParam);
11130 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, &pt);
11133 case LVM_SETITEMPOSITION32:
11134 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, (POINT*)lParam);
11136 case LVM_SETITEMSTATE:
11137 if (lParam == 0) return FALSE;
11138 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
11140 case LVM_SETITEMTEXTA:
11141 case LVM_SETITEMTEXTW:
11142 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam,
11143 uMsg == LVM_SETITEMTEXTW);
11145 /* case LVM_SETOUTLINECOLOR: */
11147 /* case LVM_SETSELECTEDCOLUMN: */
11149 case LVM_SETSELECTIONMARK:
11150 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
11152 case LVM_SETTEXTBKCOLOR:
11153 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
11155 case LVM_SETTEXTCOLOR:
11156 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
11158 /* case LVM_SETTILEINFO: */
11160 /* case LVM_SETTILEVIEWINFO: */
11162 /* case LVM_SETTILEWIDTH: */
11164 case LVM_SETTOOLTIPS:
11165 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
11167 case LVM_SETUNICODEFORMAT:
11168 return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
11171 return LISTVIEW_SetView(infoPtr, wParam);
11173 /* case LVM_SETWORKAREAS: */
11175 /* case LVM_SORTGROUPS: */
11177 case LVM_SORTITEMS:
11178 case LVM_SORTITEMSEX:
11179 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam,
11180 uMsg == LVM_SORTITEMSEX);
11181 case LVM_SUBITEMHITTEST:
11182 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
11185 return LISTVIEW_Update(infoPtr, (INT)wParam);
11187 case CCM_GETVERSION:
11188 return LISTVIEW_GetVersion(infoPtr);
11190 case CCM_SETVERSION:
11191 return LISTVIEW_SetVersion(infoPtr, wParam);
11194 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
11197 return LISTVIEW_Command(infoPtr, wParam, lParam);
11200 return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
11203 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
11206 return LISTVIEW_Destroy(infoPtr);
11209 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
11211 case WM_ERASEBKGND:
11212 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
11214 case WM_GETDLGCODE:
11215 return DLGC_WANTCHARS | DLGC_WANTARROWS;
11218 return (LRESULT)infoPtr->hFont;
11221 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
11224 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
11227 return LISTVIEW_KillFocus(infoPtr);
11229 case WM_LBUTTONDBLCLK:
11230 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11232 case WM_LBUTTONDOWN:
11233 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11236 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11239 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11241 case WM_MOUSEHOVER:
11242 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11245 return LISTVIEW_NCDestroy(infoPtr);
11248 return LISTVIEW_NCPaint(infoPtr, (HRGN)wParam);
11251 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
11252 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
11255 case WM_NOTIFYFORMAT:
11256 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
11258 case WM_PRINTCLIENT:
11259 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
11262 return LISTVIEW_WMPaint(infoPtr, (HDC)wParam);
11264 case WM_RBUTTONDBLCLK:
11265 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11267 case WM_RBUTTONDOWN:
11268 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11271 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
11274 return LISTVIEW_SetCursor(infoPtr, wParam, lParam);
11277 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
11280 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
11283 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
11285 case WM_SHOWWINDOW:
11286 return LISTVIEW_ShowWindow(infoPtr, wParam, lParam);
11289 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
11291 case WM_STYLECHANGED:
11292 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
11294 case WM_STYLECHANGING:
11295 return LISTVIEW_StyleChanging(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
11297 case WM_SYSCOLORCHANGE:
11298 COMCTL32_RefreshSysColors();
11301 /* case WM_TIMER: */
11302 case WM_THEMECHANGED:
11303 return LISTVIEW_ThemeChanged(infoPtr);
11306 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
11308 case WM_MOUSEWHEEL:
11309 if (wParam & (MK_SHIFT | MK_CONTROL))
11310 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
11311 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
11313 case WM_WINDOWPOSCHANGED:
11314 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
11316 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
11317 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
11319 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (infoPtr->uView == LV_VIEW_DETAILS))
11321 if (notify_measureitem(infoPtr)) LISTVIEW_InvalidateList(infoPtr);
11324 LISTVIEW_UpdateSize(infoPtr);
11325 LISTVIEW_UpdateScroll(infoPtr);
11327 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
11329 /* case WM_WININICHANGE: */
11332 if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
11333 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
11335 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
11342 * Registers the window class.
11350 void LISTVIEW_Register(void)
11352 WNDCLASSW wndClass;
11354 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
11355 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
11356 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
11357 wndClass.cbClsExtra = 0;
11358 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
11359 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
11360 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
11361 wndClass.lpszClassName = WC_LISTVIEWW;
11362 RegisterClassW(&wndClass);
11367 * Unregisters the window class.
11375 void LISTVIEW_Unregister(void)
11377 UnregisterClassW(WC_LISTVIEWW, NULL);
11382 * Handle any WM_COMMAND messages
11385 * [I] infoPtr : valid pointer to the listview structure
11386 * [I] wParam : the first message parameter
11387 * [I] lParam : the second message parameter
11392 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
11394 switch (HIWORD(wParam))
11399 * Adjust the edit window size
11401 WCHAR buffer[1024];
11402 HDC hdc = GetDC(infoPtr->hwndEdit);
11403 HFONT hFont, hOldFont = 0;
11407 if (!infoPtr->hwndEdit || !hdc) return 0;
11408 GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
11409 GetWindowRect(infoPtr->hwndEdit, &rect);
11411 /* Select font to get the right dimension of the string */
11412 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
11415 hOldFont = SelectObject(hdc, hFont);
11418 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
11420 TEXTMETRICW textMetric;
11422 /* Add Extra spacing for the next character */
11423 GetTextMetricsW(hdc, &textMetric);
11424 sz.cx += (textMetric.tmMaxCharWidth * 2);
11432 rect.bottom - rect.top,
11433 SWP_DRAWFRAME|SWP_NOMOVE);
11436 SelectObject(hdc, hOldFont);
11438 ReleaseDC(infoPtr->hwndEdit, hdc);
11444 LISTVIEW_CancelEditLabel(infoPtr);
11449 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
11458 * Subclassed edit control windproc function
11461 * [I] hwnd : the edit window handle
11462 * [I] uMsg : the message that is to be processed
11463 * [I] wParam : first message parameter
11464 * [I] lParam : second message parameter
11465 * [I] isW : TRUE if input is Unicode
11470 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
11472 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
11475 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
11476 hwnd, uMsg, wParam, lParam, isW);
11480 case WM_GETDLGCODE:
11481 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
11485 WNDPROC editProc = infoPtr->EditWndProc;
11486 infoPtr->EditWndProc = 0;
11487 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
11488 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
11492 if (VK_ESCAPE == (INT)wParam)
11497 else if (VK_RETURN == (INT)wParam)
11501 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
11504 /* kill the edit */
11505 if (infoPtr->hwndEdit)
11506 LISTVIEW_EndEditLabelT(infoPtr, save, isW);
11508 SendMessageW(hwnd, WM_CLOSE, 0, 0);
11514 * Subclassed edit control Unicode windproc function
11517 * [I] hwnd : the edit window handle
11518 * [I] uMsg : the message that is to be processed
11519 * [I] wParam : first message parameter
11520 * [I] lParam : second message parameter
11524 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
11526 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
11531 * Subclassed edit control ANSI windproc function
11534 * [I] hwnd : the edit window handle
11535 * [I] uMsg : the message that is to be processed
11536 * [I] wParam : first message parameter
11537 * [I] lParam : second message parameter
11541 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
11543 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
11548 * Creates a subclassed edit control
11551 * [I] infoPtr : valid pointer to the listview structure
11552 * [I] text : initial text for the edit
11553 * [I] style : the window style
11554 * [I] isW : TRUE if input is Unicode
11558 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style, BOOL isW)
11561 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
11563 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
11565 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
11567 /* Window will be resized and positioned after LVN_BEGINLABELEDIT */
11569 hedit = CreateWindowW(WC_EDITW, text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0);
11571 hedit = CreateWindowA(WC_EDITA, (LPCSTR)text, style, 0, 0, 0, 0, infoPtr->hwndSelf, 0, hinst, 0);
11573 if (!hedit) return 0;
11575 infoPtr->EditWndProc = (WNDPROC)
11576 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
11577 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
11579 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);