4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2001 CodeWeavers Inc.
8 * Copyright 2002 Dimitrie O. Paun
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * This code was audited for completeness against the documented features
27 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
29 * Unless otherwise noted, we believe this code to be complete, as per
30 * the specification mentioned above.
31 * If you discover missing features, or bugs, please note them below.
35 * Default Message Processing
36 * -- EN_KILLFOCUS should be handled in WM_COMMAND
37 * -- WM_CREATE: create the icon and small icon image lists at this point only if
38 * the LVS_SHAREIMAGELISTS style is not specified.
39 * -- WM_ERASEBKGND: forward this message to the parent window if the bkgnd
41 * -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
42 * or small icon and the LVS_AUTOARRANGE style is specified.
47 * -- Hot item handling, mouse hovering
48 * -- Workareas support
53 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
54 * -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
55 * -- LVA_SNAPTOGRID not implemented
56 * -- LISTVIEW_ApproximateViewRect partially implemented
57 * -- LISTVIEW_[GS]etColumnOrderArray stubs
58 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
59 * -- LISTVIEW_SetIconSpacing is incomplete
60 * -- LISTVIEW_SortItems is broken
61 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
64 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
65 * linear in the number of items in the list, and this is
66 * unacceptable for large lists.
67 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
68 * instead of inserting in the right spot
69 * -- we should keep an ordered array of coordinates in iconic mode
70 * this would allow to frame items (iterator_frameditems),
71 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
79 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
86 * -- LVS_NOSCROLL (see Q137520)
87 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
89 * -- LVS_TYPESTYLEMASK
92 * -- LVS_EX_BORDERSELECT
94 * -- LVS_EX_HEADERDRAGDROP
97 * -- LVS_EX_MULTIWORKAREAS
98 * -- LVS_EX_ONECLICKACTIVATE
100 * -- LVS_EX_SIMPLESELECT
101 * -- LVS_EX_TRACKSELECT
102 * -- LVS_EX_TWOCLICKACTIVATE
103 * -- LVS_EX_UNDERLINECOLD
104 * -- LVS_EX_UNDERLINEHOT
107 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
110 * -- LVN_MARQUEEBEGIN
117 * -- LVM_CANCELEDITLABEL
118 * -- LVM_ENABLEGROUPVIEW
119 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
120 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
121 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
122 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
123 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
124 * -- LVM_GETINSERTMARKRECT
125 * -- LVM_GETNUMBEROFWORKAREAS
126 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
127 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
128 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
129 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
130 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
131 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
132 * -- LVM_GETVIEW, LVM_SETVIEW
133 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
134 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
135 * -- LVM_INSERTGROUPSORTED
136 * -- LVM_INSERTMARKHITTEST
137 * -- LVM_ISGROUPVIEWENABLED
138 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
140 * -- LVM_MOVEITEMTOGROUP
142 * -- LVM_SETTILEWIDTH
147 * -- ListView_GetCheckSate, ListView_SetCheckState
148 * -- ListView_GetHoverTime, ListView_SetHoverTime
149 * -- ListView_GetISearchString
150 * -- ListView_GetNumberOfWorkAreas
151 * -- ListView_GetOrigin
152 * -- ListView_GetTextBkColor
153 * -- ListView_GetUnicodeFormat, ListView_SetUnicodeFormat
154 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
155 * -- ListView_SortItemsEx
160 * Known differences in message stream from native control (not known if
161 * these differences cause problems):
162 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
163 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
164 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
165 * processing for "USEDOUBLECLICKTIME".
169 #include "wine/port.h"
184 #include "commctrl.h"
185 #include "comctl32.h"
188 #include "wine/debug.h"
189 #include "wine/unicode.h"
191 WINE_DEFAULT_DEBUG_CHANNEL(listview);
193 /* make sure you set this to 0 for production use! */
194 #define DEBUG_RANGES 1
196 typedef struct tagCOLUMN_INFO
198 RECT rcHeader; /* tracks the header's rectangle */
199 int fmt; /* same as LVCOLUMN.fmt */
202 typedef struct tagITEMHDR
206 } ITEMHDR, *LPITEMHDR;
208 typedef struct tagSUBITEM_INFO
214 typedef struct tagITEM_INFO
222 typedef struct tagRANGE
228 typedef struct tagRANGES
233 typedef struct tagITERATOR
242 typedef struct tagDELAYED_ITEM_EDIT
248 typedef struct tagLISTVIEW_INFO
255 HIMAGELIST himlNormal;
256 HIMAGELIST himlSmall;
257 HIMAGELIST himlState;
260 POINT ptClickPos; /* point where the user clicked */
261 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
264 RANGES selectionRanges;
269 RECT rcList; /* This rectangle is really the window
270 * client rectangle possibly reduced by the
271 * horizontal scroll bar and/or header - see
272 * LISTVIEW_UpdateSize. This rectangle offset
273 * by the LISTVIEW_GetOrigin value is in
274 * client coordinates */
283 INT ntmHeight; /* Some cached metrics of the font used */
284 INT ntmMaxCharWidth; /* by the listview to draw items */
286 BOOL bRedraw; /* Turns on/off repaints & invalidations */
287 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
289 BOOL bDoChangeNotify; /* send change notification messages? */
292 DWORD dwStyle; /* the cached window GWL_STYLE */
293 DWORD dwLvExStyle; /* extended listview style */
294 INT nItemCount; /* the number of items in the list */
295 HDPA hdpaItems; /* array ITEM_INFO pointers */
296 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
297 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
298 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
299 POINT currIconPos; /* this is the position next icon will be placed */
300 PFNLVCOMPARE pfnCompare;
308 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
310 DWORD lastKeyPressTimestamp;
312 INT nSearchParamLength;
313 WCHAR szSearchParam[ MAX_PATH ];
315 INT nMeasureItemHeight;
316 INT xTrackLine; /* The x coefficient of the track line or -1 if none */
317 DELAYED_ITEM_EDIT itemEdit; /* Pointer to this structure will be the timer ID */
323 /* How many we debug buffer to allocate */
324 #define DEBUG_BUFFERS 20
325 /* The size of a single debug bbuffer */
326 #define DEBUG_BUFFER_SIZE 256
328 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
329 #define SB_INTERNAL -1
331 /* maximum size of a label */
332 #define DISP_TEXT_SIZE 512
334 /* padding for items in list and small icon display modes */
335 #define WIDTH_PADDING 12
337 /* padding for items in list, report and small icon display modes */
338 #define HEIGHT_PADDING 1
340 /* offset of items in report display mode */
341 #define REPORT_MARGINX 2
343 /* padding for icon in large icon display mode
344 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
345 * that HITTEST will see.
346 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
347 * ICON_TOP_PADDING - sum of the two above.
348 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
349 * LABEL_HOR_PADDING - between text and sides of box
350 * LABEL_VERT_PADDING - between bottom of text and end of box
352 * ICON_LR_PADDING - additional width above icon size.
353 * ICON_LR_HALF - half of the above value
355 #define ICON_TOP_PADDING_NOTHITABLE 2
356 #define ICON_TOP_PADDING_HITABLE 2
357 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
358 #define ICON_BOTTOM_PADDING 4
359 #define LABEL_HOR_PADDING 5
360 #define LABEL_VERT_PADDING 7
361 #define ICON_LR_PADDING 16
362 #define ICON_LR_HALF (ICON_LR_PADDING/2)
364 /* default label width for items in list and small icon display modes */
365 #define DEFAULT_LABEL_WIDTH 40
367 /* default column width for items in list display mode */
368 #define DEFAULT_COLUMN_WIDTH 128
370 /* Size of "line" scroll for V & H scrolls */
371 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
373 /* Padding between image and label */
374 #define IMAGE_PADDING 2
376 /* Padding behind the label */
377 #define TRAILING_LABEL_PADDING 12
378 #define TRAILING_HEADER_PADDING 11
380 /* Border for the icon caption */
381 #define CAPTION_BORDER 2
383 /* Standard DrawText flags */
384 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
385 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
386 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
388 /* Image index from state */
389 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
391 /* The time in milliseconds to reset the search in the list */
392 #define KEY_DELAY 450
394 /* Dump the LISTVIEW_INFO structure to the debug channel */
395 #define LISTVIEW_DUMP(iP) do { \
396 TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
397 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
398 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
399 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
400 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
401 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
402 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
403 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
404 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
405 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
408 static const WCHAR themeClass[] = {'L','i','s','t','V','i','e','w',0};
411 * forward declarations
413 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *, LPLVITEMW, BOOL);
414 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *, INT, LPRECT);
415 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *, INT, LPPOINT);
416 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *, INT, LPPOINT);
417 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *, INT, LPRECT);
418 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *, INT);
419 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *, LPPOINT);
420 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *, LPRECT);
421 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
422 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LVITEMW *, BOOL);
423 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *);
424 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
425 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
426 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
427 static LRESULT LISTVIEW_Command(const LISTVIEW_INFO *, WPARAM, LPARAM);
428 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
429 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *, LPCWSTR, BOOL);
430 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
431 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *, INT, UINT);
432 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
433 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
434 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
435 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *);
436 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
437 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
438 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
439 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
441 /******** Text handling functions *************************************/
443 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
444 * text string. The string may be ANSI or Unicode, in which case
445 * the boolean isW tells us the type of the string.
447 * The name of the function tell what type of strings it expects:
448 * W: Unicode, T: ANSI/Unicode - function of isW
451 static inline BOOL is_textW(LPCWSTR text)
453 return text != NULL && text != LPSTR_TEXTCALLBACKW;
456 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
458 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
459 return is_textW(text);
462 static inline int textlenT(LPCWSTR text, BOOL isW)
464 return !is_textT(text, isW) ? 0 :
465 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
468 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
471 if (isSrcW) lstrcpynW(dest, src, max);
472 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
474 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
475 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
478 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
480 LPWSTR wstr = (LPWSTR)text;
482 if (!isW && is_textT(text, isW))
484 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
485 wstr = Alloc(len * sizeof(WCHAR));
486 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
488 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
492 static inline void textfreeT(LPWSTR wstr, BOOL isW)
494 if (!isW && is_textT(wstr, isW)) Free (wstr);
498 * dest is a pointer to a Unicode string
499 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
501 static BOOL textsetptrT(LPWSTR *dest, LPCWSTR src, BOOL isW)
505 if (src == LPSTR_TEXTCALLBACKW)
507 if (is_textW(*dest)) Free(*dest);
508 *dest = LPSTR_TEXTCALLBACKW;
512 LPWSTR pszText = textdupTtoW(src, isW);
513 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
514 bResult = Str_SetPtrW(dest, pszText);
515 textfreeT(pszText, isW);
521 * compares a Unicode to a Unicode/ANSI text string
523 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
525 if (!aw) return bt ? -1 : 0;
526 if (!bt) return aw ? 1 : 0;
527 if (aw == LPSTR_TEXTCALLBACKW)
528 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
529 if (bt != LPSTR_TEXTCALLBACKW)
531 LPWSTR bw = textdupTtoW(bt, isW);
532 int r = bw ? lstrcmpW(aw, bw) : 1;
540 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
544 n = min(min(n, strlenW(s1)), strlenW(s2));
545 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
546 return res ? res - sizeof(WCHAR) : res;
549 /******** Debugging functions *****************************************/
551 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
553 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
554 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
557 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
559 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
560 n = min(textlenT(text, isW), n);
561 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
564 static char* debug_getbuf(void)
566 static int index = 0;
567 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
568 return buffers[index++ % DEBUG_BUFFERS];
571 static inline const char* debugrange(const RANGE *lprng)
573 if (!lprng) return "(null)";
574 return wine_dbg_sprintf("[%d, %d]", lprng->lower, lprng->upper);
577 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
579 char* buf = debug_getbuf(), *text = buf;
580 int len, size = DEBUG_BUFFER_SIZE;
582 if (pScrollInfo == NULL) return "(null)";
583 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
584 if (len == -1) goto end; buf += len; size -= len;
585 if (pScrollInfo->fMask & SIF_RANGE)
586 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
588 if (len == -1) goto end; buf += len; size -= len;
589 if (pScrollInfo->fMask & SIF_PAGE)
590 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
592 if (len == -1) goto end; buf += len; size -= len;
593 if (pScrollInfo->fMask & SIF_POS)
594 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
596 if (len == -1) goto end; buf += len; size -= len;
597 if (pScrollInfo->fMask & SIF_TRACKPOS)
598 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
600 if (len == -1) goto end; buf += len; size -= len;
603 buf = text + strlen(text);
605 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
609 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
611 if (!plvnm) return "(null)";
612 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
613 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
614 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
615 plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam);
618 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
620 char* buf = debug_getbuf(), *text = buf;
621 int len, size = DEBUG_BUFFER_SIZE;
623 if (lpLVItem == NULL) return "(null)";
624 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
625 if (len == -1) goto end; buf += len; size -= len;
626 if (lpLVItem->mask & LVIF_STATE)
627 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
629 if (len == -1) goto end; buf += len; size -= len;
630 if (lpLVItem->mask & LVIF_TEXT)
631 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
633 if (len == -1) goto end; buf += len; size -= len;
634 if (lpLVItem->mask & LVIF_IMAGE)
635 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
637 if (len == -1) goto end; buf += len; size -= len;
638 if (lpLVItem->mask & LVIF_PARAM)
639 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
641 if (len == -1) goto end; buf += len; size -= len;
642 if (lpLVItem->mask & LVIF_INDENT)
643 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
645 if (len == -1) goto end; buf += len; size -= len;
648 buf = text + strlen(text);
650 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
654 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
656 char* buf = debug_getbuf(), *text = buf;
657 int len, size = DEBUG_BUFFER_SIZE;
659 if (lpColumn == NULL) return "(null)";
660 len = snprintf(buf, size, "{");
661 if (len == -1) goto end; buf += len; size -= len;
662 if (lpColumn->mask & LVCF_SUBITEM)
663 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
665 if (len == -1) goto end; buf += len; size -= len;
666 if (lpColumn->mask & LVCF_FMT)
667 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
669 if (len == -1) goto end; buf += len; size -= len;
670 if (lpColumn->mask & LVCF_WIDTH)
671 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
673 if (len == -1) goto end; buf += len; size -= len;
674 if (lpColumn->mask & LVCF_TEXT)
675 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
677 if (len == -1) goto end; buf += len; size -= len;
678 if (lpColumn->mask & LVCF_IMAGE)
679 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
681 if (len == -1) goto end; buf += len; size -= len;
682 if (lpColumn->mask & LVCF_ORDER)
683 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
685 if (len == -1) goto end; buf += len; size -= len;
688 buf = text + strlen(text);
690 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
694 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
696 if (!lpht) return "(null)";
698 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
699 wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
702 /* Return the corresponding text for a given scroll value */
703 static inline LPCSTR debugscrollcode(int nScrollCode)
707 case SB_LINELEFT: return "SB_LINELEFT";
708 case SB_LINERIGHT: return "SB_LINERIGHT";
709 case SB_PAGELEFT: return "SB_PAGELEFT";
710 case SB_PAGERIGHT: return "SB_PAGERIGHT";
711 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
712 case SB_THUMBTRACK: return "SB_THUMBTRACK";
713 case SB_ENDSCROLL: return "SB_ENDSCROLL";
714 case SB_INTERNAL: return "SB_INTERNAL";
715 default: return "unknown";
720 /******** Notification functions i************************************/
722 static LRESULT notify_forward_header(const LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
724 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
725 (WPARAM)lpnmh->hdr.idFrom, (LPARAM)lpnmh);
728 static LRESULT notify_hdr(const LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
732 TRACE("(code=%d)\n", code);
734 pnmh->hwndFrom = infoPtr->hwndSelf;
735 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
737 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, pnmh->idFrom, (LPARAM)pnmh);
739 TRACE(" <= %ld\n", result);
744 static inline BOOL notify(const LISTVIEW_INFO *infoPtr, INT code)
747 HWND hwnd = infoPtr->hwndSelf;
748 notify_hdr(infoPtr, code, &nmh);
749 return IsWindow(hwnd);
752 static inline void notify_itemactivate(const LISTVIEW_INFO *infoPtr, const LVHITTESTINFO *htInfo)
763 item.mask = LVIF_PARAM|LVIF_STATE;
764 item.iItem = htInfo->iItem;
766 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
767 nmia.lParam = item.lParam;
768 nmia.uOldState = item.state;
769 nmia.uNewState = item.state | LVIS_ACTIVATING;
770 nmia.uChanged = LVIF_STATE;
773 nmia.iItem = htInfo->iItem;
774 nmia.iSubItem = htInfo->iSubItem;
775 nmia.ptAction = htInfo->pt;
777 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
778 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
779 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
781 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
784 static inline LRESULT notify_listview(const LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
786 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
787 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
790 static BOOL notify_click(const LISTVIEW_INFO *infoPtr, INT code, const LVHITTESTINFO *lvht)
794 HWND hwnd = infoPtr->hwndSelf;
796 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
797 ZeroMemory(&nmlv, sizeof(nmlv));
798 nmlv.iItem = lvht->iItem;
799 nmlv.iSubItem = lvht->iSubItem;
800 nmlv.ptAction = lvht->pt;
801 item.mask = LVIF_PARAM;
802 item.iItem = lvht->iItem;
804 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
805 notify_listview(infoPtr, code, &nmlv);
806 return IsWindow(hwnd);
809 static BOOL notify_deleteitem(const LISTVIEW_INFO *infoPtr, INT nItem)
813 HWND hwnd = infoPtr->hwndSelf;
815 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
817 item.mask = LVIF_PARAM;
820 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
821 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
822 return IsWindow(hwnd);
825 static int get_ansi_notification(INT unicodeNotificationCode)
827 switch (unicodeNotificationCode)
829 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
830 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
831 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
832 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
833 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
834 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
836 ERR("unknown notification %x\n", unicodeNotificationCode);
842 Send notification. depends on dispinfoW having same
843 structure as dispinfoA.
844 infoPtr : listview struct
845 notificationCode : *Unicode* notification code
846 pdi : dispinfo structure (can be unicode or ansi)
847 isW : TRUE if dispinfo is Unicode
849 static BOOL notify_dispinfoT(const LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
851 BOOL bResult = FALSE;
852 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
853 INT cchTempBufMax = 0, savCchTextMax = 0, realNotifCode;
854 LPWSTR pszTempBuf = NULL, savPszText = NULL;
856 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
858 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
859 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
862 if (convertToAnsi || convertToUnicode)
864 if (notificationCode != LVN_GETDISPINFOW)
866 cchTempBufMax = convertToUnicode ?
867 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
868 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
872 cchTempBufMax = pdi->item.cchTextMax;
873 *pdi->item.pszText = 0; /* make sure we don't process garbage */
876 pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
877 if (!pszTempBuf) return FALSE;
879 if (convertToUnicode)
880 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
881 pszTempBuf, cchTempBufMax);
883 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
884 cchTempBufMax, NULL, NULL);
886 savCchTextMax = pdi->item.cchTextMax;
887 savPszText = pdi->item.pszText;
888 pdi->item.pszText = pszTempBuf;
889 pdi->item.cchTextMax = cchTempBufMax;
892 if (infoPtr->notifyFormat == NFR_ANSI)
893 realNotifCode = get_ansi_notification(notificationCode);
895 realNotifCode = notificationCode;
896 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
897 bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
899 if (convertToUnicode || convertToAnsi)
901 if (convertToUnicode) /* note : pointer can be changed by app ! */
902 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
903 savCchTextMax, NULL, NULL);
905 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
906 savPszText, savCchTextMax);
907 pdi->item.pszText = savPszText; /* restores our buffer */
908 pdi->item.cchTextMax = savCchTextMax;
914 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, const LISTVIEW_INFO *infoPtr, HDC hdc,
915 const RECT *rcBounds, const LVITEMW *lplvItem)
917 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
918 lpnmlvcd->nmcd.hdc = hdc;
919 lpnmlvcd->nmcd.rc = *rcBounds;
920 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
921 lpnmlvcd->clrText = infoPtr->clrText;
922 if (!lplvItem) return;
923 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
924 lpnmlvcd->iSubItem = lplvItem->iSubItem;
925 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
926 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
927 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
928 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
931 static inline DWORD notify_customdraw (const LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
933 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
936 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
937 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
938 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
939 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
940 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
941 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
945 static void prepaint_setup (const LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd, BOOL SubItem)
947 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
948 lpnmlvcd->clrTextBk = comctl32_color.clrWindow;
949 if (lpnmlvcd->clrText == CLR_DEFAULT)
950 lpnmlvcd->clrText = comctl32_color.clrWindowText;
952 /* apparently, for selected items, we have to override the returned values */
955 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
959 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
960 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
962 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
964 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
965 lpnmlvcd->clrText = comctl32_color.clrBtnText;
970 /* Set the text attributes */
971 if (lpnmlvcd->clrTextBk != CLR_NONE)
973 SetBkMode(hdc, OPAQUE);
974 SetBkColor(hdc,lpnmlvcd->clrTextBk);
977 SetBkMode(hdc, TRANSPARENT);
978 SetTextColor(hdc, lpnmlvcd->clrText);
981 static inline DWORD notify_postpaint (const LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
983 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
986 /******** Item iterator functions **********************************/
988 static RANGES ranges_create(int count);
989 static void ranges_destroy(RANGES ranges);
990 static BOOL ranges_add(RANGES ranges, RANGE range);
991 static BOOL ranges_del(RANGES ranges, RANGE range);
992 static void ranges_dump(RANGES ranges);
994 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
996 RANGE range = { nItem, nItem + 1 };
998 return ranges_add(ranges, range);
1001 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
1003 RANGE range = { nItem, nItem + 1 };
1005 return ranges_del(ranges, range);
1009 * ITERATOR DOCUMENTATION
1011 * The iterator functions allow for easy, and convenient iteration
1012 * over items of interest in the list. Typically, you create a
1013 * iterator, use it, and destroy it, as such:
1016 * iterator_xxxitems(&i, ...);
1017 * while (iterator_{prev,next}(&i)
1019 * //code which uses i.nItem
1021 * iterator_destroy(&i);
1023 * where xxx is either: framed, or visible.
1024 * Note that it is important that the code destroys the iterator
1025 * after it's done with it, as the creation of the iterator may
1026 * allocate memory, which thus needs to be freed.
1028 * You can iterate both forwards, and backwards through the list,
1029 * by using iterator_next or iterator_prev respectively.
1031 * Lower numbered items are draw on top of higher number items in
1032 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1033 * items may overlap). So, to test items, you should use
1035 * which lists the items top to bottom (in Z-order).
1036 * For drawing items, you should use
1038 * which lists the items bottom to top (in Z-order).
1039 * If you keep iterating over the items after the end-of-items
1040 * marker (-1) is returned, the iterator will start from the
1041 * beginning. Typically, you don't need to test for -1,
1042 * because iterator_{next,prev} will return TRUE if more items
1043 * are to be iterated over, or FALSE otherwise.
1045 * Note: the iterator is defined to be bidirectional. That is,
1046 * any number of prev followed by any number of next, or
1047 * five versa, should leave the iterator at the same item:
1048 * prev * n, next * n = next * n, prev * n
1050 * The iterator has a notion of an out-of-order, special item,
1051 * which sits at the start of the list. This is used in
1052 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1053 * which needs to be first, as it may overlap other items.
1055 * The code is a bit messy because we have:
1056 * - a special item to deal with
1057 * - simple range, or composite range
1059 * If you find bugs, or want to add features, please make sure you
1060 * always check/modify *both* iterator_prev, and iterator_next.
1064 * This function iterates through the items in increasing order,
1065 * but prefixed by the special item, then -1. That is:
1066 * special, 1, 2, 3, ..., n, -1.
1067 * Each item is listed only once.
1069 static inline BOOL iterator_next(ITERATOR* i)
1073 i->nItem = i->nSpecial;
1074 if (i->nItem != -1) return TRUE;
1076 if (i->nItem == i->nSpecial)
1078 if (i->ranges) i->index = 0;
1084 if (i->nItem == i->nSpecial) i->nItem++;
1085 if (i->nItem < i->range.upper) return TRUE;
1090 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1091 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1094 else if (i->nItem >= i->range.upper) goto end;
1096 i->nItem = i->range.lower;
1097 if (i->nItem >= 0) goto testitem;
1104 * This function iterates through the items in decreasing order,
1105 * followed by the special item, then -1. That is:
1106 * n, n-1, ..., 3, 2, 1, special, -1.
1107 * Each item is listed only once.
1109 static inline BOOL iterator_prev(ITERATOR* i)
1116 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1119 if (i->nItem == i->nSpecial)
1127 if (i->nItem == i->nSpecial) i->nItem--;
1128 if (i->nItem >= i->range.lower) return TRUE;
1134 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1137 else if (!start && i->nItem < i->range.lower) goto end;
1139 i->nItem = i->range.upper;
1140 if (i->nItem > 0) goto testitem;
1142 return (i->nItem = i->nSpecial) != -1;
1145 static RANGE iterator_range(const ITERATOR *i)
1149 if (!i->ranges) return i->range;
1151 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1153 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1154 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1156 else range.lower = range.upper = 0;
1162 * Releases resources associated with this ierator.
1164 static inline void iterator_destroy(const ITERATOR *i)
1166 ranges_destroy(i->ranges);
1170 * Create an empty iterator.
1172 static inline BOOL iterator_empty(ITERATOR* i)
1174 ZeroMemory(i, sizeof(*i));
1175 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1180 * Create an iterator over a range.
1182 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1190 * Create an iterator over a bunch of ranges.
1191 * Please note that the iterator will take ownership of the ranges,
1192 * and will free them upon destruction.
1194 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1202 * Creates an iterator over the items which intersect lprc.
1204 static BOOL iterator_frameditems(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *lprc)
1206 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1207 RECT frame = *lprc, rcItem, rcTemp;
1210 /* in case we fail, we want to return an empty iterator */
1211 if (!iterator_empty(i)) return FALSE;
1213 LISTVIEW_GetOrigin(infoPtr, &Origin);
1215 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc));
1216 OffsetRect(&frame, -Origin.x, -Origin.y);
1218 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1222 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1224 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1225 if (IntersectRect(&rcTemp, &rcItem, lprc))
1226 i->nSpecial = infoPtr->nFocusedItem;
1228 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1229 /* to do better here, we need to have PosX, and PosY sorted */
1230 TRACE("building icon ranges:\n");
1231 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1233 rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1234 rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1235 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1236 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1237 if (IntersectRect(&rcTemp, &rcItem, &frame))
1238 ranges_additem(i->ranges, nItem);
1242 else if (uView == LVS_REPORT)
1246 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1247 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1249 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1250 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1251 if (range.upper <= range.lower) return TRUE;
1252 if (!iterator_rangeitems(i, range)) return FALSE;
1253 TRACE(" report=%s\n", debugrange(&i->range));
1257 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1258 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1259 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1260 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1261 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1262 INT lower = nFirstCol * nPerCol + nFirstRow;
1266 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1267 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1269 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1271 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1272 TRACE("building list ranges:\n");
1273 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1275 item_range.lower = nCol * nPerCol + nFirstRow;
1276 if(item_range.lower >= infoPtr->nItemCount) break;
1277 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1278 TRACE(" list=%s\n", debugrange(&item_range));
1279 ranges_add(i->ranges, item_range);
1287 * Creates an iterator over the items which intersect the visible region of hdc.
1289 static BOOL iterator_visibleitems(ITERATOR *i, const LISTVIEW_INFO *infoPtr, HDC hdc)
1291 POINT Origin, Position;
1292 RECT rcItem, rcClip;
1295 rgntype = GetClipBox(hdc, &rcClip);
1296 if (rgntype == NULLREGION) return iterator_empty(i);
1297 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1298 if (rgntype == SIMPLEREGION) return TRUE;
1300 /* first deal with the special item */
1301 if (i->nSpecial != -1)
1303 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1304 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1307 /* if we can't deal with the region, we'll just go with the simple range */
1308 LISTVIEW_GetOrigin(infoPtr, &Origin);
1309 TRACE("building visible range:\n");
1310 if (!i->ranges && i->range.lower < i->range.upper)
1312 if (!(i->ranges = ranges_create(50))) return TRUE;
1313 if (!ranges_add(i->ranges, i->range))
1315 ranges_destroy(i->ranges);
1321 /* now delete the invisible items from the list */
1322 while(iterator_next(i))
1324 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1325 rcItem.left = Position.x + Origin.x;
1326 rcItem.top = Position.y + Origin.y;
1327 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1328 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1329 if (!RectVisible(hdc, &rcItem))
1330 ranges_delitem(i->ranges, i->nItem);
1332 /* the iterator should restart on the next iterator_next */
1338 /******** Misc helper functions ************************************/
1340 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1341 WPARAM wParam, LPARAM lParam, BOOL isW)
1343 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1344 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1347 static inline BOOL is_autoarrange(const LISTVIEW_INFO *infoPtr)
1349 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1351 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1352 (uView == LVS_ICON || uView == LVS_SMALLICON);
1355 static void toggle_checkbox_state(LISTVIEW_INFO *infoPtr, INT nItem)
1357 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
1358 if(state == 1 || state == 2)
1362 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
1363 lvitem.stateMask = LVIS_STATEIMAGEMASK;
1364 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
1368 /******** Internal API functions ************************************/
1370 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(const LISTVIEW_INFO *infoPtr, INT nSubItem)
1372 static COLUMN_INFO mainItem;
1374 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1375 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1376 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1379 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO *infoPtr, INT nSubItem, LPRECT lprc)
1381 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1384 static inline BOOL LISTVIEW_GetItemW(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1386 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1389 /* Listview invalidation functions: use _only_ these functions to invalidate */
1391 static inline BOOL is_redrawing(const LISTVIEW_INFO *infoPtr)
1393 return infoPtr->bRedraw;
1396 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO *infoPtr, const RECT* rect)
1398 if(!is_redrawing(infoPtr)) return;
1399 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect));
1400 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1403 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO *infoPtr, INT nItem)
1407 if(!is_redrawing(infoPtr)) return;
1408 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1409 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1412 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1414 POINT Origin, Position;
1417 if(!is_redrawing(infoPtr)) return;
1418 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1419 LISTVIEW_GetOrigin(infoPtr, &Origin);
1420 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1421 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1423 rcBox.bottom = infoPtr->nItemHeight;
1424 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1425 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1428 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO *infoPtr)
1430 LISTVIEW_InvalidateRect(infoPtr, NULL);
1433 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO *infoPtr, INT nColumn)
1437 if(!is_redrawing(infoPtr)) return;
1438 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1439 rcCol.top = infoPtr->rcList.top;
1440 rcCol.bottom = infoPtr->rcList.bottom;
1441 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1446 * Retrieves the number of items that can fit vertically in the client area.
1449 * [I] infoPtr : valid pointer to the listview structure
1452 * Number of items per row.
1454 static inline INT LISTVIEW_GetCountPerRow(const LISTVIEW_INFO *infoPtr)
1456 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1458 return max(nListWidth/infoPtr->nItemWidth, 1);
1463 * Retrieves the number of items that can fit horizontally in the client
1467 * [I] infoPtr : valid pointer to the listview structure
1470 * Number of items per column.
1472 static inline INT LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO *infoPtr)
1474 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1476 return max(nListHeight / infoPtr->nItemHeight, 1);
1480 /*************************************************************************
1481 * LISTVIEW_ProcessLetterKeys
1483 * Processes keyboard messages generated by pressing the letter keys
1485 * What this does is perform a case insensitive search from the
1486 * current position with the following quirks:
1487 * - If two chars or more are pressed in quick succession we search
1488 * for the corresponding string (e.g. 'abc').
1489 * - If there is a delay we wipe away the current search string and
1490 * restart with just that char.
1491 * - If the user keeps pressing the same character, whether slowly or
1492 * fast, so that the search string is entirely composed of this
1493 * character ('aaaaa' for instance), then we search for first item
1494 * that starting with that character.
1495 * - If the user types the above character in quick succession, then
1496 * we must also search for the corresponding string ('aaaaa'), and
1497 * go to that string if there is a match.
1500 * [I] hwnd : handle to the window
1501 * [I] charCode : the character code, the actual character
1502 * [I] keyData : key data
1510 * - The current implementation has a list of characters it will
1511 * accept and it ignores everything else. In particular it will
1512 * ignore accentuated characters which seems to match what
1513 * Windows does. But I'm not sure it makes sense to follow
1515 * - We don't sound a beep when the search fails.
1519 * TREEVIEW_ProcessLetterKeys
1521 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1526 WCHAR buffer[MAX_PATH];
1527 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1529 /* simple parameter checking */
1530 if (!charCode || !keyData) return 0;
1532 /* only allow the valid WM_CHARs through */
1533 if (!isalnum(charCode) &&
1534 charCode != '.' && charCode != '`' && charCode != '!' &&
1535 charCode != '@' && charCode != '#' && charCode != '$' &&
1536 charCode != '%' && charCode != '^' && charCode != '&' &&
1537 charCode != '*' && charCode != '(' && charCode != ')' &&
1538 charCode != '-' && charCode != '_' && charCode != '+' &&
1539 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1540 charCode != '}' && charCode != '[' && charCode != '{' &&
1541 charCode != '/' && charCode != '?' && charCode != '>' &&
1542 charCode != '<' && charCode != ',' && charCode != '~')
1545 /* if there's one item or less, there is no where to go */
1546 if (infoPtr->nItemCount <= 1) return 0;
1548 /* update the search parameters */
1549 infoPtr->lastKeyPressTimestamp = GetTickCount();
1550 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1551 if (infoPtr->nSearchParamLength < MAX_PATH)
1552 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1553 if (infoPtr->charCode != charCode)
1554 infoPtr->charCode = charCode = 0;
1556 infoPtr->charCode=charCode;
1557 infoPtr->szSearchParam[0]=charCode;
1558 infoPtr->nSearchParamLength=1;
1559 /* Redundant with the 1 char string */
1563 /* and search from the current position */
1565 if (infoPtr->nFocusedItem >= 0) {
1566 endidx=infoPtr->nFocusedItem;
1568 /* if looking for single character match,
1569 * then we must always move forward
1571 if (infoPtr->nSearchParamLength == 1)
1574 endidx=infoPtr->nItemCount;
1578 if (idx == infoPtr->nItemCount) {
1579 if (endidx == infoPtr->nItemCount || endidx == 0)
1585 item.mask = LVIF_TEXT;
1588 item.pszText = buffer;
1589 item.cchTextMax = MAX_PATH;
1590 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1592 /* check for a match */
1593 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1596 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1597 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1598 /* This would work but we must keep looking for a longer match */
1602 } while (idx != endidx);
1605 LISTVIEW_KeySelection(infoPtr, nItem);
1610 /*************************************************************************
1611 * LISTVIEW_UpdateHeaderSize [Internal]
1613 * Function to resize the header control
1616 * [I] hwnd : handle to a window
1617 * [I] nNewScrollPos : scroll pos to set
1622 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1627 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1629 GetWindowRect(infoPtr->hwndHeader, &winRect);
1630 point[0].x = winRect.left;
1631 point[0].y = winRect.top;
1632 point[1].x = winRect.right;
1633 point[1].y = winRect.bottom;
1635 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1636 point[0].x = -nNewScrollPos;
1637 point[1].x += nNewScrollPos;
1639 SetWindowPos(infoPtr->hwndHeader,0,
1640 point[0].x,point[0].y,point[1].x,point[1].y,
1641 (infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW |
1642 SWP_NOZORDER | SWP_NOACTIVATE);
1647 * Update the scrollbars. This functions should be called whenever
1648 * the content, size or view changes.
1651 * [I] infoPtr : valid pointer to the listview structure
1656 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *infoPtr)
1658 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1659 SCROLLINFO horzInfo, vertInfo;
1662 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1664 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1665 horzInfo.cbSize = sizeof(SCROLLINFO);
1666 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1668 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1669 if (uView == LVS_LIST)
1671 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1672 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1674 /* scroll by at least one column per page */
1675 if(horzInfo.nPage < infoPtr->nItemWidth)
1676 horzInfo.nPage = infoPtr->nItemWidth;
1678 horzInfo.nPage /= infoPtr->nItemWidth;
1680 else if (uView == LVS_REPORT)
1682 horzInfo.nMax = infoPtr->nItemWidth;
1684 else /* LVS_ICON, or LVS_SMALLICON */
1688 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1691 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1692 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1693 dx = GetScrollPos(infoPtr->hwndSelf, SB_HORZ);
1694 dx -= SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1695 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1697 /* Setting the horizontal scroll can change the listview size
1698 * (and potentially everything else) so we need to recompute
1699 * everything again for the vertical scroll
1702 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1703 vertInfo.cbSize = sizeof(SCROLLINFO);
1704 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1706 if (uView == LVS_REPORT)
1708 vertInfo.nMax = infoPtr->nItemCount;
1710 /* scroll by at least one page */
1711 if(vertInfo.nPage < infoPtr->nItemHeight)
1712 vertInfo.nPage = infoPtr->nItemHeight;
1714 vertInfo.nPage /= infoPtr->nItemHeight;
1716 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1720 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1723 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1724 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1725 dy = GetScrollPos(infoPtr->hwndSelf, SB_VERT);
1726 dy -= SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1727 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1729 /* Change of the range may have changed the scroll pos. If so move the content */
1730 if (dx != 0 || dy != 0)
1733 listRect = infoPtr->rcList;
1734 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &listRect, &listRect, 0, 0,
1735 SW_ERASE | SW_INVALIDATE);
1738 /* Update the Header Control */
1739 if (uView == LVS_REPORT)
1741 horzInfo.fMask = SIF_POS;
1742 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1743 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1750 * Shows/hides the focus rectangle.
1753 * [I] infoPtr : valid pointer to the listview structure
1754 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1759 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO *infoPtr, BOOL fShow)
1761 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1764 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1766 if (infoPtr->nFocusedItem < 0) return;
1768 /* we need some gymnastics in ICON mode to handle large items */
1769 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1773 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1774 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1776 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1781 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1783 /* for some reason, owner draw should work only in report mode */
1784 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1789 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1790 HFONT hOldFont = SelectObject(hdc, hFont);
1792 item.iItem = infoPtr->nFocusedItem;
1794 item.mask = LVIF_PARAM;
1795 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1797 ZeroMemory(&dis, sizeof(dis));
1798 dis.CtlType = ODT_LISTVIEW;
1799 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1800 dis.itemID = item.iItem;
1801 dis.itemAction = ODA_FOCUS;
1802 if (fShow) dis.itemState |= ODS_FOCUS;
1803 dis.hwndItem = infoPtr->hwndSelf;
1805 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1806 dis.itemData = item.lParam;
1808 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1810 SelectObject(hdc, hOldFont);
1814 DrawFocusRect(hdc, &infoPtr->rcFocus);
1817 ReleaseDC(infoPtr->hwndSelf, hdc);
1821 * Invalidates all visible selected items.
1823 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO *infoPtr)
1827 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1828 while(iterator_next(&i))
1830 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1831 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1833 iterator_destroy(&i);
1838 * DESCRIPTION: [INTERNAL]
1839 * Computes an item's (left,top) corner, relative to rcView.
1840 * That is, the position has NOT been made relative to the Origin.
1841 * This is deliberate, to avoid computing the Origin over, and
1842 * over again, when this function is called in a loop. Instead,
1843 * one can factor the computation of the Origin before the loop,
1844 * and offset the value returned by this function, on every iteration.
1847 * [I] infoPtr : valid pointer to the listview structure
1848 * [I] nItem : item number
1849 * [O] lpptOrig : item top, left corner
1854 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1856 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1858 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1860 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1862 lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1863 lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1865 else if (uView == LVS_LIST)
1867 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1868 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1869 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1871 else /* LVS_REPORT */
1873 lpptPosition->x = 0;
1874 lpptPosition->y = nItem * infoPtr->nItemHeight;
1879 * DESCRIPTION: [INTERNAL]
1880 * Compute the rectangles of an item. This is to localize all
1881 * the computations in one place. If you are not interested in some
1882 * of these values, simply pass in a NULL -- the function is smart
1883 * enough to compute only what's necessary. The function computes
1884 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1885 * one, the BOX rectangle. This rectangle is very cheap to compute,
1886 * and is guaranteed to contain all the other rectangles. Computing
1887 * the ICON rect is also cheap, but all the others are potentially
1888 * expensive. This gives an easy and effective optimization when
1889 * searching (like point inclusion, or rectangle intersection):
1890 * first test against the BOX, and if TRUE, test against the desired
1892 * If the function does not have all the necessary information
1893 * to computed the requested rectangles, will crash with a
1894 * failed assertion. This is done so we catch all programming
1895 * errors, given that the function is called only from our code.
1897 * We have the following 'special' meanings for a few fields:
1898 * * If LVIS_FOCUSED is set, we assume the item has the focus
1899 * This is important in ICON mode, where it might get a larger
1900 * then usual rectangle
1902 * Please note that subitem support works only in REPORT mode.
1905 * [I] infoPtr : valid pointer to the listview structure
1906 * [I] lpLVItem : item to compute the measures for
1907 * [O] lprcBox : ptr to Box rectangle
1908 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1909 * [0] lprcSelectBox : ptr to select box rectangle
1910 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
1911 * [O] lprcIcon : ptr to Icon rectangle
1912 * Same as LVM_GETITEMRECT with LVIR_ICON
1913 * [O] lprcStateIcon: ptr to State Icon rectangle
1914 * [O] lprcLabel : ptr to Label rectangle
1915 * Same as LVM_GETITEMRECT with LVIR_LABEL
1920 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1921 LPRECT lprcBox, LPRECT lprcSelectBox,
1922 LPRECT lprcIcon, LPRECT lprcStateIcon, LPRECT lprcLabel)
1924 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1925 BOOL doSelectBox = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1926 RECT Box, SelectBox, Icon, Label;
1927 COLUMN_INFO *lpColumnInfo = NULL;
1928 SIZE labelSize = { 0, 0 };
1930 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1932 /* Be smart and try to figure out the minimum we have to do */
1933 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1934 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1936 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1937 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1939 if (lprcSelectBox) doSelectBox = TRUE;
1940 if (lprcLabel) doLabel = TRUE;
1941 if (doLabel || lprcIcon || lprcStateIcon) doIcon = TRUE;
1948 /************************************************************/
1949 /* compute the box rectangle (it should be cheap to do) */
1950 /************************************************************/
1951 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1952 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1954 if (lpLVItem->iSubItem)
1956 Box = lpColumnInfo->rcHeader;
1961 Box.right = infoPtr->nItemWidth;
1964 Box.bottom = infoPtr->nItemHeight;
1966 /******************************************************************/
1967 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
1968 /******************************************************************/
1971 LONG state_width = 0;
1973 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1974 state_width = infoPtr->iconStateSize.cx;
1976 if (uView == LVS_ICON)
1978 Icon.left = Box.left + state_width;
1979 if (infoPtr->himlNormal)
1980 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx - state_width) / 2;
1981 Icon.top = Box.top + ICON_TOP_PADDING;
1982 Icon.right = Icon.left;
1983 Icon.bottom = Icon.top;
1984 if (infoPtr->himlNormal)
1986 Icon.right += infoPtr->iconSize.cx;
1987 Icon.bottom += infoPtr->iconSize.cy;
1990 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1992 Icon.left = Box.left + state_width;
1994 if (uView == LVS_REPORT)
1995 Icon.left += REPORT_MARGINX;
1998 Icon.right = Icon.left;
1999 if (infoPtr->himlSmall &&
2000 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
2001 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
2002 Icon.right += infoPtr->iconSize.cx;
2003 Icon.bottom = Icon.top + infoPtr->iconSize.cy;
2005 if(lprcIcon) *lprcIcon = Icon;
2006 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon));
2008 /* TODO: is this correct? */
2011 lprcStateIcon->left = Icon.left - state_width;
2012 lprcStateIcon->right = Icon.left;
2013 lprcStateIcon->top = Icon.top;
2014 lprcStateIcon->bottom = lprcStateIcon->top + infoPtr->iconSize.cy;
2015 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon));
2018 else Icon.right = 0;
2020 /************************************************************/
2021 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2022 /************************************************************/
2025 /* calculate how far to the right can the label stretch */
2026 Label.right = Box.right;
2027 if (uView == LVS_REPORT)
2029 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
2032 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
2034 labelSize.cx = infoPtr->nItemWidth;
2035 labelSize.cy = infoPtr->nItemHeight;
2039 /* we need the text in non owner draw mode */
2040 assert(lpLVItem->mask & LVIF_TEXT);
2041 if (is_textT(lpLVItem->pszText, TRUE))
2043 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2044 HDC hdc = GetDC(infoPtr->hwndSelf);
2045 HFONT hOldFont = SelectObject(hdc, hFont);
2049 /* compute rough rectangle where the label will go */
2050 SetRectEmpty(&rcText);
2051 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2052 rcText.bottom = infoPtr->nItemHeight;
2053 if (uView == LVS_ICON)
2054 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2056 /* now figure out the flags */
2057 if (uView == LVS_ICON)
2058 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2060 uFormat = LV_SL_DT_FLAGS;
2062 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2064 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2065 labelSize.cy = rcText.bottom - rcText.top;
2067 SelectObject(hdc, hOldFont);
2068 ReleaseDC(infoPtr->hwndSelf, hdc);
2072 if (uView == LVS_ICON)
2074 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2075 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2076 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2077 Label.right = Label.left + labelSize.cx;
2078 Label.bottom = Label.top + infoPtr->nItemHeight;
2079 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2081 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2082 labelSize.cy /= infoPtr->ntmHeight;
2083 labelSize.cy = max(labelSize.cy, 1);
2084 labelSize.cy *= infoPtr->ntmHeight;
2086 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2088 else if (uView == LVS_REPORT)
2090 Label.left = Icon.right;
2091 Label.top = Box.top;
2092 Label.right = lpColumnInfo->rcHeader.right;
2093 Label.bottom = Label.top + infoPtr->nItemHeight;
2095 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2097 Label.left = Icon.right;
2098 Label.top = Box.top;
2099 Label.right = min(Label.left + labelSize.cx, Label.right);
2100 Label.bottom = Label.top + infoPtr->nItemHeight;
2103 if (lprcLabel) *lprcLabel = Label;
2104 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label));
2107 /************************************************************/
2108 /* compute STATEICON bounding box */
2109 /************************************************************/
2112 if (uView == LVS_REPORT)
2114 SelectBox.left = Icon.right; /* FIXME: should be Icon.left */
2115 SelectBox.top = Box.top;
2116 SelectBox.bottom = Box.bottom;
2117 if (lpLVItem->iSubItem == 0)
2119 /* we need the indent in report mode */
2120 assert(lpLVItem->mask & LVIF_INDENT);
2121 SelectBox.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
2123 SelectBox.right = min(SelectBox.left + labelSize.cx, Label.right);
2127 UnionRect(&SelectBox, &Icon, &Label);
2129 if (lprcSelectBox) *lprcSelectBox = SelectBox;
2130 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox));
2133 /* Fix the Box if necessary */
2136 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2137 else *lprcBox = Box;
2139 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box));
2143 * DESCRIPTION: [INTERNAL]
2146 * [I] infoPtr : valid pointer to the listview structure
2147 * [I] nItem : item number
2148 * [O] lprcBox : ptr to Box rectangle
2153 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2155 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2156 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2157 POINT Position, Origin;
2160 LISTVIEW_GetOrigin(infoPtr, &Origin);
2161 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2163 /* Be smart and try to figure out the minimum we have to do */
2165 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2166 lvItem.mask |= LVIF_TEXT;
2167 lvItem.iItem = nItem;
2168 lvItem.iSubItem = 0;
2169 lvItem.pszText = szDispText;
2170 lvItem.cchTextMax = DISP_TEXT_SIZE;
2171 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2172 if (uView == LVS_ICON)
2174 lvItem.mask |= LVIF_STATE;
2175 lvItem.stateMask = LVIS_FOCUSED;
2176 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2178 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0, 0);
2180 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2186 * Returns the current icon position, and advances it along the top.
2187 * The returned position is not offset by Origin.
2190 * [I] infoPtr : valid pointer to the listview structure
2191 * [O] lpPos : will get the current icon position
2196 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2198 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2200 *lpPos = infoPtr->currIconPos;
2202 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2203 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2205 infoPtr->currIconPos.x = 0;
2206 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2212 * Returns the current icon position, and advances it down the left edge.
2213 * The returned position is not offset by Origin.
2216 * [I] infoPtr : valid pointer to the listview structure
2217 * [O] lpPos : will get the current icon position
2222 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2224 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2226 *lpPos = infoPtr->currIconPos;
2228 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2229 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2231 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2232 infoPtr->currIconPos.y = 0;
2238 * Moves an icon to the specified position.
2239 * It takes care of invalidating the item, etc.
2242 * [I] infoPtr : valid pointer to the listview structure
2243 * [I] nItem : the item to move
2244 * [I] lpPos : the new icon position
2245 * [I] isNew : flags the item as being new
2251 static BOOL LISTVIEW_MoveIconTo(const LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2257 old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2258 old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2260 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2261 LISTVIEW_InvalidateItem(infoPtr, nItem);
2264 /* Allocating a POINTER for every item is too resource intensive,
2265 * so we'll keep the (x,y) in different arrays */
2266 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2267 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2269 LISTVIEW_InvalidateItem(infoPtr, nItem);
2276 * Arranges listview items in icon display mode.
2279 * [I] infoPtr : valid pointer to the listview structure
2280 * [I] nAlignCode : alignment code
2286 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2288 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2289 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2293 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2295 TRACE("nAlignCode=%d\n", nAlignCode);
2297 if (nAlignCode == LVA_DEFAULT)
2299 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2300 else nAlignCode = LVA_ALIGNTOP;
2305 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2306 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2307 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2308 default: return FALSE;
2311 infoPtr->bAutoarrange = TRUE;
2312 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2313 for (i = 0; i < infoPtr->nItemCount; i++)
2315 next_pos(infoPtr, &pos);
2316 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2324 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2327 * [I] infoPtr : valid pointer to the listview structure
2328 * [O] lprcView : bounding rectangle
2334 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2338 SetRectEmpty(lprcView);
2340 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2344 for (i = 0; i < infoPtr->nItemCount; i++)
2346 x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2347 y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2348 lprcView->right = max(lprcView->right, x);
2349 lprcView->bottom = max(lprcView->bottom, y);
2351 if (infoPtr->nItemCount > 0)
2353 lprcView->right += infoPtr->nItemWidth;
2354 lprcView->bottom += infoPtr->nItemHeight;
2359 y = LISTVIEW_GetCountPerColumn(infoPtr);
2360 x = infoPtr->nItemCount / y;
2361 if (infoPtr->nItemCount % y) x++;
2362 lprcView->right = x * infoPtr->nItemWidth;
2363 lprcView->bottom = y * infoPtr->nItemHeight;
2367 lprcView->right = infoPtr->nItemWidth;
2368 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2375 * Retrieves the bounding rectangle of all the items.
2378 * [I] infoPtr : valid pointer to the listview structure
2379 * [O] lprcView : bounding rectangle
2385 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2389 TRACE("(lprcView=%p)\n", lprcView);
2391 if (!lprcView) return FALSE;
2393 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2394 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2395 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2397 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView));
2404 * Retrieves the subitem pointer associated with the subitem index.
2407 * [I] hdpaSubItems : DPA handle for a specific item
2408 * [I] nSubItem : index of subitem
2411 * SUCCESS : subitem pointer
2414 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2416 SUBITEM_INFO *lpSubItem;
2419 /* we should binary search here if need be */
2420 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2422 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2423 if (lpSubItem->iSubItem == nSubItem)
2433 * Calculates the desired item width.
2436 * [I] infoPtr : valid pointer to the listview structure
2439 * The desired item width.
2441 static INT LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO *infoPtr)
2443 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2446 TRACE("uView=%d\n", uView);
2448 if (uView == LVS_ICON)
2449 nItemWidth = infoPtr->iconSpacing.cx;
2450 else if (uView == LVS_REPORT)
2454 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2456 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2457 nItemWidth = rcHeader.right;
2460 else /* LVS_SMALLICON, or LVS_LIST */
2464 for (i = 0; i < infoPtr->nItemCount; i++)
2465 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2467 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2468 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2470 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2473 return max(nItemWidth, 1);
2478 * Calculates the desired item height.
2481 * [I] infoPtr : valid pointer to the listview structure
2484 * The desired item height.
2486 static INT LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO *infoPtr)
2488 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2491 TRACE("uView=%d\n", uView);
2493 if (uView == LVS_ICON)
2494 nItemHeight = infoPtr->iconSpacing.cy;
2497 nItemHeight = infoPtr->ntmHeight;
2498 if (uView == LVS_REPORT && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
2500 if (infoPtr->himlState)
2501 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2502 if (infoPtr->himlSmall)
2503 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2504 if (infoPtr->himlState || infoPtr->himlSmall)
2505 nItemHeight += HEIGHT_PADDING;
2506 if (infoPtr->nMeasureItemHeight > 0)
2507 nItemHeight = infoPtr->nMeasureItemHeight;
2510 return max(nItemHeight, 1);
2515 * Updates the width, and height of an item.
2518 * [I] infoPtr : valid pointer to the listview structure
2523 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2525 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2526 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2532 * Retrieves and saves important text metrics info for the current
2536 * [I] infoPtr : valid pointer to the listview structure
2539 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2541 HDC hdc = GetDC(infoPtr->hwndSelf);
2542 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2543 HFONT hOldFont = SelectObject(hdc, hFont);
2547 if (GetTextMetricsW(hdc, &tm))
2549 infoPtr->ntmHeight = tm.tmHeight;
2550 infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
2553 if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
2554 infoPtr->nEllipsisWidth = sz.cx;
2556 SelectObject(hdc, hOldFont);
2557 ReleaseDC(infoPtr->hwndSelf, hdc);
2559 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2564 * A compare function for ranges
2567 * [I] range1 : pointer to range 1;
2568 * [I] range2 : pointer to range 2;
2572 * > 0 : if range 1 > range 2
2573 * < 0 : if range 2 > range 1
2574 * = 0 : if range intersects range 2
2576 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2580 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2582 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2587 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2593 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2595 #define ranges_check(ranges, desc) do { } while(0)
2598 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2603 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2605 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2606 ranges_dump(ranges);
2607 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2608 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2609 assert (prev->lower >= 0 && prev->lower < prev->upper);
2610 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2612 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2613 assert (prev->upper <= curr->lower);
2614 assert (curr->lower < curr->upper);
2617 TRACE("--- Done checking---\n");
2620 static RANGES ranges_create(int count)
2622 RANGES ranges = Alloc(sizeof(struct tagRANGES));
2623 if (!ranges) return NULL;
2624 ranges->hdpa = DPA_Create(count);
2625 if (ranges->hdpa) return ranges;
2630 static void ranges_clear(RANGES ranges)
2634 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2635 Free(DPA_GetPtr(ranges->hdpa, i));
2636 DPA_DeleteAllPtrs(ranges->hdpa);
2640 static void ranges_destroy(RANGES ranges)
2642 if (!ranges) return;
2643 ranges_clear(ranges);
2644 DPA_Destroy(ranges->hdpa);
2648 static RANGES ranges_clone(RANGES ranges)
2653 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2655 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2657 RANGE *newrng = Alloc(sizeof(RANGE));
2658 if (!newrng) goto fail;
2659 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2660 DPA_SetPtr(clone->hdpa, i, newrng);
2665 TRACE ("clone failed\n");
2666 ranges_destroy(clone);
2670 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2674 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2675 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2680 static void ranges_dump(RANGES ranges)
2684 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2685 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2688 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2690 RANGE srchrng = { nItem, nItem + 1 };
2692 TRACE("(nItem=%d)\n", nItem);
2693 ranges_check(ranges, "before contain");
2694 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2697 static INT ranges_itemcount(RANGES ranges)
2701 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2703 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2704 count += sel->upper - sel->lower;
2710 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2712 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2715 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2716 if (index == -1) return TRUE;
2718 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2720 chkrng = DPA_GetPtr(ranges->hdpa, index);
2721 if (chkrng->lower >= nItem)
2722 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2723 if (chkrng->upper > nItem)
2724 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2729 static BOOL ranges_add(RANGES ranges, RANGE range)
2734 TRACE("(%s)\n", debugrange(&range));
2735 ranges_check(ranges, "before add");
2737 /* try find overlapping regions first */
2738 srchrgn.lower = range.lower - 1;
2739 srchrgn.upper = range.upper + 1;
2740 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2746 TRACE("Adding new range\n");
2748 /* create the brand new range to insert */
2749 newrgn = Alloc(sizeof(RANGE));
2750 if(!newrgn) goto fail;
2753 /* figure out where to insert it */
2754 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2755 TRACE("index=%d\n", index);
2756 if (index == -1) index = 0;
2758 /* and get it over with */
2759 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2767 RANGE *chkrgn, *mrgrgn;
2768 INT fromindex, mergeindex;
2770 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2771 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2773 chkrgn->lower = min(range.lower, chkrgn->lower);
2774 chkrgn->upper = max(range.upper, chkrgn->upper);
2776 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2778 /* merge now common ranges */
2780 srchrgn.lower = chkrgn->lower - 1;
2781 srchrgn.upper = chkrgn->upper + 1;
2785 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2786 if (mergeindex == -1) break;
2787 if (mergeindex == index)
2789 fromindex = index + 1;
2793 TRACE("Merge with index %i\n", mergeindex);
2795 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2796 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2797 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2799 DPA_DeletePtr(ranges->hdpa, mergeindex);
2800 if (mergeindex < index) index --;
2804 ranges_check(ranges, "after add");
2808 ranges_check(ranges, "failed add");
2812 static BOOL ranges_del(RANGES ranges, RANGE range)
2817 TRACE("(%s)\n", debugrange(&range));
2818 ranges_check(ranges, "before del");
2820 /* we don't use DPAS_SORTED here, since we need *
2821 * to find the first overlapping range */
2822 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2825 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2827 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2829 /* case 1: Same range */
2830 if ( (chkrgn->upper == range.upper) &&
2831 (chkrgn->lower == range.lower) )
2833 DPA_DeletePtr(ranges->hdpa, index);
2836 /* case 2: engulf */
2837 else if ( (chkrgn->upper <= range.upper) &&
2838 (chkrgn->lower >= range.lower) )
2840 DPA_DeletePtr(ranges->hdpa, index);
2842 /* case 3: overlap upper */
2843 else if ( (chkrgn->upper <= range.upper) &&
2844 (chkrgn->lower < range.lower) )
2846 chkrgn->upper = range.lower;
2848 /* case 4: overlap lower */
2849 else if ( (chkrgn->upper > range.upper) &&
2850 (chkrgn->lower >= range.lower) )
2852 chkrgn->lower = range.upper;
2855 /* case 5: fully internal */
2858 RANGE tmprgn = *chkrgn, *newrgn;
2860 if (!(newrgn = Alloc(sizeof(RANGE)))) goto fail;
2861 newrgn->lower = chkrgn->lower;
2862 newrgn->upper = range.lower;
2863 chkrgn->lower = range.upper;
2864 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2873 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2876 ranges_check(ranges, "after del");
2880 ranges_check(ranges, "failed del");
2886 * Removes all selection ranges
2889 * [I] infoPtr : valid pointer to the listview structure
2890 * [I] toSkip : item range to skip removing the selection
2896 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2905 lvItem.stateMask = LVIS_SELECTED;
2907 /* need to clone the DPA because callbacks can change it */
2908 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2909 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2910 while(iterator_next(&i))
2911 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2912 /* note that the iterator destructor will free the cloned range */
2913 iterator_destroy(&i);
2918 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2922 if (!(toSkip = ranges_create(1))) return FALSE;
2923 if (nItem != -1) ranges_additem(toSkip, nItem);
2924 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2925 ranges_destroy(toSkip);
2929 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2931 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2936 * Retrieves the number of items that are marked as selected.
2939 * [I] infoPtr : valid pointer to the listview structure
2942 * Number of items selected.
2944 static INT LISTVIEW_GetSelectedCount(const LISTVIEW_INFO *infoPtr)
2946 INT nSelectedCount = 0;
2948 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2951 for (i = 0; i < infoPtr->nItemCount; i++)
2953 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2958 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2960 TRACE("nSelectedCount=%d\n", nSelectedCount);
2961 return nSelectedCount;
2966 * Manages the item focus.
2969 * [I] infoPtr : valid pointer to the listview structure
2970 * [I] nItem : item index
2973 * TRUE : focused item changed
2974 * FALSE : focused item has NOT changed
2976 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2978 INT oldFocus = infoPtr->nFocusedItem;
2981 if (nItem == infoPtr->nFocusedItem) return FALSE;
2983 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2984 lvItem.stateMask = LVIS_FOCUSED;
2985 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2987 return oldFocus != infoPtr->nFocusedItem;
2990 /* Helper function for LISTVIEW_ShiftIndices *only* */
2991 static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2993 if (nShiftItem < nItem) return nShiftItem;
2995 if (nShiftItem > nItem) return nShiftItem + direction;
2997 if (direction > 0) return nShiftItem + direction;
2999 return min(nShiftItem, infoPtr->nItemCount - 1);
3004 * Updates the various indices after an item has been inserted or deleted.
3007 * [I] infoPtr : valid pointer to the listview structure
3008 * [I] nItem : item index
3009 * [I] direction : Direction of shift, +1 or -1.
3014 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
3019 /* temporarily disable change notification while shifting items */
3020 bOldChange = infoPtr->bDoChangeNotify;
3021 infoPtr->bDoChangeNotify = FALSE;
3023 TRACE("Shifting %iu, %i steps\n", nItem, direction);
3025 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
3027 assert(abs(direction) == 1);
3029 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
3031 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
3032 if (nNewFocus != infoPtr->nFocusedItem)
3033 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
3035 /* But we are not supposed to modify nHotItem! */
3037 infoPtr->bDoChangeNotify = bOldChange;
3043 * Adds a block of selections.
3046 * [I] infoPtr : valid pointer to the listview structure
3047 * [I] nItem : item index
3050 * Whether the window is still valid.
3052 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3054 INT nFirst = min(infoPtr->nSelectionMark, nItem);
3055 INT nLast = max(infoPtr->nSelectionMark, nItem);
3056 HWND hwndSelf = infoPtr->hwndSelf;
3057 NMLVODSTATECHANGE nmlv;
3062 /* Temporarily disable change notification
3063 * If the control is LVS_OWNERDATA, we need to send
3064 * only one LVN_ODSTATECHANGED notification.
3065 * See MSDN documentation for LVN_ITEMCHANGED.
3067 bOldChange = infoPtr->bDoChangeNotify;
3068 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3070 if (nFirst == -1) nFirst = nItem;
3072 item.state = LVIS_SELECTED;
3073 item.stateMask = LVIS_SELECTED;
3075 for (i = nFirst; i <= nLast; i++)
3076 LISTVIEW_SetItemState(infoPtr,i,&item);
3078 ZeroMemory(&nmlv, sizeof(nmlv));
3079 nmlv.iFrom = nFirst;
3082 nmlv.uOldState = item.state;
3084 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3085 if (!IsWindow(hwndSelf))
3087 infoPtr->bDoChangeNotify = bOldChange;
3094 * Sets a single group selection.
3097 * [I] infoPtr : valid pointer to the listview structure
3098 * [I] nItem : item index
3103 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3105 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3111 if (!(selection = ranges_create(100))) return;
3113 item.state = LVIS_SELECTED;
3114 item.stateMask = LVIS_SELECTED;
3116 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3118 if (infoPtr->nSelectionMark == -1)
3120 infoPtr->nSelectionMark = nItem;
3121 ranges_additem(selection, nItem);
3127 sel.lower = min(infoPtr->nSelectionMark, nItem);
3128 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3129 ranges_add(selection, sel);
3134 RECT rcItem, rcSel, rcSelMark;
3137 rcItem.left = LVIR_BOUNDS;
3138 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3139 rcSelMark.left = LVIR_BOUNDS;
3140 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3141 UnionRect(&rcSel, &rcItem, &rcSelMark);
3142 iterator_frameditems(&i, infoPtr, &rcSel);
3143 while(iterator_next(&i))
3145 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3146 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3148 iterator_destroy(&i);
3151 bOldChange = infoPtr->bDoChangeNotify;
3152 infoPtr->bDoChangeNotify = FALSE;
3154 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3157 iterator_rangesitems(&i, selection);
3158 while(iterator_next(&i))
3159 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3160 /* this will also destroy the selection */
3161 iterator_destroy(&i);
3163 infoPtr->bDoChangeNotify = bOldChange;
3165 LISTVIEW_SetItemFocus(infoPtr, nItem);
3170 * Sets a single selection.
3173 * [I] infoPtr : valid pointer to the listview structure
3174 * [I] nItem : item index
3179 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3183 TRACE("nItem=%d\n", nItem);
3185 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3187 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3188 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3189 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3191 infoPtr->nSelectionMark = nItem;
3196 * Set selection(s) with keyboard.
3199 * [I] infoPtr : valid pointer to the listview structure
3200 * [I] nItem : item index
3203 * SUCCESS : TRUE (needs to be repainted)
3204 * FAILURE : FALSE (nothing has changed)
3206 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3208 /* FIXME: pass in the state */
3209 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3210 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3211 BOOL bResult = FALSE;
3213 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3214 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3216 if (infoPtr->dwStyle & LVS_SINGLESEL)
3219 LISTVIEW_SetSelection(infoPtr, nItem);
3226 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3231 lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3232 lvItem.stateMask = LVIS_SELECTED;
3233 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3235 if (lvItem.state & LVIS_SELECTED)
3236 infoPtr->nSelectionMark = nItem;
3238 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3243 LISTVIEW_SetSelection(infoPtr, nItem);
3246 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3249 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3253 static BOOL LISTVIEW_GetItemAtPt(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3255 LVHITTESTINFO lvHitTestInfo;
3257 ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3258 lvHitTestInfo.pt.x = pt.x;
3259 lvHitTestInfo.pt.y = pt.y;
3261 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3263 lpLVItem->mask = LVIF_PARAM;
3264 lpLVItem->iItem = lvHitTestInfo.iItem;
3265 lpLVItem->iSubItem = 0;
3267 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3272 * Called when the mouse is being actively tracked and has hovered for a specified
3276 * [I] infoPtr : valid pointer to the listview structure
3277 * [I] fwKeys : key indicator
3278 * [I] x,y : mouse position
3281 * 0 if the message was processed, non-zero if there was an error
3284 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3285 * over the item for a certain period of time.
3288 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3290 if (infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3298 if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3299 LISTVIEW_SetSelection(infoPtr, item.iItem);
3307 * Called whenever WM_MOUSEMOVE is received.
3310 * [I] infoPtr : valid pointer to the listview structure
3311 * [I] fwKeys : key indicator
3312 * [I] x,y : mouse position
3315 * 0 if the message is processed, non-zero if there was an error
3317 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3319 TRACKMOUSEEVENT trackinfo;
3321 if (!(fwKeys & MK_LBUTTON))
3322 infoPtr->bLButtonDown = FALSE;
3324 if (infoPtr->bLButtonDown)
3328 /* Check to see if we got a WM_LBUTTONUP, and skip the DragDetect.
3329 * Otherwise, DragDetect will eat it.
3331 if (PeekMessageW(&msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_NOREMOVE))
3332 if (msg.message == WM_LBUTTONUP)
3335 if (!skip && DragDetect(infoPtr->hwndSelf, infoPtr->ptClickPos))
3337 LVHITTESTINFO lvHitTestInfo;
3340 lvHitTestInfo.pt = infoPtr->ptClickPos;
3341 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3343 ZeroMemory(&nmlv, sizeof(nmlv));
3344 nmlv.iItem = lvHitTestInfo.iItem;
3345 nmlv.ptAction = infoPtr->ptClickPos;
3347 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3353 infoPtr->bLButtonDown = FALSE;
3355 /* see if we are supposed to be tracking mouse hovering */
3356 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3357 /* fill in the trackinfo struct */
3358 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3359 trackinfo.dwFlags = TME_QUERY;
3360 trackinfo.hwndTrack = infoPtr->hwndSelf;
3361 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3363 /* see if we are already tracking this hwnd */
3364 _TrackMouseEvent(&trackinfo);
3366 if(!(trackinfo.dwFlags & TME_HOVER)) {
3367 trackinfo.dwFlags = TME_HOVER;
3369 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3370 _TrackMouseEvent(&trackinfo);
3379 * Tests whether the item is assignable to a list with style lStyle
3381 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3383 if ( (lpLVItem->mask & LVIF_TEXT) &&
3384 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3385 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3393 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3396 * [I] infoPtr : valid pointer to the listview structure
3397 * [I] lpLVItem : valid pointer to new item attributes
3398 * [I] isNew : the item being set is being inserted
3399 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3400 * [O] bChanged : will be set to TRUE if the item really changed
3406 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3408 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3416 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3418 if (lpLVItem->mask == 0) return TRUE;
3420 if (infoPtr->dwStyle & LVS_OWNERDATA)
3422 /* a virtual listview only stores selection and focus */
3423 if (lpLVItem->mask & ~LVIF_STATE)
3429 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3430 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3434 /* we need to get the lParam and state of the item */
3435 item.iItem = lpLVItem->iItem;
3436 item.iSubItem = lpLVItem->iSubItem;
3437 item.mask = LVIF_STATE | LVIF_PARAM;
3438 item.stateMask = ~0;
3441 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3443 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3444 /* determine what fields will change */
3445 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3446 uChanged |= LVIF_STATE;
3448 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3449 uChanged |= LVIF_IMAGE;
3451 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3452 uChanged |= LVIF_PARAM;
3454 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3455 uChanged |= LVIF_INDENT;
3457 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3458 uChanged |= LVIF_TEXT;
3460 TRACE("uChanged=0x%x\n", uChanged);
3461 if (!uChanged) return TRUE;
3464 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3465 nmlv.iItem = lpLVItem->iItem;
3466 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3467 nmlv.uOldState = item.state;
3468 nmlv.uChanged = uChanged;
3469 nmlv.lParam = item.lParam;
3471 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3472 /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
3474 if(lpItem && !isNew && infoPtr->bDoChangeNotify)
3476 HWND hwndSelf = infoPtr->hwndSelf;
3478 if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3480 if (!IsWindow(hwndSelf))
3484 /* copy information */
3485 if (lpLVItem->mask & LVIF_TEXT)
3486 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3488 if (lpLVItem->mask & LVIF_IMAGE)
3489 lpItem->hdr.iImage = lpLVItem->iImage;
3491 if (lpLVItem->mask & LVIF_PARAM)
3492 lpItem->lParam = lpLVItem->lParam;
3494 if (lpLVItem->mask & LVIF_INDENT)
3495 lpItem->iIndent = lpLVItem->iIndent;
3497 if (uChanged & LVIF_STATE)
3499 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3501 lpItem->state &= ~lpLVItem->stateMask;
3502 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3504 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3506 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3507 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3509 else if (lpLVItem->stateMask & LVIS_SELECTED)
3510 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3512 /* if we are asked to change focus, and we manage it, do it */
3513 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3515 if (lpLVItem->state & LVIS_FOCUSED)
3517 LISTVIEW_SetItemFocus(infoPtr, -1);
3518 infoPtr->nFocusedItem = lpLVItem->iItem;
3519 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3521 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3522 infoPtr->nFocusedItem = -1;
3526 /* if we're inserting the item, we're done */
3527 if (isNew) return TRUE;
3529 /* send LVN_ITEMCHANGED notification */
3530 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3531 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3538 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3541 * [I] infoPtr : valid pointer to the listview structure
3542 * [I] lpLVItem : valid pointer to new subitem attributes
3543 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3544 * [O] bChanged : will be set to TRUE if the item really changed
3550 static BOOL set_sub_item(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3553 SUBITEM_INFO *lpSubItem;
3555 /* we do not support subitems for virtual listviews */
3556 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3558 /* set subitem only if column is present */
3559 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3561 /* First do some sanity checks */
3562 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
3563 particularly useful. We currently do not actually do anything with
3564 the flag on subitems.
3566 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)) return FALSE;
3567 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))) return TRUE;
3569 /* get the subitem structure, and create it if not there */
3570 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3571 assert (hdpaSubItems);
3573 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3576 SUBITEM_INFO *tmpSubItem;
3579 lpSubItem = Alloc(sizeof(SUBITEM_INFO));
3580 if (!lpSubItem) return FALSE;
3581 /* we could binary search here, if need be...*/
3582 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3584 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3585 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3587 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3592 lpSubItem->iSubItem = lpLVItem->iSubItem;
3593 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3597 if (lpLVItem->mask & LVIF_IMAGE)
3598 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3600 lpSubItem->hdr.iImage = lpLVItem->iImage;
3604 if (lpLVItem->mask & LVIF_TEXT)
3605 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3607 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3616 * Sets item attributes.
3619 * [I] infoPtr : valid pointer to the listview structure
3620 * [I] lpLVItem : new item attributes
3621 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3627 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem, BOOL isW)
3629 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3630 HWND hwndSelf = infoPtr->hwndSelf;
3631 LPWSTR pszText = NULL;
3632 BOOL bResult, bChanged = FALSE;
3634 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3636 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3639 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3640 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3642 pszText = lpLVItem->pszText;
3643 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
3646 /* actually set the fields */
3647 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3649 if (lpLVItem->iSubItem)
3650 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3652 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3653 if (!IsWindow(hwndSelf))
3656 /* redraw item, if necessary */
3657 if (bChanged && !infoPtr->bIsDrawing)
3659 /* this little optimization eliminates some nasty flicker */
3660 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3661 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
3662 lpLVItem->iSubItem > 0 && lpLVItem->iSubItem <= DPA_GetPtrCount(infoPtr->hdpaColumns) )
3663 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3665 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3670 textfreeT(lpLVItem->pszText, isW);
3671 lpLVItem->pszText = pszText;
3679 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3682 * [I] infoPtr : valid pointer to the listview structure
3687 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *infoPtr)
3689 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3691 SCROLLINFO scrollInfo;
3693 scrollInfo.cbSize = sizeof(SCROLLINFO);
3694 scrollInfo.fMask = SIF_POS;
3696 if (uView == LVS_LIST)
3698 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3699 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3701 else if (uView == LVS_REPORT)
3703 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3704 nItem = scrollInfo.nPos;
3708 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3709 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3712 TRACE("nItem=%d\n", nItem);
3720 * Erases the background of the given rectangle
3723 * [I] infoPtr : valid pointer to the listview structure
3724 * [I] hdc : device context handle
3725 * [I] lprcBox : clipping rectangle
3731 static inline BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3733 if (!infoPtr->hBkBrush) return FALSE;
3735 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
3737 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3745 * [I] infoPtr : valid pointer to the listview structure
3746 * [I] hdc : device context handle
3747 * [I] nItem : item index
3748 * [I] nSubItem : subitem index
3749 * [I] pos : item position in client coordinates
3750 * [I] cdmode : custom draw mode
3756 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3758 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3759 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3760 static WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3761 DWORD cdsubitemmode = CDRF_DODEFAULT;
3763 RECT rcSelect, rcBox, rcIcon, rcLabel, rcStateIcon;
3764 NMLVCUSTOMDRAW nmlvcd;
3769 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
3771 /* get information needed for drawing the item */
3772 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
3773 if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
3774 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3775 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3776 lvItem.iItem = nItem;
3777 lvItem.iSubItem = nSubItem;
3780 lvItem.cchTextMax = DISP_TEXT_SIZE;
3781 lvItem.pszText = szDispText;
3782 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3783 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3784 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3785 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
3786 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3788 /* now check if we need to update the focus rectangle */
3789 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3791 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3792 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
3793 OffsetRect(&rcBox, pos.x, pos.y);
3794 OffsetRect(&rcSelect, pos.x, pos.y);
3795 OffsetRect(&rcIcon, pos.x, pos.y);
3796 OffsetRect(&rcStateIcon, pos.x, pos.y);
3797 OffsetRect(&rcLabel, pos.x, pos.y);
3798 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
3799 wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect),
3800 wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
3802 /* fill in the custom draw structure */
3803 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3805 hOldFont = GetCurrentObject(hdc, OBJ_FONT);
3806 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3807 if (cdmode & CDRF_SKIPDEFAULT) goto postpaint;
3808 if (cdmode & CDRF_NOTIFYITEMDRAW)
3809 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3810 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3811 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3812 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3813 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3815 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3816 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3818 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3819 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
3820 else if ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) == FALSE)
3821 prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE);
3823 /* in full row select, subitems, will just use main item's colors */
3824 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3825 nmlvcd.clrTextBk = CLR_NONE;
3828 if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && (nSubItem == 0))
3830 UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
3833 TRACE("uStateImage=%d\n", uStateImage);
3834 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
3835 rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
3840 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3841 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3843 TRACE("iImage=%d\n", lvItem.iImage);
3844 ImageList_DrawEx(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3845 rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk, CLR_DEFAULT,
3846 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3849 /* Don't bother painting item being edited */
3850 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3852 /* FIXME: temporary hack */
3853 rcSelect.left = rcLabel.left;
3855 /* draw the selection background, if we're drawing the main item */
3858 /* in icon mode, the label rect is really what we want to draw the
3860 if (uView == LVS_ICON)
3863 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3864 rcSelect.right = rcBox.right;
3866 if (nmlvcd.clrTextBk != CLR_NONE)
3867 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3868 if(lprcFocus) *lprcFocus = rcSelect;
3871 /* figure out the text drawing flags */
3872 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3873 if (uView == LVS_ICON)
3874 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3877 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3879 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3880 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3881 default: uFormat |= DT_LEFT;
3884 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3886 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3887 else rcLabel.left += LABEL_HOR_PADDING;
3889 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3891 /* for GRIDLINES reduce the bottom so the text formats correctly */
3892 if (uView == LVS_REPORT && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
3895 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3898 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3899 notify_postpaint(infoPtr, &nmlvcd);
3900 if (cdsubitemmode & CDRF_NEWFONT)
3901 SelectObject(hdc, hOldFont);
3907 * Draws listview items when in owner draw mode.
3910 * [I] infoPtr : valid pointer to the listview structure
3911 * [I] hdc : device context handle
3916 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3918 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3919 DWORD cditemmode = CDRF_DODEFAULT;
3920 NMLVCUSTOMDRAW nmlvcd;
3921 POINT Origin, Position;
3927 ZeroMemory(&dis, sizeof(dis));
3929 /* Get scroll info once before loop */
3930 LISTVIEW_GetOrigin(infoPtr, &Origin);
3932 /* iterate through the invalidated rows */
3933 while(iterator_next(i))
3935 item.iItem = i->nItem;
3937 item.mask = LVIF_PARAM | LVIF_STATE;
3938 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3939 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3941 dis.CtlType = ODT_LISTVIEW;
3943 dis.itemID = item.iItem;
3944 dis.itemAction = ODA_DRAWENTIRE;
3946 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3947 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3948 dis.hwndItem = infoPtr->hwndSelf;
3950 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3951 dis.rcItem.left = Position.x + Origin.x;
3952 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3953 dis.rcItem.top = Position.y + Origin.y;
3954 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3955 dis.itemData = item.lParam;
3957 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
3960 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3961 * structure for the rest. of the paint cycle
3963 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3964 if (cdmode & CDRF_NOTIFYITEMDRAW)
3965 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3967 if (!(cditemmode & CDRF_SKIPDEFAULT))
3969 prepaint_setup (infoPtr, hdc, &nmlvcd, FALSE);
3970 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3973 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3974 notify_postpaint(infoPtr, &nmlvcd);
3980 * Draws listview items when in report display mode.
3983 * [I] infoPtr : valid pointer to the listview structure
3984 * [I] hdc : device context handle
3985 * [I] cdmode : custom draw mode
3990 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3993 RECT rcClip, rcItem;
3994 POINT Origin, Position;
4000 /* figure out what to draw */
4001 rgntype = GetClipBox(hdc, &rcClip);
4002 if (rgntype == NULLREGION) return;
4004 /* Get scroll info once before loop */
4005 LISTVIEW_GetOrigin(infoPtr, &Origin);
4007 /* narrow down the columns we need to paint */
4008 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
4010 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
4011 if (rcItem.right + Origin.x >= rcClip.left) break;
4013 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
4015 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
4016 if (rcItem.left + Origin.x < rcClip.right) break;
4018 iterator_rangeitems(&j, colRange);
4020 /* in full row select, we _have_ to draw the main item */
4021 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
4024 /* iterate through the invalidated rows */
4025 while(iterator_next(i))
4027 /* iterate through the invalidated columns */
4028 while(iterator_next(&j))
4030 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4031 Position.x += Origin.x;
4032 Position.y += Origin.y;
4034 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
4036 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4038 rcItem.bottom = infoPtr->nItemHeight;
4039 OffsetRect(&rcItem, Position.x, Position.y);
4040 if (!RectVisible(hdc, &rcItem)) continue;
4043 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
4046 iterator_destroy(&j);
4051 * Draws the gridlines if necessary when in report display mode.
4054 * [I] infoPtr : valid pointer to the listview structure
4055 * [I] hdc : device context handle
4060 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO *infoPtr, HDC hdc)
4065 RECT rcClip, rcItem;
4072 /* figure out what to draw */
4073 rgntype = GetClipBox(hdc, &rcClip);
4074 if (rgntype == NULLREGION) return;
4076 /* Get scroll info once before loop */
4077 LISTVIEW_GetOrigin(infoPtr, &Origin);
4079 /* narrow down the columns we need to paint */
4080 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
4082 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
4083 if (rcItem.right + Origin.x >= rcClip.left) break;
4085 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
4087 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
4088 if (rcItem.left + Origin.x < rcClip.right) break;
4090 iterator_rangeitems(&j, colRange);
4092 if ((hPen = CreatePen( PS_SOLID, 1, comctl32_color.clr3dFace )))
4094 hOldPen = SelectObject ( hdc, hPen );
4096 /* draw the vertical lines for the columns */
4097 iterator_rangeitems(&j, colRange);
4098 while(iterator_next(&j))
4100 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4101 if (rcItem.left == 0) continue; /* skip first column */
4102 rcItem.left += Origin.x;
4103 rcItem.right += Origin.x;
4104 rcItem.top = infoPtr->rcList.top;
4105 rcItem.bottom = infoPtr->rcList.bottom;
4106 TRACE("vert col=%d, rcItem=%s\n", j.nItem, wine_dbgstr_rect(&rcItem));
4107 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4108 LineTo (hdc, rcItem.left, rcItem.bottom);
4110 iterator_destroy(&j);
4112 /* draw the horizontial lines for the rows */
4113 itemheight = LISTVIEW_CalculateItemHeight(infoPtr);
4114 rcItem.left = infoPtr->rcList.left + Origin.x;
4115 rcItem.right = infoPtr->rcList.right + Origin.x;
4116 rcItem.bottom = rcItem.top = Origin.y - 1;
4117 MoveToEx(hdc, rcItem.left, rcItem.top, NULL);
4118 LineTo(hdc, rcItem.right, rcItem.top);
4119 for(y=itemheight-1+Origin.y; y<=infoPtr->rcList.bottom; y+=itemheight)
4121 rcItem.bottom = rcItem.top = y;
4122 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem));
4123 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4124 LineTo (hdc, rcItem.right, rcItem.top);
4127 SelectObject( hdc, hOldPen );
4128 DeleteObject( hPen );
4134 * Draws listview items when in list display mode.
4137 * [I] infoPtr : valid pointer to the listview structure
4138 * [I] hdc : device context handle
4139 * [I] cdmode : custom draw mode
4144 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4146 POINT Origin, Position;
4148 /* Get scroll info once before loop */
4149 LISTVIEW_GetOrigin(infoPtr, &Origin);
4151 while(iterator_prev(i))
4153 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4154 Position.x += Origin.x;
4155 Position.y += Origin.y;
4157 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
4164 * Draws listview items.
4167 * [I] infoPtr : valid pointer to the listview structure
4168 * [I] hdc : device context handle
4169 * [I] prcErase : rect to be erased before refresh (may be NULL)
4174 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcErase)
4176 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4177 COLORREF oldTextColor = 0, oldBkColor = 0, oldClrTextBk, oldClrText;
4178 NMLVCUSTOMDRAW nmlvcd;
4185 HBITMAP hbmp = NULL;
4187 LISTVIEW_DUMP(infoPtr);
4189 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4190 TRACE("double buffering\n");
4192 hdc = CreateCompatibleDC(hdcOrig);
4194 ERR("Failed to create DC for backbuffer\n");
4197 hbmp = CreateCompatibleBitmap(hdcOrig, infoPtr->rcList.right,
4198 infoPtr->rcList.bottom);
4200 ERR("Failed to create bitmap for backbuffer\n");
4205 SelectObject(hdc, hbmp);
4206 SelectObject(hdc, infoPtr->hFont);
4208 /* Save dc values we're gonna trash while drawing
4209 * FIXME: Should be done in LISTVIEW_DrawItem() */
4210 hOldFont = SelectObject(hdc, infoPtr->hFont);
4211 oldBkMode = GetBkMode(hdc);
4212 oldBkColor = GetBkColor(hdc);
4213 oldTextColor = GetTextColor(hdc);
4216 infoPtr->bIsDrawing = TRUE;
4219 LISTVIEW_FillBkgnd(infoPtr, hdc, prcErase);
4220 } else if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4221 /* If no erasing was done (usually because RedrawWindow was called
4222 * with RDW_INVALIDATE only) we need to copy the old contents into
4223 * the backbuffer before continuing. */
4224 BitBlt(hdc, infoPtr->rcList.left, infoPtr->rcList.top,
4225 infoPtr->rcList.right - infoPtr->rcList.left,
4226 infoPtr->rcList.bottom - infoPtr->rcList.top,
4227 hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4230 /* FIXME: Shouldn't need to do this */
4231 oldClrTextBk = infoPtr->clrTextBk;
4232 oldClrText = infoPtr->clrText;
4234 infoPtr->cditemmode = CDRF_DODEFAULT;
4236 GetClientRect(infoPtr->hwndSelf, &rcClient);
4237 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4238 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4239 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4240 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4242 /* Use these colors to draw the items */
4243 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4244 infoPtr->clrText = nmlvcd.clrText;
4246 /* nothing to draw */
4247 if(infoPtr->nItemCount == 0) goto enddraw;
4249 /* figure out what we need to draw */
4250 iterator_visibleitems(&i, infoPtr, hdc);
4252 /* send cache hint notification */
4253 if (infoPtr->dwStyle & LVS_OWNERDATA)
4255 RANGE range = iterator_range(&i);
4258 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4259 nmlv.iFrom = range.lower;
4260 nmlv.iTo = range.upper - 1;
4261 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4264 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4265 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4268 if (uView == LVS_REPORT)
4269 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4270 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4271 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4273 /* if we have a focus rect, draw it */
4274 if (infoPtr->bFocus)
4275 DrawFocusRect(hdc, &infoPtr->rcFocus);
4277 iterator_destroy(&i);
4280 /* For LVS_EX_GRIDLINES go and draw lines */
4281 /* This includes the case where there were *no* items */
4282 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
4283 infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
4284 LISTVIEW_RefreshReportGrid(infoPtr, hdc);
4286 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4287 notify_postpaint(infoPtr, &nmlvcd);
4289 infoPtr->clrTextBk = oldClrTextBk;
4290 infoPtr->clrText = oldClrText;
4293 BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top,
4294 infoPtr->rcList.right - infoPtr->rcList.left,
4295 infoPtr->rcList.bottom - infoPtr->rcList.top,
4296 hdc, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4301 SelectObject(hdc, hOldFont);
4302 SetBkMode(hdc, oldBkMode);
4303 SetBkColor(hdc, oldBkColor);
4304 SetTextColor(hdc, oldTextColor);
4307 infoPtr->bIsDrawing = FALSE;
4313 * Calculates the approximate width and height of a given number of items.
4316 * [I] infoPtr : valid pointer to the listview structure
4317 * [I] nItemCount : number of items
4318 * [I] wWidth : width
4319 * [I] wHeight : height
4322 * Returns a DWORD. The width in the low word and the height in high word.
4324 static DWORD LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO *infoPtr, INT nItemCount,
4325 WORD wWidth, WORD wHeight)
4327 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4328 INT nItemCountPerColumn = 1;
4329 INT nColumnCount = 0;
4330 DWORD dwViewRect = 0;
4332 if (nItemCount == -1)
4333 nItemCount = infoPtr->nItemCount;
4335 if (uView == LVS_LIST)
4337 if (wHeight == 0xFFFF)
4339 /* use current height */
4340 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4343 if (wHeight < infoPtr->nItemHeight)
4344 wHeight = infoPtr->nItemHeight;
4348 if (infoPtr->nItemHeight > 0)
4350 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4351 if (nItemCountPerColumn == 0)
4352 nItemCountPerColumn = 1;
4354 if (nItemCount % nItemCountPerColumn != 0)
4355 nColumnCount = nItemCount / nItemCountPerColumn;
4357 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4361 /* Microsoft padding magic */
4362 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4363 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4365 dwViewRect = MAKELONG(wWidth, wHeight);
4367 else if (uView == LVS_REPORT)
4371 if (infoPtr->nItemCount > 0)
4373 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4374 wWidth = rcBox.right - rcBox.left;
4375 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4379 /* use current height and width */
4380 if (wHeight == 0xffff)
4381 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4382 if (wWidth == 0xffff)
4383 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4386 dwViewRect = MAKELONG(wWidth, wHeight);
4388 else if (uView == LVS_SMALLICON)
4389 FIXME("uView == LVS_SMALLICON: not implemented\n");
4390 else if (uView == LVS_ICON)
4391 FIXME("uView == LVS_ICON: not implemented\n");
4399 * Create a drag image list for the specified item.
4402 * [I] infoPtr : valid pointer to the listview structure
4403 * [I] iItem : index of item
4404 * [O] lppt : Upperr-left corner of the image
4407 * Returns a handle to the image list if successful, NULL otherwise.
4409 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4415 HBITMAP hbmp, hOldbmp;
4416 HIMAGELIST dragList = 0;
4417 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
4419 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4422 rcItem.left = LVIR_BOUNDS;
4423 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4426 lppt->x = rcItem.left;
4427 lppt->y = rcItem.top;
4429 size.cx = rcItem.right - rcItem.left;
4430 size.cy = rcItem.bottom - rcItem.top;
4432 hdcOrig = GetDC(infoPtr->hwndSelf);
4433 hdc = CreateCompatibleDC(hdcOrig);
4434 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4435 hOldbmp = SelectObject(hdc, hbmp);
4437 rcItem.left = rcItem.top = 0;
4438 rcItem.right = size.cx;
4439 rcItem.bottom = size.cy;
4440 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4443 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4445 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4446 SelectObject(hdc, hOldbmp);
4447 ImageList_Add(dragList, hbmp, 0);
4450 SelectObject(hdc, hOldbmp);
4454 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4456 TRACE("ret=%p\n", dragList);
4464 * Removes all listview items and subitems.
4467 * [I] infoPtr : valid pointer to the listview structure
4473 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr, BOOL destroy)
4476 HDPA hdpaSubItems = NULL;
4483 /* we do it directly, to avoid notifications */
4484 ranges_clear(infoPtr->selectionRanges);
4485 infoPtr->nSelectionMark = -1;
4486 infoPtr->nFocusedItem = -1;
4487 SetRectEmpty(&infoPtr->rcFocus);
4488 /* But we are supposed to leave nHotItem as is! */
4491 /* send LVN_DELETEALLITEMS notification */
4492 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4494 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4496 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4498 /* send LVN_DELETEITEM notification, if not suppressed */
4499 if (!bSuppress) notify_deleteitem(infoPtr, i);
4500 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4502 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4503 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4505 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4506 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4509 DPA_Destroy(hdpaSubItems);
4510 DPA_DeletePtr(infoPtr->hdpaItems, i);
4512 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4513 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4514 infoPtr->nItemCount --;
4519 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4520 LISTVIEW_UpdateScroll(infoPtr);
4522 LISTVIEW_InvalidateList(infoPtr);
4529 * Scrolls, and updates the columns, when a column is changing width.
4532 * [I] infoPtr : valid pointer to the listview structure
4533 * [I] nColumn : column to scroll
4534 * [I] dx : amount of scroll, in pixels
4539 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4541 COLUMN_INFO *lpColumnInfo;
4546 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4547 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4548 rcCol = lpColumnInfo->rcHeader;
4549 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4550 rcCol.left = rcCol.right;
4552 /* adjust the other columns */
4553 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4555 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4556 lpColumnInfo->rcHeader.left += dx;
4557 lpColumnInfo->rcHeader.right += dx;
4560 /* do not update screen if not in report mode */
4561 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4563 /* if we have a focus, we must first erase the focus rect */
4564 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4566 /* Need to reset the item width when inserting a new column */
4567 infoPtr->nItemWidth += dx;
4569 LISTVIEW_UpdateScroll(infoPtr);
4570 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4572 /* scroll to cover the deleted column, and invalidate for redraw */
4573 rcOld = infoPtr->rcList;
4574 rcOld.left = ptOrigin.x + rcCol.left + dx;
4575 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4577 /* we can restore focus now */
4578 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4583 * Removes a column from the listview control.
4586 * [I] infoPtr : valid pointer to the listview structure
4587 * [I] nColumn : column index
4593 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4597 TRACE("nColumn=%d\n", nColumn);
4599 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4600 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4602 /* While the MSDN specifically says that column zero should not be deleted,
4603 what actually happens is that the column itself is deleted but no items or subitems
4607 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4609 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4612 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4613 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4615 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4617 SUBITEM_INFO *lpSubItem, *lpDelItem;
4619 INT nItem, nSubItem, i;
4621 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4623 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4626 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4628 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4629 if (lpSubItem->iSubItem == nColumn)
4632 lpDelItem = lpSubItem;
4634 else if (lpSubItem->iSubItem > nColumn)
4636 lpSubItem->iSubItem--;
4640 /* if we found our subitem, zapp it */
4644 if (is_textW(lpDelItem->hdr.pszText))
4645 Free(lpDelItem->hdr.pszText);
4650 /* free dpa memory */
4651 DPA_DeletePtr(hdpaSubItems, nSubItem);
4656 /* update the other column info */
4657 LISTVIEW_UpdateItemSize(infoPtr);
4658 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4659 LISTVIEW_InvalidateList(infoPtr);
4661 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4668 * Invalidates the listview after an item's insertion or deletion.
4671 * [I] infoPtr : valid pointer to the listview structure
4672 * [I] nItem : item index
4673 * [I] dir : -1 if deleting, 1 if inserting
4678 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4680 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4681 INT nPerCol, nItemCol, nItemRow;
4685 /* if we don't refresh, what's the point of scrolling? */
4686 if (!is_redrawing(infoPtr)) return;
4688 assert (abs(dir) == 1);
4690 /* arrange icons if autoarrange is on */
4691 if (is_autoarrange(infoPtr))
4693 BOOL arrange = TRUE;
4694 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4695 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4696 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4699 /* scrollbars need updating */
4700 LISTVIEW_UpdateScroll(infoPtr);
4702 /* figure out the item's position */
4703 if (uView == LVS_REPORT)
4704 nPerCol = infoPtr->nItemCount + 1;
4705 else if (uView == LVS_LIST)
4706 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4707 else /* LVS_ICON, or LVS_SMALLICON */
4710 nItemCol = nItem / nPerCol;
4711 nItemRow = nItem % nPerCol;
4712 LISTVIEW_GetOrigin(infoPtr, &Origin);
4714 /* move the items below up a slot */
4715 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4716 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4717 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4718 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4719 OffsetRect(&rcScroll, Origin.x, Origin.y);
4720 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
4721 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4723 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
4724 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4725 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4728 /* report has only that column, so we're done */
4729 if (uView == LVS_REPORT) return;
4731 /* now for LISTs, we have to deal with the columns to the right */
4732 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4734 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4735 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4736 OffsetRect(&rcScroll, Origin.x, Origin.y);
4737 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4738 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4739 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4744 * Removes an item from the listview control.
4747 * [I] infoPtr : valid pointer to the listview structure
4748 * [I] nItem : item index
4754 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4757 const UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4758 const BOOL is_icon = (uView == LVS_SMALLICON || uView == LVS_ICON);
4760 TRACE("(nItem=%d)\n", nItem);
4762 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4764 /* remove selection, and focus */
4766 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4767 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4769 /* send LVN_DELETEITEM notification. */
4770 if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
4772 /* we need to do this here, because we'll be deleting stuff */
4774 LISTVIEW_InvalidateItem(infoPtr, nItem);
4776 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4782 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4783 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4785 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4786 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4789 DPA_Destroy(hdpaSubItems);
4794 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4795 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4798 infoPtr->nItemCount--;
4799 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4801 /* now is the invalidation fun */
4803 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4810 * Callback implementation for editlabel control
4813 * [I] infoPtr : valid pointer to the listview structure
4814 * [I] pszText : modified text
4815 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4821 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4823 HWND hwndSelf = infoPtr->hwndSelf;
4824 NMLVDISPINFOW dispInfo;
4826 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4828 ZeroMemory(&dispInfo, sizeof(dispInfo));
4829 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4830 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4831 dispInfo.item.iSubItem = 0;
4832 dispInfo.item.stateMask = ~0;
4833 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4834 /* add the text from the edit in */
4835 dispInfo.item.mask |= LVIF_TEXT;
4836 dispInfo.item.pszText = pszText;
4837 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4839 /* Do we need to update the Item Text */
4840 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4841 if (!IsWindow(hwndSelf))
4843 if (!pszText) return TRUE;
4845 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4847 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nEditLabelItem);
4848 ITEM_INFO* lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
4849 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4851 LISTVIEW_InvalidateItem(infoPtr, infoPtr->nEditLabelItem);
4856 ZeroMemory(&dispInfo, sizeof(dispInfo));
4857 dispInfo.item.mask = LVIF_TEXT;
4858 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4859 dispInfo.item.iSubItem = 0;
4860 dispInfo.item.pszText = pszText;
4861 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4862 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4867 * Begin in place editing of specified list view item
4870 * [I] infoPtr : valid pointer to the listview structure
4871 * [I] nItem : item index
4872 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4878 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4880 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4881 NMLVDISPINFOW dispInfo;
4883 HWND hwndSelf = infoPtr->hwndSelf;
4885 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4887 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4888 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4890 infoPtr->nEditLabelItem = nItem;
4892 /* Is the EditBox still there, if so remove it */
4893 if(infoPtr->hwndEdit != 0)
4895 SetFocus(infoPtr->hwndSelf);
4896 infoPtr->hwndEdit = 0;
4899 LISTVIEW_SetSelection(infoPtr, nItem);
4900 LISTVIEW_SetItemFocus(infoPtr, nItem);
4901 LISTVIEW_InvalidateItem(infoPtr, nItem);
4903 rect.left = LVIR_LABEL;
4904 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4906 ZeroMemory(&dispInfo, sizeof(dispInfo));
4907 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4908 dispInfo.item.iItem = nItem;
4909 dispInfo.item.iSubItem = 0;
4910 dispInfo.item.stateMask = ~0;
4911 dispInfo.item.pszText = szDispText;
4912 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4913 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4915 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4916 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4917 if (!infoPtr->hwndEdit) return 0;
4919 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4921 if (!IsWindow(hwndSelf))
4923 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4924 infoPtr->hwndEdit = 0;
4928 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4929 SetFocus(infoPtr->hwndEdit);
4930 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4931 return infoPtr->hwndEdit;
4937 * Ensures the specified item is visible, scrolling into view if necessary.
4940 * [I] infoPtr : valid pointer to the listview structure
4941 * [I] nItem : item index
4942 * [I] bPartial : partially or entirely visible
4948 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4950 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4951 INT nScrollPosHeight = 0;
4952 INT nScrollPosWidth = 0;
4953 INT nHorzAdjust = 0;
4954 INT nVertAdjust = 0;
4957 RECT rcItem, rcTemp;
4959 rcItem.left = LVIR_BOUNDS;
4960 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4962 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4964 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4966 /* scroll left/right, but in LVS_REPORT mode */
4967 if (uView == LVS_LIST)
4968 nScrollPosWidth = infoPtr->nItemWidth;
4969 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4970 nScrollPosWidth = 1;
4972 if (rcItem.left < infoPtr->rcList.left)
4975 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4980 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4984 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4986 /* scroll up/down, but not in LVS_LIST mode */
4987 if (uView == LVS_REPORT)
4988 nScrollPosHeight = infoPtr->nItemHeight;
4989 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4990 nScrollPosHeight = 1;
4992 if (rcItem.top < infoPtr->rcList.top)
4995 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
5000 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
5004 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
5006 if (nScrollPosWidth)
5008 INT diff = nHorzDiff / nScrollPosWidth;
5009 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
5010 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
5013 if (nScrollPosHeight)
5015 INT diff = nVertDiff / nScrollPosHeight;
5016 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
5017 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
5025 * Searches for an item with specific characteristics.
5028 * [I] hwnd : window handle
5029 * [I] nStart : base item index
5030 * [I] lpFindInfo : item information to look for
5033 * SUCCESS : index of item
5036 static INT LISTVIEW_FindItemW(const LISTVIEW_INFO *infoPtr, INT nStart,
5037 const LVFINDINFOW *lpFindInfo)
5039 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5040 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5041 BOOL bWrap = FALSE, bNearest = FALSE;
5042 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
5043 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
5044 POINT Position, Destination;
5047 if (!lpFindInfo || nItem < 0) return -1;
5050 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
5052 lvItem.mask |= LVIF_TEXT;
5053 lvItem.pszText = szDispText;
5054 lvItem.cchTextMax = DISP_TEXT_SIZE;
5057 if (lpFindInfo->flags & LVFI_WRAP)
5060 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
5061 (uView == LVS_ICON || uView ==LVS_SMALLICON))
5066 LISTVIEW_GetOrigin(infoPtr, &Origin);
5067 Destination.x = lpFindInfo->pt.x - Origin.x;
5068 Destination.y = lpFindInfo->pt.y - Origin.y;
5069 switch(lpFindInfo->vkDirection)
5071 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
5072 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
5073 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
5074 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
5075 case VK_HOME: Destination.x = Destination.y = 0; break;
5076 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5077 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5079 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
5080 Destination.x = rcArea.right;
5081 Destination.y = rcArea.bottom;
5083 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
5087 else Destination.x = Destination.y = 0;
5089 /* if LVFI_PARAM is specified, all other flags are ignored */
5090 if (lpFindInfo->flags & LVFI_PARAM)
5092 lvItem.mask |= LVIF_PARAM;
5094 lvItem.mask &= ~LVIF_TEXT;
5098 for (; nItem < nLast; nItem++)
5100 lvItem.iItem = nItem;
5101 lvItem.iSubItem = 0;
5102 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
5104 if (lvItem.mask & LVIF_PARAM)
5106 if (lpFindInfo->lParam == lvItem.lParam)
5112 if (lvItem.mask & LVIF_TEXT)
5114 if (lpFindInfo->flags & LVFI_PARTIAL)
5116 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
5120 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
5124 if (!bNearest) return nItem;
5126 /* This is very inefficient. To do a good job here,
5127 * we need a sorted array of (x,y) item positions */
5128 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5130 /* compute the distance^2 to the destination */
5131 xdist = Destination.x - Position.x;
5132 ydist = Destination.y - Position.y;
5133 dist = xdist * xdist + ydist * ydist;
5135 /* remember the distance, and item if it's closer */
5139 nNearestItem = nItem;
5146 nLast = min(nStart + 1, infoPtr->nItemCount);
5151 return nNearestItem;
5156 * Searches for an item with specific characteristics.
5159 * [I] hwnd : window handle
5160 * [I] nStart : base item index
5161 * [I] lpFindInfo : item information to look for
5164 * SUCCESS : index of item
5167 static INT LISTVIEW_FindItemA(const LISTVIEW_INFO *infoPtr, INT nStart,
5168 const LVFINDINFOA *lpFindInfo)
5170 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
5175 memcpy(&fiw, lpFindInfo, sizeof(fiw));
5176 if (hasText) fiw.psz = strW = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
5177 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
5178 textfreeT(strW, FALSE);
5184 * Retrieves the background image of the listview control.
5187 * [I] infoPtr : valid pointer to the listview structure
5188 * [O] lpBkImage : background image attributes
5194 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
5196 /* FIXME (listview, "empty stub!\n"); */
5202 * Retrieves column attributes.
5205 * [I] infoPtr : valid pointer to the listview structure
5206 * [I] nColumn : column index
5207 * [IO] lpColumn : column information
5208 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5209 * otherwise it is in fact a LPLVCOLUMNA
5215 static BOOL LISTVIEW_GetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
5217 COLUMN_INFO *lpColumnInfo;
5220 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5221 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
5223 /* initialize memory */
5224 ZeroMemory(&hdi, sizeof(hdi));
5226 if (lpColumn->mask & LVCF_TEXT)
5228 hdi.mask |= HDI_TEXT;
5229 hdi.pszText = lpColumn->pszText;
5230 hdi.cchTextMax = lpColumn->cchTextMax;
5233 if (lpColumn->mask & LVCF_IMAGE)
5234 hdi.mask |= HDI_IMAGE;
5236 if (lpColumn->mask & LVCF_ORDER)
5237 hdi.mask |= HDI_ORDER;
5239 if (lpColumn->mask & LVCF_SUBITEM)
5240 hdi.mask |= HDI_LPARAM;
5242 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
5244 if (lpColumn->mask & LVCF_FMT)
5245 lpColumn->fmt = lpColumnInfo->fmt;
5247 if (lpColumn->mask & LVCF_WIDTH)
5248 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
5250 if (lpColumn->mask & LVCF_IMAGE)
5251 lpColumn->iImage = hdi.iImage;
5253 if (lpColumn->mask & LVCF_ORDER)
5254 lpColumn->iOrder = hdi.iOrder;
5256 if (lpColumn->mask & LVCF_SUBITEM)
5257 lpColumn->iSubItem = hdi.lParam;
5263 static BOOL LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5270 /* FIXME: little hack */
5271 for (i = 0; i < iCount; i++)
5279 * Retrieves the column width.
5282 * [I] infoPtr : valid pointer to the listview structure
5283 * [I] int : column index
5286 * SUCCESS : column width
5289 static INT LISTVIEW_GetColumnWidth(const LISTVIEW_INFO *infoPtr, INT nColumn)
5291 INT nColumnWidth = 0;
5294 TRACE("nColumn=%d\n", nColumn);
5296 /* we have a 'column' in LIST and REPORT mode only */
5297 switch(infoPtr->dwStyle & LVS_TYPEMASK)
5300 nColumnWidth = infoPtr->nItemWidth;
5303 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDM_ITEMCHANGED.
5304 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
5305 * HDM_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
5307 * TODO: should we do the same in LVM_GETCOLUMN?
5309 hdItem.mask = HDI_WIDTH;
5310 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdItem))
5312 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr->hwndSelf, nColumn);
5315 nColumnWidth = hdItem.cxy;
5319 TRACE("nColumnWidth=%d\n", nColumnWidth);
5320 return nColumnWidth;
5325 * In list or report display mode, retrieves the number of items that can fit
5326 * vertically in the visible area. In icon or small icon display mode,
5327 * retrieves the total number of visible items.
5330 * [I] infoPtr : valid pointer to the listview structure
5333 * Number of fully visible items.
5335 static INT LISTVIEW_GetCountPerPage(const LISTVIEW_INFO *infoPtr)
5337 switch (infoPtr->dwStyle & LVS_TYPEMASK)
5341 return infoPtr->nItemCount;
5343 return LISTVIEW_GetCountPerColumn(infoPtr);
5345 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5353 * Retrieves an image list handle.
5356 * [I] infoPtr : valid pointer to the listview structure
5357 * [I] nImageList : image list identifier
5360 * SUCCESS : image list handle
5363 static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImageList)
5367 case LVSIL_NORMAL: return infoPtr->himlNormal;
5368 case LVSIL_SMALL: return infoPtr->himlSmall;
5369 case LVSIL_STATE: return infoPtr->himlState;
5374 /* LISTVIEW_GetISearchString */
5378 * Retrieves item attributes.
5381 * [I] hwnd : window handle
5382 * [IO] lpLVItem : item info
5383 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5384 * if FALSE, then lpLVItem is a LPLVITEMA.
5387 * This is the internal 'GetItem' interface -- it tries to
5388 * be smart and avoid text copies, if possible, by modifying
5389 * lpLVItem->pszText to point to the text string. Please note
5390 * that this is not always possible (e.g. OWNERDATA), so on
5391 * entry you *must* supply valid values for pszText, and cchTextMax.
5392 * The only difference to the documented interface is that upon
5393 * return, you should use *only* the lpLVItem->pszText, rather than
5394 * the buffer pointer you provided on input. Most code already does
5395 * that, so it's not a problem.
5396 * For the two cases when the text must be copied (that is,
5397 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5403 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5405 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5406 NMLVDISPINFOW dispInfo;
5412 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5414 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5417 if (lpLVItem->mask == 0) return TRUE;
5419 /* make a local copy */
5420 isubitem = lpLVItem->iSubItem;
5422 /* a quick optimization if all we're asked is the focus state
5423 * these queries are worth optimising since they are common,
5424 * and can be answered in constant time, without the heavy accesses */
5425 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5426 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5428 lpLVItem->state = 0;
5429 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5430 lpLVItem->state |= LVIS_FOCUSED;
5434 ZeroMemory(&dispInfo, sizeof(dispInfo));
5436 /* if the app stores all the data, handle it separately */
5437 if (infoPtr->dwStyle & LVS_OWNERDATA)
5439 dispInfo.item.state = 0;
5441 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5442 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5444 /* NOTE: copy only fields which we _know_ are initialized, some apps
5445 * depend on the uninitialized fields being 0 */
5446 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5447 dispInfo.item.iItem = lpLVItem->iItem;
5448 dispInfo.item.iSubItem = isubitem;
5449 if (lpLVItem->mask & LVIF_TEXT)
5451 dispInfo.item.pszText = lpLVItem->pszText;
5452 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5454 if (lpLVItem->mask & LVIF_STATE)
5455 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5456 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5457 dispInfo.item.stateMask = lpLVItem->stateMask;
5458 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5460 /* full size structure expected - _WIN32IE >= 0x560 */
5461 *lpLVItem = dispInfo.item;
5463 else if (lpLVItem->mask & LVIF_INDENT)
5465 /* indent member expected - _WIN32IE >= 0x300 */
5466 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5470 /* minimal structure expected */
5471 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5473 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5476 /* make sure lParam is zeroed out */
5477 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5479 /* we store only a little state, so if we're not asked, we're done */
5480 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5482 /* if focus is handled by us, report it */
5483 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5485 lpLVItem->state &= ~LVIS_FOCUSED;
5486 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5487 lpLVItem->state |= LVIS_FOCUSED;
5490 /* and do the same for selection, if we handle it */
5491 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5493 lpLVItem->state &= ~LVIS_SELECTED;
5494 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5495 lpLVItem->state |= LVIS_SELECTED;
5501 /* find the item and subitem structures before we proceed */
5502 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5503 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5508 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5509 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5512 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5517 pItemHdr = &lpItem->hdr;
5519 /* Do we need to query the state from the app? */
5520 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5522 dispInfo.item.mask |= LVIF_STATE;
5523 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5526 /* Do we need to enquire about the image? */
5527 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5528 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5530 dispInfo.item.mask |= LVIF_IMAGE;
5531 dispInfo.item.iImage = I_IMAGECALLBACK;
5534 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5535 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5537 dispInfo.item.mask |= LVIF_TEXT;
5538 dispInfo.item.pszText = lpLVItem->pszText;
5539 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5540 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5541 *dispInfo.item.pszText = '\0';
5544 /* If we don't have all the requested info, query the application */
5545 if (dispInfo.item.mask != 0)
5547 dispInfo.item.iItem = lpLVItem->iItem;
5548 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5549 dispInfo.item.lParam = lpItem->lParam;
5550 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5551 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5554 /* we should not store values for subitems */
5555 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5557 /* Now, handle the iImage field */
5558 if (dispInfo.item.mask & LVIF_IMAGE)
5560 lpLVItem->iImage = dispInfo.item.iImage;
5561 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5562 pItemHdr->iImage = dispInfo.item.iImage;
5564 else if (lpLVItem->mask & LVIF_IMAGE)
5566 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5567 lpLVItem->iImage = pItemHdr->iImage;
5569 lpLVItem->iImage = 0;
5572 /* The pszText field */
5573 if (dispInfo.item.mask & LVIF_TEXT)
5575 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5576 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5578 lpLVItem->pszText = dispInfo.item.pszText;
5580 else if (lpLVItem->mask & LVIF_TEXT)
5582 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5583 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5586 /* Next is the lParam field */
5587 if (dispInfo.item.mask & LVIF_PARAM)
5589 lpLVItem->lParam = dispInfo.item.lParam;
5590 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5591 lpItem->lParam = dispInfo.item.lParam;
5593 else if (lpLVItem->mask & LVIF_PARAM)
5594 lpLVItem->lParam = lpItem->lParam;
5596 /* if this is a subitem, we're done */
5597 if (isubitem) return TRUE;
5599 /* ... the state field (this one is different due to uCallbackmask) */
5600 if (lpLVItem->mask & LVIF_STATE)
5602 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5603 if (dispInfo.item.mask & LVIF_STATE)
5605 lpLVItem->state &= ~dispInfo.item.stateMask;
5606 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5608 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5610 lpLVItem->state &= ~LVIS_FOCUSED;
5611 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5612 lpLVItem->state |= LVIS_FOCUSED;
5614 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5616 lpLVItem->state &= ~LVIS_SELECTED;
5617 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5618 lpLVItem->state |= LVIS_SELECTED;
5622 /* and last, but not least, the indent field */
5623 if (lpLVItem->mask & LVIF_INDENT)
5624 lpLVItem->iIndent = lpItem->iIndent;
5631 * Retrieves item attributes.
5634 * [I] hwnd : window handle
5635 * [IO] lpLVItem : item info
5636 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5637 * if FALSE, then lpLVItem is a LPLVITEMA.
5640 * This is the external 'GetItem' interface -- it properly copies
5641 * the text in the provided buffer.
5647 static BOOL LISTVIEW_GetItemExtT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5652 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5655 pszText = lpLVItem->pszText;
5656 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5657 if (bResult && lpLVItem->pszText != pszText)
5658 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5659 lpLVItem->pszText = pszText;
5667 * Retrieves the position (upper-left) of the listview control item.
5668 * Note that for LVS_ICON style, the upper-left is that of the icon
5669 * and not the bounding box.
5672 * [I] infoPtr : valid pointer to the listview structure
5673 * [I] nItem : item index
5674 * [O] lpptPosition : coordinate information
5680 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5682 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5685 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5687 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5689 LISTVIEW_GetOrigin(infoPtr, &Origin);
5690 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5692 if (uView == LVS_ICON)
5694 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5695 lpptPosition->y += ICON_TOP_PADDING;
5697 lpptPosition->x += Origin.x;
5698 lpptPosition->y += Origin.y;
5700 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
5707 * Retrieves the bounding rectangle for a listview control item.
5710 * [I] infoPtr : valid pointer to the listview structure
5711 * [I] nItem : item index
5712 * [IO] lprc : bounding rectangle coordinates
5713 * lprc->left specifies the portion of the item for which the bounding
5714 * rectangle will be retrieved.
5716 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5717 * including the icon and label.
5720 * * Experiment shows that native control returns:
5721 * * width = min (48, length of text line)
5722 * * .left = position.x - (width - iconsize.cx)/2
5723 * * .right = .left + width
5724 * * height = #lines of text * ntmHeight + icon height + 8
5725 * * .top = position.y - 2
5726 * * .bottom = .top + height
5727 * * separation between items .y = itemSpacing.cy - height
5728 * * .x = itemSpacing.cx - width
5729 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5732 * * Experiment shows that native control returns:
5733 * * width = iconSize.cx + 16
5734 * * .left = position.x - (width - iconsize.cx)/2
5735 * * .right = .left + width
5736 * * height = iconSize.cy + 4
5737 * * .top = position.y - 2
5738 * * .bottom = .top + height
5739 * * separation between items .y = itemSpacing.cy - height
5740 * * .x = itemSpacing.cx - width
5741 * LVIR_LABEL Returns the bounding rectangle of the item text.
5744 * * Experiment shows that native control returns:
5745 * * width = text length
5746 * * .left = position.x - width/2
5747 * * .right = .left + width
5748 * * height = ntmH * linecount + 2
5749 * * .top = position.y + iconSize.cy + 6
5750 * * .bottom = .top + height
5751 * * separation between items .y = itemSpacing.cy - height
5752 * * .x = itemSpacing.cx - width
5753 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5754 * rectangles, but excludes columns in report view.
5761 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5762 * upon whether the window has the focus currently and on whether the item
5763 * is the one with the focus. Ensure that the control's record of which
5764 * item has the focus agrees with the items' records.
5766 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5768 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5769 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5770 BOOL doLabel = TRUE, oversizedBox = FALSE;
5771 POINT Position, Origin;
5774 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5776 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5778 LISTVIEW_GetOrigin(infoPtr, &Origin);
5779 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5781 /* Be smart and try to figure out the minimum we have to do */
5782 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5783 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5784 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5785 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5786 oversizedBox = TRUE;
5788 /* get what we need from the item before hand, so we make
5789 * only one request. This can speed up things, if data
5790 * is stored on the app side */
5792 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5793 if (doLabel) lvItem.mask |= LVIF_TEXT;
5794 lvItem.iItem = nItem;
5795 lvItem.iSubItem = 0;
5796 lvItem.pszText = szDispText;
5797 lvItem.cchTextMax = DISP_TEXT_SIZE;
5798 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5799 /* we got the state already up, simulate it here, to avoid a reget */
5800 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5802 lvItem.mask |= LVIF_STATE;
5803 lvItem.stateMask = LVIS_FOCUSED;
5804 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5807 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5808 lprc->left = LVIR_BOUNDS;
5812 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
5816 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc);
5820 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
5823 case LVIR_SELECTBOUNDS:
5824 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL);
5828 WARN("Unknown value: %d\n", lprc->left);
5832 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5834 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
5841 * Retrieves the spacing between listview control items.
5844 * [I] infoPtr : valid pointer to the listview structure
5845 * [IO] lprc : rectangle to receive the output
5846 * on input, lprc->top = nSubItem
5847 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5849 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5850 * not only those of the first column.
5851 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5857 static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5863 if (!lprc) return FALSE;
5865 nColumn = lprc->top;
5867 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
5868 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5870 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5872 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5874 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5876 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5879 lvItem.iItem = nItem;
5880 lvItem.iSubItem = nColumn;
5882 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5886 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
5891 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
5895 ERR("Unknown bounds=%d\n", lprc->left);
5899 OffsetRect(lprc, Position.x, Position.y);
5906 * Retrieves the width of a label.
5909 * [I] infoPtr : valid pointer to the listview structure
5912 * SUCCESS : string width (in pixels)
5915 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *infoPtr, INT nItem)
5917 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5920 TRACE("(nItem=%d)\n", nItem);
5922 lvItem.mask = LVIF_TEXT;
5923 lvItem.iItem = nItem;
5924 lvItem.iSubItem = 0;
5925 lvItem.pszText = szDispText;
5926 lvItem.cchTextMax = DISP_TEXT_SIZE;
5927 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5929 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5934 * Retrieves the spacing between listview control items.
5937 * [I] infoPtr : valid pointer to the listview structure
5938 * [I] bSmall : flag for small or large icon
5941 * Horizontal + vertical spacing
5943 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall)
5949 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5953 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5954 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5956 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5963 * Retrieves the state of a listview control item.
5966 * [I] infoPtr : valid pointer to the listview structure
5967 * [I] nItem : item index
5968 * [I] uMask : state mask
5971 * State specified by the mask.
5973 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5977 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5979 lvItem.iItem = nItem;
5980 lvItem.iSubItem = 0;
5981 lvItem.mask = LVIF_STATE;
5982 lvItem.stateMask = uMask;
5983 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5985 return lvItem.state & uMask;
5990 * Retrieves the text of a listview control item or subitem.
5993 * [I] hwnd : window handle
5994 * [I] nItem : item index
5995 * [IO] lpLVItem : item information
5996 * [I] isW : TRUE if lpLVItem is Unicode
5999 * SUCCESS : string length
6002 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6004 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6006 lpLVItem->mask = LVIF_TEXT;
6007 lpLVItem->iItem = nItem;
6008 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
6010 return textlenT(lpLVItem->pszText, isW);
6015 * Searches for an item based on properties + relationships.
6018 * [I] infoPtr : valid pointer to the listview structure
6019 * [I] nItem : item index
6020 * [I] uFlags : relationship flag
6023 * SUCCESS : item index
6026 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
6028 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6030 LVFINDINFOW lvFindInfo;
6031 INT nCountPerColumn;
6035 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
6036 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
6038 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6040 if (uFlags & LVNI_CUT)
6043 if (uFlags & LVNI_DROPHILITED)
6044 uMask |= LVIS_DROPHILITED;
6046 if (uFlags & LVNI_FOCUSED)
6047 uMask |= LVIS_FOCUSED;
6049 if (uFlags & LVNI_SELECTED)
6050 uMask |= LVIS_SELECTED;
6052 /* if we're asked for the focused item, that's only one,
6053 * so it's worth optimizing */
6054 if (uFlags & LVNI_FOCUSED)
6056 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
6057 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
6060 if (uFlags & LVNI_ABOVE)
6062 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6067 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6073 /* Special case for autoarrange - move 'til the top of a list */
6074 if (is_autoarrange(infoPtr))
6076 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6077 while (nItem - nCountPerRow >= 0)
6079 nItem -= nCountPerRow;
6080 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6085 lvFindInfo.flags = LVFI_NEARESTXY;
6086 lvFindInfo.vkDirection = VK_UP;
6087 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6088 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6090 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6095 else if (uFlags & LVNI_BELOW)
6097 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6099 while (nItem < infoPtr->nItemCount)
6102 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6108 /* Special case for autoarrange - move 'til the bottom of a list */
6109 if (is_autoarrange(infoPtr))
6111 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6112 while (nItem + nCountPerRow < infoPtr->nItemCount )
6114 nItem += nCountPerRow;
6115 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6120 lvFindInfo.flags = LVFI_NEARESTXY;
6121 lvFindInfo.vkDirection = VK_DOWN;
6122 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6123 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6125 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6130 else if (uFlags & LVNI_TOLEFT)
6132 if (uView == LVS_LIST)
6134 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6135 while (nItem - nCountPerColumn >= 0)
6137 nItem -= nCountPerColumn;
6138 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6142 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6144 /* Special case for autoarrange - move 'ti the beginning of a row */
6145 if (is_autoarrange(infoPtr))
6147 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6148 while (nItem % nCountPerRow > 0)
6151 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6156 lvFindInfo.flags = LVFI_NEARESTXY;
6157 lvFindInfo.vkDirection = VK_LEFT;
6158 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6159 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6161 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6166 else if (uFlags & LVNI_TORIGHT)
6168 if (uView == LVS_LIST)
6170 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6171 while (nItem + nCountPerColumn < infoPtr->nItemCount)
6173 nItem += nCountPerColumn;
6174 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6178 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6180 /* Special case for autoarrange - move 'til the end of a row */
6181 if (is_autoarrange(infoPtr))
6183 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6184 while (nItem % nCountPerRow < nCountPerRow - 1 )
6187 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6192 lvFindInfo.flags = LVFI_NEARESTXY;
6193 lvFindInfo.vkDirection = VK_RIGHT;
6194 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6195 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6197 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6206 /* search by index */
6207 for (i = nItem; i < infoPtr->nItemCount; i++)
6209 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
6217 /* LISTVIEW_GetNumberOfWorkAreas */
6221 * Retrieves the origin coordinates when in icon or small icon display mode.
6224 * [I] infoPtr : valid pointer to the listview structure
6225 * [O] lpptOrigin : coordinate information
6230 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
6232 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6233 INT nHorzPos = 0, nVertPos = 0;
6234 SCROLLINFO scrollInfo;
6236 scrollInfo.cbSize = sizeof(SCROLLINFO);
6237 scrollInfo.fMask = SIF_POS;
6239 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6240 nHorzPos = scrollInfo.nPos;
6241 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6242 nVertPos = scrollInfo.nPos;
6244 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6246 lpptOrigin->x = infoPtr->rcList.left;
6247 lpptOrigin->y = infoPtr->rcList.top;
6248 if (uView == LVS_LIST)
6249 nHorzPos *= infoPtr->nItemWidth;
6250 else if (uView == LVS_REPORT)
6251 nVertPos *= infoPtr->nItemHeight;
6253 lpptOrigin->x -= nHorzPos;
6254 lpptOrigin->y -= nVertPos;
6256 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
6261 * Retrieves the width of a string.
6264 * [I] hwnd : window handle
6265 * [I] lpszText : text string to process
6266 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6269 * SUCCESS : string width (in pixels)
6272 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6277 if (is_textT(lpszText, isW))
6279 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6280 HDC hdc = GetDC(infoPtr->hwndSelf);
6281 HFONT hOldFont = SelectObject(hdc, hFont);
6284 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6286 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6287 SelectObject(hdc, hOldFont);
6288 ReleaseDC(infoPtr->hwndSelf, hdc);
6290 return stringSize.cx;
6295 * Determines which listview item is located at the specified position.
6298 * [I] infoPtr : valid pointer to the listview structure
6299 * [IO] lpht : hit test information
6300 * [I] subitem : fill out iSubItem.
6301 * [I] select : return the index only if the hit selects the item
6304 * (mm 20001022): We must not allow iSubItem to be touched, for
6305 * an app might pass only a structure with space up to iItem!
6306 * (MS Office 97 does that for instance in the file open dialog)
6309 * SUCCESS : item index
6312 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6314 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6315 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6316 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6317 POINT Origin, Position, opt;
6322 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6326 if (subitem) lpht->iSubItem = 0;
6328 if (infoPtr->rcList.left > lpht->pt.x)
6329 lpht->flags |= LVHT_TOLEFT;
6330 else if (infoPtr->rcList.right < lpht->pt.x)
6331 lpht->flags |= LVHT_TORIGHT;
6333 if (infoPtr->rcList.top > lpht->pt.y)
6334 lpht->flags |= LVHT_ABOVE;
6335 else if (infoPtr->rcList.bottom < lpht->pt.y)
6336 lpht->flags |= LVHT_BELOW;
6338 TRACE("lpht->flags=0x%x\n", lpht->flags);
6339 if (lpht->flags) return -1;
6341 lpht->flags |= LVHT_NOWHERE;
6343 LISTVIEW_GetOrigin(infoPtr, &Origin);
6345 /* first deal with the large items */
6346 rcSearch.left = lpht->pt.x;
6347 rcSearch.top = lpht->pt.y;
6348 rcSearch.right = rcSearch.left + 1;
6349 rcSearch.bottom = rcSearch.top + 1;
6351 iterator_frameditems(&i, infoPtr, &rcSearch);
6352 iterator_next(&i); /* go to first item in the sequence */
6354 iterator_destroy(&i);
6356 TRACE("lpht->iItem=%d\n", iItem);
6357 if (iItem == -1) return -1;
6359 lvItem.mask = LVIF_STATE | LVIF_TEXT;
6360 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6361 lvItem.stateMask = LVIS_STATEIMAGEMASK;
6362 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6363 lvItem.iItem = iItem;
6364 lvItem.iSubItem = 0;
6365 lvItem.pszText = szDispText;
6366 lvItem.cchTextMax = DISP_TEXT_SIZE;
6367 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6368 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6370 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
6371 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6372 opt.x = lpht->pt.x - Position.x - Origin.x;
6373 opt.y = lpht->pt.y - Position.y - Origin.y;
6375 if (uView == LVS_REPORT)
6379 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6380 UnionRect(&rcBounds, &rcBounds, &rcState);
6382 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6383 if (!PtInRect(&rcBounds, opt)) return -1;
6385 if (PtInRect(&rcIcon, opt))
6386 lpht->flags |= LVHT_ONITEMICON;
6387 else if (PtInRect(&rcLabel, opt))
6388 lpht->flags |= LVHT_ONITEMLABEL;
6389 else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6390 lpht->flags |= LVHT_ONITEMSTATEICON;
6391 if (lpht->flags & LVHT_ONITEM)
6392 lpht->flags &= ~LVHT_NOWHERE;
6394 TRACE("lpht->flags=0x%x\n", lpht->flags);
6395 if (uView == LVS_REPORT && subitem)
6399 rcBounds.right = rcBounds.left;
6400 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6402 rcBounds.left = rcBounds.right;
6403 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6404 if (PtInRect(&rcBounds, opt))
6412 if (select && !(uView == LVS_REPORT &&
6413 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6414 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6416 if (uView == LVS_REPORT)
6418 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6419 UnionRect(&rcBounds, &rcBounds, &rcState);
6421 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6423 return lpht->iItem = iItem;
6427 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6428 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6429 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6430 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6431 their own sort proc. when sending LVM_SORTITEMS.
6434 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6436 LVS_SORTXXX must be specified,
6437 LVS_OWNERDRAW is not set,
6438 <item>.pszText is not LPSTR_TEXTCALLBACK.
6440 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6441 are sorted based on item text..."
6443 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6445 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6446 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6447 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6449 /* if we're sorting descending, negate the return value */
6450 return (((const LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6455 * Inserts a new item in the listview control.
6458 * [I] infoPtr : valid pointer to the listview structure
6459 * [I] lpLVItem : item information
6460 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6463 * SUCCESS : new item index
6466 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6468 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6473 BOOL is_sorted, has_changed;
6475 HWND hwndSelf = infoPtr->hwndSelf;
6477 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6479 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6481 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6482 if (!lpLVItem || lpLVItem->iSubItem) return -1;
6484 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6486 if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
6488 /* insert item in listview control data structure */
6489 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6490 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6492 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6493 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6495 if (lpLVItem->iItem < 0 && !is_sorted) return -1;
6497 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6498 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6499 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6500 if (nItem == -1) goto fail;
6501 infoPtr->nItemCount++;
6503 /* shift indices first so they don't get tangled */
6504 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6506 /* set the item attributes */
6507 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6509 /* full size structure expected - _WIN32IE >= 0x560 */
6512 else if (lpLVItem->mask & LVIF_INDENT)
6514 /* indent member expected - _WIN32IE >= 0x300 */
6515 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6519 /* minimal structure expected */
6520 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6523 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6525 item.mask |= LVIF_STATE;
6526 item.stateMask |= LVIS_STATEIMAGEMASK;
6527 item.state &= ~LVIS_STATEIMAGEMASK;
6528 item.state |= INDEXTOSTATEIMAGEMASK(1);
6530 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6532 /* if we're sorted, sort the list, and update the index */
6535 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6536 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6537 assert(nItem != -1);
6540 /* make room for the position, if we are in the right mode */
6541 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6543 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6545 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6547 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6552 /* send LVN_INSERTITEM notification */
6553 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6555 nmlv.lParam = lpItem->lParam;
6556 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6557 if (!IsWindow(hwndSelf))
6560 /* align items (set position of each item) */
6561 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6565 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6566 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6568 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6570 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6573 /* now is the invalidation fun */
6574 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6578 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6579 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6580 infoPtr->nItemCount--;
6582 DPA_DeletePtr(hdpaSubItems, 0);
6583 DPA_Destroy (hdpaSubItems);
6590 * Redraws a range of items.
6593 * [I] infoPtr : valid pointer to the listview structure
6594 * [I] nFirst : first item
6595 * [I] nLast : last item
6601 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6605 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6606 max(nFirst, nLast) >= infoPtr->nItemCount)
6609 for (i = nFirst; i <= nLast; i++)
6610 LISTVIEW_InvalidateItem(infoPtr, i);
6617 * Scroll the content of a listview.
6620 * [I] infoPtr : valid pointer to the listview structure
6621 * [I] dx : horizontal scroll amount in pixels
6622 * [I] dy : vertical scroll amount in pixels
6629 * If the control is in report mode (LVS_REPORT) the control can
6630 * be scrolled only in line increments. "dy" will be rounded to the
6631 * nearest number of pixels that are a whole line. Ex: if line height
6632 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6633 * is passed, then the scroll will be 0. (per MSDN 7/2002)
6635 * For: (per experimentation with native control and CSpy ListView)
6636 * LVS_ICON dy=1 = 1 pixel (vertical only)
6638 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6640 * LVS_LIST dx=1 = 1 column (horizontal only)
6641 * but will only scroll 1 column per message
6642 * no matter what the value.
6643 * dy must be 0 or FALSE returned.
6644 * LVS_REPORT dx=1 = 1 pixel
6648 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6650 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6652 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6653 dy /= infoPtr->nItemHeight;
6656 if (dy != 0) return FALSE;
6663 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6664 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6671 * Sets the background color.
6674 * [I] infoPtr : valid pointer to the listview structure
6675 * [I] clrBk : background color
6681 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6683 TRACE("(clrBk=%x)\n", clrBk);
6685 if(infoPtr->clrBk != clrBk) {
6686 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6687 infoPtr->clrBk = clrBk;
6688 if (clrBk == CLR_NONE)
6689 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6691 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6692 LISTVIEW_InvalidateList(infoPtr);
6698 /* LISTVIEW_SetBkImage */
6700 /*** Helper for {Insert,Set}ColumnT *only* */
6701 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
6702 const LVCOLUMNW *lpColumn, BOOL isW)
6704 if (lpColumn->mask & LVCF_FMT)
6706 /* format member is valid */
6707 lphdi->mask |= HDI_FORMAT;
6709 /* set text alignment (leftmost column must be left-aligned) */
6710 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6711 lphdi->fmt |= HDF_LEFT;
6712 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6713 lphdi->fmt |= HDF_RIGHT;
6714 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6715 lphdi->fmt |= HDF_CENTER;
6717 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6718 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6720 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6722 lphdi->fmt |= HDF_IMAGE;
6723 lphdi->iImage = I_IMAGECALLBACK;
6727 if (lpColumn->mask & LVCF_WIDTH)
6729 lphdi->mask |= HDI_WIDTH;
6730 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6732 /* make it fill the remainder of the controls width */
6736 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6738 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6739 lphdi->cxy += rcHeader.right - rcHeader.left;
6742 /* retrieve the layout of the header */
6743 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6744 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
6746 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6749 lphdi->cxy = lpColumn->cx;
6752 if (lpColumn->mask & LVCF_TEXT)
6754 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6755 lphdi->fmt |= HDF_STRING;
6756 lphdi->pszText = lpColumn->pszText;
6757 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6760 if (lpColumn->mask & LVCF_IMAGE)
6762 lphdi->mask |= HDI_IMAGE;
6763 lphdi->iImage = lpColumn->iImage;
6766 if (lpColumn->mask & LVCF_ORDER)
6768 lphdi->mask |= HDI_ORDER;
6769 lphdi->iOrder = lpColumn->iOrder;
6776 * Inserts a new column.
6779 * [I] infoPtr : valid pointer to the listview structure
6780 * [I] nColumn : column index
6781 * [I] lpColumn : column information
6782 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6785 * SUCCESS : new column index
6788 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6789 const LVCOLUMNW *lpColumn, BOOL isW)
6791 COLUMN_INFO *lpColumnInfo;
6795 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6797 if (!lpColumn || nColumn < 0) return -1;
6798 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6800 ZeroMemory(&hdi, sizeof(HDITEMW));
6801 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6804 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
6805 * (can be seen in SPY) otherwise column never gets added.
6807 if (!(lpColumn->mask & LVCF_WIDTH)) {
6808 hdi.mask |= HDI_WIDTH;
6813 * when the iSubItem is available Windows copies it to the header lParam. It seems
6814 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
6816 if (lpColumn->mask & LVCF_SUBITEM)
6818 hdi.mask |= HDI_LPARAM;
6819 hdi.lParam = lpColumn->iSubItem;
6822 /* insert item in header control */
6823 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6824 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6825 (WPARAM)nColumn, (LPARAM)&hdi);
6826 if (nNewColumn == -1) return -1;
6827 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6829 /* create our own column info */
6830 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6831 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6833 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6834 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6836 /* now we have to actually adjust the data */
6837 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6839 SUBITEM_INFO *lpSubItem;
6843 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6845 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6846 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6848 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6849 if (lpSubItem->iSubItem >= nNewColumn)
6850 lpSubItem->iSubItem++;
6855 /* make space for the new column */
6856 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6857 LISTVIEW_UpdateItemSize(infoPtr);
6862 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6865 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6873 * Sets the attributes of a header item.
6876 * [I] infoPtr : valid pointer to the listview structure
6877 * [I] nColumn : column index
6878 * [I] lpColumn : column attributes
6879 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6885 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
6886 const LVCOLUMNW *lpColumn, BOOL isW)
6888 HDITEMW hdi, hdiget;
6891 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6893 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6895 ZeroMemory(&hdi, sizeof(HDITEMW));
6896 if (lpColumn->mask & LVCF_FMT)
6898 hdi.mask |= HDI_FORMAT;
6899 hdiget.mask = HDI_FORMAT;
6900 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6901 hdi.fmt = hdiget.fmt & HDF_STRING;
6903 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6905 /* set header item attributes */
6906 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6907 if (!bResult) return FALSE;
6909 if (lpColumn->mask & LVCF_FMT)
6911 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6912 int oldFmt = lpColumnInfo->fmt;
6914 lpColumnInfo->fmt = lpColumn->fmt;
6915 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6917 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6918 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6927 * Sets the column order array
6930 * [I] infoPtr : valid pointer to the listview structure
6931 * [I] iCount : number of elements in column order array
6932 * [I] lpiArray : pointer to column order array
6938 static BOOL LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6940 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6951 * Sets the width of a column
6954 * [I] infoPtr : valid pointer to the listview structure
6955 * [I] nColumn : column index
6956 * [I] cx : column width
6962 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6964 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6965 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6969 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6971 /* set column width only if in report or list mode */
6972 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6974 /* take care of invalid cx values */
6975 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6976 else if (uView == LVS_LIST && cx < 1) return FALSE;
6978 /* resize all columns if in LVS_LIST mode */
6979 if(uView == LVS_LIST)
6981 infoPtr->nItemWidth = cx;
6982 LISTVIEW_InvalidateList(infoPtr);
6986 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6988 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6993 lvItem.mask = LVIF_TEXT;
6995 lvItem.iSubItem = nColumn;
6996 lvItem.pszText = szDispText;
6997 lvItem.cchTextMax = DISP_TEXT_SIZE;
6998 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7000 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
7001 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
7002 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
7004 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
7005 max_cx += infoPtr->iconSize.cx;
7006 max_cx += TRAILING_LABEL_PADDING;
7009 /* autosize based on listview items width */
7010 if(cx == LVSCW_AUTOSIZE)
7012 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7014 /* if iCol is the last column make it fill the remainder of the controls width */
7015 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
7020 LISTVIEW_GetOrigin(infoPtr, &Origin);
7021 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
7023 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
7027 /* Despite what the MS docs say, if this is not the last
7028 column, then MS resizes the column to the width of the
7029 largest text string in the column, including headers
7030 and items. This is different from LVSCW_AUTOSIZE in that
7031 LVSCW_AUTOSIZE ignores the header string length. */
7034 /* retrieve header text */
7035 hdi.mask = HDI_TEXT;
7036 hdi.cchTextMax = DISP_TEXT_SIZE;
7037 hdi.pszText = szDispText;
7038 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
7040 HDC hdc = GetDC(infoPtr->hwndSelf);
7041 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
7044 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
7045 cx = size.cx + TRAILING_HEADER_PADDING;
7046 /* FIXME: Take into account the header image, if one is present */
7047 SelectObject(hdc, old_font);
7048 ReleaseDC(infoPtr->hwndSelf, hdc);
7050 cx = max (cx, max_cx);
7054 if (cx < 0) return FALSE;
7056 /* call header to update the column change */
7057 hdi.mask = HDI_WIDTH;
7059 TRACE("hdi.cxy=%d\n", hdi.cxy);
7060 return Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7064 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
7067 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
7070 HBITMAP hbm_im, hbm_mask, hbm_orig;
7072 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
7073 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
7076 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
7077 ILC_COLOR | ILC_MASK, 2, 2);
7078 hdc_wnd = GetDC(infoPtr->hwndSelf);
7079 hdc = CreateCompatibleDC(hdc_wnd);
7080 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
7081 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
7082 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
7084 rc.left = rc.top = 0;
7085 rc.right = GetSystemMetrics(SM_CXSMICON);
7086 rc.bottom = GetSystemMetrics(SM_CYSMICON);
7088 hbm_orig = SelectObject(hdc, hbm_mask);
7089 FillRect(hdc, &rc, hbr_white);
7090 InflateRect(&rc, -3, -3);
7091 FillRect(hdc, &rc, hbr_black);
7093 SelectObject(hdc, hbm_im);
7094 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
7095 SelectObject(hdc, hbm_orig);
7096 ImageList_Add(himl, hbm_im, hbm_mask);
7098 SelectObject(hdc, hbm_im);
7099 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
7100 SelectObject(hdc, hbm_orig);
7101 ImageList_Add(himl, hbm_im, hbm_mask);
7103 DeleteObject(hbm_mask);
7104 DeleteObject(hbm_im);
7112 * Sets the extended listview style.
7115 * [I] infoPtr : valid pointer to the listview structure
7117 * [I] dwStyle : style
7120 * SUCCESS : previous style
7123 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwExStyle)
7125 DWORD dwOldExStyle = infoPtr->dwLvExStyle;
7129 infoPtr->dwLvExStyle = (dwOldExStyle & ~dwMask) | (dwExStyle & dwMask);
7131 infoPtr->dwLvExStyle = dwExStyle;
7133 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_CHECKBOXES)
7135 HIMAGELIST himl = 0;
7136 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7139 item.mask = LVIF_STATE;
7140 item.stateMask = LVIS_STATEIMAGEMASK;
7141 item.state = INDEXTOSTATEIMAGEMASK(1);
7142 LISTVIEW_SetItemState(infoPtr, -1, &item);
7144 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
7146 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
7149 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_HEADERDRAGDROP)
7151 DWORD dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
7152 if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
7153 dwStyle |= HDS_DRAGDROP;
7155 dwStyle &= ~HDS_DRAGDROP;
7156 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
7159 /* GRIDLINES adds decoration at top so changes sizes */
7160 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_GRIDLINES)
7162 LISTVIEW_UpdateSize(infoPtr);
7166 LISTVIEW_InvalidateList(infoPtr);
7167 return dwOldExStyle;
7172 * Sets the new hot cursor used during hot tracking and hover selection.
7175 * [I] infoPtr : valid pointer to the listview structure
7176 * [I] hCursor : the new hot cursor handle
7179 * Returns the previous hot cursor
7181 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
7183 HCURSOR oldCursor = infoPtr->hHotCursor;
7185 infoPtr->hHotCursor = hCursor;
7193 * Sets the hot item index.
7196 * [I] infoPtr : valid pointer to the listview structure
7197 * [I] iIndex : index
7200 * SUCCESS : previous hot item index
7201 * FAILURE : -1 (no hot item)
7203 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
7205 INT iOldIndex = infoPtr->nHotItem;
7207 infoPtr->nHotItem = iIndex;
7215 * Sets the amount of time the cursor must hover over an item before it is selected.
7218 * [I] infoPtr : valid pointer to the listview structure
7219 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7222 * Returns the previous hover time
7224 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
7226 DWORD oldHoverTime = infoPtr->dwHoverTime;
7228 infoPtr->dwHoverTime = dwHoverTime;
7230 return oldHoverTime;
7235 * Sets spacing for icons of LVS_ICON style.
7238 * [I] infoPtr : valid pointer to the listview structure
7239 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7240 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7243 * MAKELONG(oldcx, oldcy)
7245 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
7247 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7248 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7250 TRACE("requested=(%d,%d)\n", cx, cy);
7252 /* this is supported only for LVS_ICON style */
7253 if (uView != LVS_ICON) return oldspacing;
7255 /* set to defaults, if instructed to */
7256 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
7257 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
7259 /* if 0 then compute width
7260 * FIXME: Should scan each item and determine max width of
7261 * icon or label, then make that the width */
7263 cx = infoPtr->iconSpacing.cx;
7265 /* if 0 then compute height */
7267 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
7268 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
7271 infoPtr->iconSpacing.cx = cx;
7272 infoPtr->iconSpacing.cy = cy;
7274 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7275 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
7276 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7277 infoPtr->ntmHeight);
7279 /* these depend on the iconSpacing */
7280 LISTVIEW_UpdateItemSize(infoPtr);
7285 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
7289 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
7296 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
7297 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7306 * [I] infoPtr : valid pointer to the listview structure
7307 * [I] nType : image list type
7308 * [I] himl : image list handle
7311 * SUCCESS : old image list
7314 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7316 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7317 INT oldHeight = infoPtr->nItemHeight;
7318 HIMAGELIST himlOld = 0;
7320 TRACE("(nType=%d, himl=%p\n", nType, himl);
7325 himlOld = infoPtr->himlNormal;
7326 infoPtr->himlNormal = himl;
7327 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7328 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7332 himlOld = infoPtr->himlSmall;
7333 infoPtr->himlSmall = himl;
7334 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7338 himlOld = infoPtr->himlState;
7339 infoPtr->himlState = himl;
7340 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7341 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7345 ERR("Unknown icon type=%d\n", nType);
7349 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7350 if (infoPtr->nItemHeight != oldHeight)
7351 LISTVIEW_UpdateScroll(infoPtr);
7358 * Preallocates memory (does *not* set the actual count of items !)
7361 * [I] infoPtr : valid pointer to the listview structure
7362 * [I] nItems : item count (projected number of items to allocate)
7363 * [I] dwFlags : update flags
7369 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7371 TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
7373 if (infoPtr->dwStyle & LVS_OWNERDATA)
7375 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7376 INT nOldCount = infoPtr->nItemCount;
7378 if (nItems < nOldCount)
7380 RANGE range = { nItems, nOldCount };
7381 ranges_del(infoPtr->selectionRanges, range);
7382 if (infoPtr->nFocusedItem >= nItems)
7384 infoPtr->nFocusedItem = -1;
7385 SetRectEmpty(&infoPtr->rcFocus);
7389 infoPtr->nItemCount = nItems;
7390 LISTVIEW_UpdateScroll(infoPtr);
7392 /* the flags are valid only in ownerdata report and list modes */
7393 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7395 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7396 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7398 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7399 LISTVIEW_InvalidateList(infoPtr);
7406 LISTVIEW_GetOrigin(infoPtr, &Origin);
7407 nFrom = min(nOldCount, nItems);
7408 nTo = max(nOldCount, nItems);
7410 if (uView == LVS_REPORT)
7413 rcErase.top = nFrom * infoPtr->nItemHeight;
7414 rcErase.right = infoPtr->nItemWidth;
7415 rcErase.bottom = nTo * infoPtr->nItemHeight;
7416 OffsetRect(&rcErase, Origin.x, Origin.y);
7417 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7418 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7422 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7424 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7425 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7426 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7427 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7428 OffsetRect(&rcErase, Origin.x, Origin.y);
7429 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7430 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7432 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7434 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7435 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7436 OffsetRect(&rcErase, Origin.x, Origin.y);
7437 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7438 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7444 /* According to MSDN for non-LVS_OWNERDATA this is just
7445 * a performance issue. The control allocates its internal
7446 * data structures for the number of items specified. It
7447 * cuts down on the number of memory allocations. Therefore
7448 * we will just issue a WARN here
7450 WARN("for non-ownerdata performance option not implemented.\n");
7458 * Sets the position of an item.
7461 * [I] infoPtr : valid pointer to the listview structure
7462 * [I] nItem : item index
7463 * [I] pt : coordinate
7469 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7471 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7474 TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7476 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7477 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7479 LISTVIEW_GetOrigin(infoPtr, &Origin);
7481 /* This point value seems to be an undocumented feature.
7482 * The best guess is that it means either at the origin,
7483 * or at true beginning of the list. I will assume the origin. */
7484 if ((pt.x == -1) && (pt.y == -1))
7487 if (uView == LVS_ICON)
7489 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7490 pt.y -= ICON_TOP_PADDING;
7495 infoPtr->bAutoarrange = FALSE;
7497 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7502 * Sets the state of one or many items.
7505 * [I] infoPtr : valid pointer to the listview structure
7506 * [I] nItem : item index
7507 * [I] lpLVItem : item or subitem info
7513 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7515 BOOL bResult = TRUE;
7518 lvItem.iItem = nItem;
7519 lvItem.iSubItem = 0;
7520 lvItem.mask = LVIF_STATE;
7521 lvItem.state = lpLVItem->state;
7522 lvItem.stateMask = lpLVItem->stateMask;
7523 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7527 /* apply to all items */
7528 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7529 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7532 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7535 * Update selection mark
7537 * Investigation on windows 2k showed that selection mark was updated
7538 * whenever a new selection was made, but if the selected item was
7539 * unselected it was not updated.
7541 * we are probably still not 100% accurate, but this at least sets the
7542 * proper selection mark when it is needed
7545 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7546 (infoPtr->nSelectionMark == -1))
7549 for (i = 0; i < infoPtr->nItemCount; i++)
7551 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7553 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7555 infoPtr->nSelectionMark = i;
7559 else if (ranges_contain(infoPtr->selectionRanges, i))
7561 infoPtr->nSelectionMark = i;
7572 * Sets the text of an item or subitem.
7575 * [I] hwnd : window handle
7576 * [I] nItem : item index
7577 * [I] lpLVItem : item or subitem info
7578 * [I] isW : TRUE if input is Unicode
7584 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7588 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7590 lvItem.iItem = nItem;
7591 lvItem.iSubItem = lpLVItem->iSubItem;
7592 lvItem.mask = LVIF_TEXT;
7593 lvItem.pszText = lpLVItem->pszText;
7594 lvItem.cchTextMax = lpLVItem->cchTextMax;
7596 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7598 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7603 * Set item index that marks the start of a multiple selection.
7606 * [I] infoPtr : valid pointer to the listview structure
7607 * [I] nIndex : index
7610 * Index number or -1 if there is no selection mark.
7612 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7614 INT nOldIndex = infoPtr->nSelectionMark;
7616 TRACE("(nIndex=%d)\n", nIndex);
7618 infoPtr->nSelectionMark = nIndex;
7625 * Sets the text background color.
7628 * [I] infoPtr : valid pointer to the listview structure
7629 * [I] clrTextBk : text background color
7635 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7637 TRACE("(clrTextBk=%x)\n", clrTextBk);
7639 if (infoPtr->clrTextBk != clrTextBk)
7641 infoPtr->clrTextBk = clrTextBk;
7642 LISTVIEW_InvalidateList(infoPtr);
7650 * Sets the text foreground color.
7653 * [I] infoPtr : valid pointer to the listview structure
7654 * [I] clrText : text color
7660 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7662 TRACE("(clrText=%x)\n", clrText);
7664 if (infoPtr->clrText != clrText)
7666 infoPtr->clrText = clrText;
7667 LISTVIEW_InvalidateList(infoPtr);
7675 * Determines which listview item is located at the specified position.
7678 * [I] infoPtr : valid pointer to the listview structure
7679 * [I] hwndNewToolTip : handle to new ToolTip
7684 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7686 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7687 infoPtr->hwndToolTip = hwndNewToolTip;
7688 return hwndOldToolTip;
7693 * sets the Unicode character format flag for the control
7695 * [I] infoPtr :valid pointer to the listview structure
7696 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
7699 * Old Unicode Format
7701 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL fUnicode)
7703 BOOL rc = infoPtr->notifyFormat;
7704 infoPtr->notifyFormat = (fUnicode)?NFR_UNICODE:NFR_ANSI;
7708 /* LISTVIEW_SetWorkAreas */
7712 * Callback internally used by LISTVIEW_SortItems()
7715 * [I] first : pointer to first ITEM_INFO to compare
7716 * [I] second : pointer to second ITEM_INFO to compare
7717 * [I] lParam : HWND of control
7720 * if first comes before second : negative
7721 * if first comes after second : positive
7722 * if first and second are equivalent : zero
7724 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7726 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7727 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7728 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7730 /* Forward the call to the client defined callback */
7731 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7736 * Sorts the listview items.
7739 * [I] infoPtr : valid pointer to the listview structure
7740 * [I] pfnCompare : application-defined value
7741 * [I] lParamSort : pointer to comparison callback
7747 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7749 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7752 LPVOID selectionMarkItem;
7756 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7758 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7760 if (!pfnCompare) return FALSE;
7761 if (!infoPtr->hdpaItems) return FALSE;
7763 /* if there are 0 or 1 items, there is no need to sort */
7764 if (infoPtr->nItemCount < 2) return TRUE;
7766 if (infoPtr->nFocusedItem >= 0)
7768 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7769 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7770 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7772 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7773 /* clear the lpItem->state for non-selected ones */
7774 /* remove the selection ranges */
7776 infoPtr->pfnCompare = pfnCompare;
7777 infoPtr->lParamSort = lParamSort;
7778 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7780 /* Adjust selections and indices so that they are the way they should
7781 * be after the sort (otherwise, the list items move around, but
7782 * whatever is at the item's previous original position will be
7785 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7786 for (i=0; i < infoPtr->nItemCount; i++)
7788 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7789 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7791 if (lpItem->state & LVIS_SELECTED)
7793 item.state = LVIS_SELECTED;
7794 item.stateMask = LVIS_SELECTED;
7795 LISTVIEW_SetItemState(infoPtr, i, &item);
7797 if (lpItem->state & LVIS_FOCUSED)
7799 infoPtr->nFocusedItem = i;
7800 lpItem->state &= ~LVIS_FOCUSED;
7803 if (selectionMarkItem != NULL)
7804 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7805 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7807 /* refresh the display */
7808 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7809 LISTVIEW_InvalidateList(infoPtr);
7816 * Update theme handle after a theme change.
7819 * [I] infoPtr : valid pointer to the listview structure
7823 * FAILURE : something else
7825 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr)
7827 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7828 CloseThemeData(theme);
7829 OpenThemeData(infoPtr->hwndSelf, themeClass);
7835 * Updates an items or rearranges the listview control.
7838 * [I] infoPtr : valid pointer to the listview structure
7839 * [I] nItem : item index
7845 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7847 TRACE("(nItem=%d)\n", nItem);
7849 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7851 /* rearrange with default alignment style */
7852 if (is_autoarrange(infoPtr))
7853 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7855 LISTVIEW_InvalidateItem(infoPtr, nItem);
7862 * Draw the track line at the place defined in the infoPtr structure.
7863 * The line is drawn with a XOR pen so drawing the line for the second time
7864 * in the same place erases the line.
7867 * [I] infoPtr : valid pointer to the listview structure
7873 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
7879 if (infoPtr->xTrackLine == -1)
7882 if (!(hdc = GetDC(infoPtr->hwndSelf)))
7884 hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
7885 oldROP = SetROP2(hdc, R2_XORPEN);
7886 MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
7887 LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
7888 SetROP2(hdc, oldROP);
7889 SelectObject(hdc, hOldPen);
7890 ReleaseDC(infoPtr->hwndSelf, hdc);
7896 * Called when an edit control should be displayed. This function is called after
7897 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
7900 * [I] hwnd : Handle to the listview
7901 * [I] uMsg : WM_TIMER (ignored)
7902 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
7903 * [I] dwTimer : The elapsed time (ignored)
7908 static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
7910 DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
7911 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
7913 KillTimer(hwnd, idEvent);
7914 editItem->fEnabled = FALSE;
7915 /* check if the item is still selected */
7916 if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
7917 LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
7922 * Creates the listview control - the WM_NCCREATE phase.
7925 * [I] hwnd : window handle
7926 * [I] lpcs : the create parameters
7932 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
7934 LISTVIEW_INFO *infoPtr;
7937 TRACE("(lpcs=%p)\n", lpcs);
7939 /* initialize info pointer */
7940 infoPtr = Alloc(sizeof(LISTVIEW_INFO));
7941 if (!infoPtr) return FALSE;
7943 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
7945 infoPtr->hwndSelf = hwnd;
7946 infoPtr->dwStyle = lpcs->style; /* Note: may be changed in WM_CREATE */
7947 /* determine the type of structures to use */
7948 infoPtr->hwndNotify = lpcs->hwndParent;
7949 /* infoPtr->notifyFormat will be filled in WM_CREATE */
7951 /* initialize color information */
7952 infoPtr->clrBk = CLR_NONE;
7953 infoPtr->clrText = CLR_DEFAULT;
7954 infoPtr->clrTextBk = CLR_DEFAULT;
7955 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7957 /* set default values */
7958 infoPtr->nFocusedItem = -1;
7959 infoPtr->nSelectionMark = -1;
7960 infoPtr->nHotItem = -1;
7961 infoPtr->bRedraw = TRUE;
7962 infoPtr->bNoItemMetrics = TRUE;
7963 infoPtr->bDoChangeNotify = TRUE;
7964 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7965 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7966 infoPtr->nEditLabelItem = -1;
7967 infoPtr->dwHoverTime = -1; /* default system hover time */
7968 infoPtr->nMeasureItemHeight = 0;
7969 infoPtr->xTrackLine = -1; /* no track line */
7970 infoPtr->itemEdit.fEnabled = FALSE;
7972 /* get default font (icon title) */
7973 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7974 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7975 infoPtr->hFont = infoPtr->hDefaultFont;
7976 LISTVIEW_SaveTextMetrics(infoPtr);
7978 /* allocate memory for the data structure */
7979 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7980 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7981 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7982 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7983 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7987 DestroyWindow(infoPtr->hwndHeader);
7988 ranges_destroy(infoPtr->selectionRanges);
7989 DPA_Destroy(infoPtr->hdpaItems);
7990 DPA_Destroy(infoPtr->hdpaPosX);
7991 DPA_Destroy(infoPtr->hdpaPosY);
7992 DPA_Destroy(infoPtr->hdpaColumns);
7999 * Creates the listview control - the WM_CREATE phase. Most of the data is
8000 * already set up in LISTVIEW_NCCreate
8003 * [I] hwnd : window handle
8004 * [I] lpcs : the create parameters
8010 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
8012 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8013 UINT uView = lpcs->style & LVS_TYPEMASK;
8014 DWORD dFlags = WS_CHILD | HDS_HORZ | HDS_FULLDRAG | HDS_DRAGDROP;
8016 TRACE("(lpcs=%p)\n", lpcs);
8018 infoPtr->dwStyle = lpcs->style;
8019 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
8020 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
8022 /* setup creation flags */
8023 dFlags |= (LVS_NOSORTHEADER & lpcs->style) ? 0 : HDS_BUTTONS;
8024 dFlags |= (LVS_NOCOLUMNHEADER & lpcs->style) ? HDS_HIDDEN : 0;
8027 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL, dFlags,
8028 0, 0, 0, 0, hwnd, NULL,
8029 lpcs->hInstance, NULL);
8030 if (!infoPtr->hwndHeader) return -1;
8032 /* set header unicode format */
8033 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
8035 /* set header font */
8036 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
8038 /* init item size to avoid division by 0 */
8039 LISTVIEW_UpdateItemSize (infoPtr);
8041 if (uView == LVS_REPORT)
8043 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
8045 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8047 LISTVIEW_UpdateSize(infoPtr);
8048 LISTVIEW_UpdateScroll(infoPtr);
8051 OpenThemeData(hwnd, themeClass);
8053 /* initialize the icon sizes */
8054 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
8055 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
8061 * Destroys the listview control.
8064 * [I] infoPtr : valid pointer to the listview structure
8070 static LRESULT LISTVIEW_Destroy(const LISTVIEW_INFO *infoPtr)
8072 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8073 CloseThemeData(theme);
8079 * Enables the listview control.
8082 * [I] infoPtr : valid pointer to the listview structure
8083 * [I] bEnable : specifies whether to enable or disable the window
8089 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr, BOOL bEnable)
8091 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
8092 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8098 * Erases the background of the listview control.
8101 * [I] infoPtr : valid pointer to the listview structure
8102 * [I] hdc : device context handle
8108 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
8112 TRACE("(hdc=%p)\n", hdc);
8114 if (!GetClipBox(hdc, &rc)) return FALSE;
8116 /* for double buffered controls we need to do this during refresh */
8117 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
8119 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
8125 * Helper function for LISTVIEW_[HV]Scroll *only*.
8126 * Performs vertical/horizontal scrolling by a give amount.
8129 * [I] infoPtr : valid pointer to the listview structure
8130 * [I] dx : amount of horizontal scroll
8131 * [I] dy : amount of vertical scroll
8133 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
8135 /* now we can scroll the list */
8136 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
8137 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
8138 /* if we have focus, adjust rect */
8139 OffsetRect(&infoPtr->rcFocus, dx, dy);
8140 UpdateWindow(infoPtr->hwndSelf);
8145 * Performs vertical scrolling.
8148 * [I] infoPtr : valid pointer to the listview structure
8149 * [I] nScrollCode : scroll code
8150 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8151 * [I] hScrollWnd : scrollbar control window handle
8157 * SB_LINEUP/SB_LINEDOWN:
8158 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8159 * for LVS_REPORT is 1 line
8160 * for LVS_LIST cannot occur
8163 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8164 INT nScrollDiff, HWND hScrollWnd)
8166 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8167 INT nOldScrollPos, nNewScrollPos;
8168 SCROLLINFO scrollInfo;
8171 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8172 debugscrollcode(nScrollCode), nScrollDiff);
8174 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8176 scrollInfo.cbSize = sizeof(SCROLLINFO);
8177 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8179 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
8181 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
8183 nOldScrollPos = scrollInfo.nPos;
8184 switch (nScrollCode)
8190 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
8194 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
8198 nScrollDiff = -scrollInfo.nPage;
8202 nScrollDiff = scrollInfo.nPage;
8205 case SB_THUMBPOSITION:
8207 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8214 /* quit right away if pos isn't changing */
8215 if (nScrollDiff == 0) return 0;
8217 /* calculate new position, and handle overflows */
8218 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8219 if (nScrollDiff > 0) {
8220 if (nNewScrollPos < nOldScrollPos ||
8221 nNewScrollPos > scrollInfo.nMax)
8222 nNewScrollPos = scrollInfo.nMax;
8224 if (nNewScrollPos > nOldScrollPos ||
8225 nNewScrollPos < scrollInfo.nMin)
8226 nNewScrollPos = scrollInfo.nMin;
8229 /* set the new position, and reread in case it changed */
8230 scrollInfo.fMask = SIF_POS;
8231 scrollInfo.nPos = nNewScrollPos;
8232 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
8234 /* carry on only if it really changed */
8235 if (nNewScrollPos == nOldScrollPos) return 0;
8237 /* now adjust to client coordinates */
8238 nScrollDiff = nOldScrollPos - nNewScrollPos;
8239 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
8241 /* and scroll the window */
8242 scroll_list(infoPtr, 0, nScrollDiff);
8249 * Performs horizontal scrolling.
8252 * [I] infoPtr : valid pointer to the listview structure
8253 * [I] nScrollCode : scroll code
8254 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8255 * [I] hScrollWnd : scrollbar control window handle
8261 * SB_LINELEFT/SB_LINERIGHT:
8262 * for LVS_ICON, LVS_SMALLICON 1 pixel
8263 * for LVS_REPORT is 1 pixel
8264 * for LVS_LIST is 1 column --> which is a 1 because the
8265 * scroll is based on columns not pixels
8268 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8269 INT nScrollDiff, HWND hScrollWnd)
8271 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8272 INT nOldScrollPos, nNewScrollPos;
8273 SCROLLINFO scrollInfo;
8275 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8276 debugscrollcode(nScrollCode), nScrollDiff);
8278 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8280 scrollInfo.cbSize = sizeof(SCROLLINFO);
8281 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8283 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
8285 nOldScrollPos = scrollInfo.nPos;
8287 switch (nScrollCode)
8301 nScrollDiff = -scrollInfo.nPage;
8305 nScrollDiff = scrollInfo.nPage;
8308 case SB_THUMBPOSITION:
8310 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8317 /* quit right away if pos isn't changing */
8318 if (nScrollDiff == 0) return 0;
8320 /* calculate new position, and handle overflows */
8321 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8322 if (nScrollDiff > 0) {
8323 if (nNewScrollPos < nOldScrollPos ||
8324 nNewScrollPos > scrollInfo.nMax)
8325 nNewScrollPos = scrollInfo.nMax;
8327 if (nNewScrollPos > nOldScrollPos ||
8328 nNewScrollPos < scrollInfo.nMin)
8329 nNewScrollPos = scrollInfo.nMin;
8332 /* set the new position, and reread in case it changed */
8333 scrollInfo.fMask = SIF_POS;
8334 scrollInfo.nPos = nNewScrollPos;
8335 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
8337 /* carry on only if it really changed */
8338 if (nNewScrollPos == nOldScrollPos) return 0;
8340 if(uView == LVS_REPORT)
8341 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
8343 /* now adjust to client coordinates */
8344 nScrollDiff = nOldScrollPos - nNewScrollPos;
8345 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
8347 /* and scroll the window */
8348 scroll_list(infoPtr, nScrollDiff, 0);
8353 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
8355 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8356 INT gcWheelDelta = 0;
8357 INT pulScrollLines = 3;
8358 SCROLLINFO scrollInfo;
8360 TRACE("(wheelDelta=%d)\n", wheelDelta);
8362 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8363 gcWheelDelta -= wheelDelta;
8365 scrollInfo.cbSize = sizeof(SCROLLINFO);
8366 scrollInfo.fMask = SIF_POS;
8373 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8374 * should be fixed in the future.
8376 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
8377 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8381 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8383 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
8384 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8385 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
8390 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8401 * [I] infoPtr : valid pointer to the listview structure
8402 * [I] nVirtualKey : virtual key
8403 * [I] lKeyData : key data
8408 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8410 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8411 HWND hwndSelf = infoPtr->hwndSelf;
8413 NMLVKEYDOWN nmKeyDown;
8415 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
8417 /* send LVN_KEYDOWN notification */
8418 nmKeyDown.wVKey = nVirtualKey;
8419 nmKeyDown.flags = 0;
8420 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8421 if (!IsWindow(hwndSelf))
8424 switch (nVirtualKey)
8427 nItem = infoPtr->nFocusedItem;
8428 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
8429 toggle_checkbox_state(infoPtr, infoPtr->nFocusedItem);
8433 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8435 if (!notify(infoPtr, NM_RETURN)) return 0;
8436 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8441 if (infoPtr->nItemCount > 0)
8446 if (infoPtr->nItemCount > 0)
8447 nItem = infoPtr->nItemCount - 1;
8451 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8455 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8459 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8463 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8467 if (uView == LVS_REPORT)
8469 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8470 if (infoPtr->nFocusedItem == topidx)
8471 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8476 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8477 * LISTVIEW_GetCountPerRow(infoPtr);
8478 if(nItem < 0) nItem = 0;
8482 if (uView == LVS_REPORT)
8484 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8485 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8486 if (infoPtr->nFocusedItem == topidx + cnt - 1)
8487 nItem = infoPtr->nFocusedItem + cnt - 1;
8489 nItem = topidx + cnt - 1;
8492 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8493 * LISTVIEW_GetCountPerRow(infoPtr);
8494 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8498 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8499 LISTVIEW_KeySelection(infoPtr, nItem);
8509 * [I] infoPtr : valid pointer to the listview structure
8514 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8518 /* if we did not have the focus, there's nothing to do */
8519 if (!infoPtr->bFocus) return 0;
8521 /* send NM_KILLFOCUS notification */
8522 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8524 /* if we have a focus rectagle, get rid of it */
8525 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8527 /* set window focus flag */
8528 infoPtr->bFocus = FALSE;
8530 /* invalidate the selected items before resetting focus flag */
8531 LISTVIEW_InvalidateSelectedItems(infoPtr);
8538 * Processes double click messages (left mouse button).
8541 * [I] infoPtr : valid pointer to the listview structure
8542 * [I] wKey : key flag
8543 * [I] x,y : mouse coordinate
8548 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8550 LVHITTESTINFO htInfo;
8552 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8554 /* Cancel the item edition if any */
8555 if (infoPtr->itemEdit.fEnabled)
8557 KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
8558 infoPtr->itemEdit.fEnabled = FALSE;
8561 /* send NM_RELEASEDCAPTURE notification */
8562 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8567 /* send NM_DBLCLK notification */
8568 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8569 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8571 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8572 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8579 * Processes mouse down messages (left mouse button).
8582 * infoPtr [I ] valid pointer to the listview structure
8583 * wKey [I ] key flag
8584 * x,y [I ] mouse coordinate
8589 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8591 LVHITTESTINFO lvHitTestInfo;
8592 static BOOL bGroupSelect = TRUE;
8593 BOOL bReceivedFocus = FALSE;
8594 POINT pt = { x, y };
8597 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8599 /* send NM_RELEASEDCAPTURE notification */
8600 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8602 if (!infoPtr->bFocus)
8603 bReceivedFocus = TRUE;
8605 /* set left button down flag and record the click position */
8606 infoPtr->bLButtonDown = TRUE;
8607 infoPtr->ptClickPos = pt;
8609 lvHitTestInfo.pt.x = x;
8610 lvHitTestInfo.pt.y = y;
8612 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8613 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
8614 infoPtr->nEditLabelItem = -1;
8615 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8617 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8619 toggle_checkbox_state(infoPtr, nItem);
8623 if (infoPtr->dwStyle & LVS_SINGLESEL)
8625 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8626 infoPtr->nEditLabelItem = nItem;
8628 LISTVIEW_SetSelection(infoPtr, nItem);
8632 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8636 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8637 LISTVIEW_SetItemFocus(infoPtr, nItem);
8638 infoPtr->nSelectionMark = nItem;
8644 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8645 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8647 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8648 infoPtr->nSelectionMark = nItem;
8651 else if (wKey & MK_CONTROL)
8655 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8657 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8658 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8659 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8660 infoPtr->nSelectionMark = nItem;
8662 else if (wKey & MK_SHIFT)
8664 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8668 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8669 infoPtr->nEditLabelItem = nItem;
8671 /* set selection (clears other pre-existing selections) */
8672 LISTVIEW_SetSelection(infoPtr, nItem);
8678 /* remove all selections */
8679 LISTVIEW_DeselectAll(infoPtr);
8684 infoPtr->nEditLabelItem = -1;
8691 * Processes mouse up messages (left mouse button).
8694 * infoPtr [I ] valid pointer to the listview structure
8695 * wKey [I ] key flag
8696 * x,y [I ] mouse coordinate
8701 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8703 LVHITTESTINFO lvHitTestInfo;
8705 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8707 if (!infoPtr->bLButtonDown) return 0;
8709 lvHitTestInfo.pt.x = x;
8710 lvHitTestInfo.pt.y = y;
8712 /* send NM_CLICK notification */
8713 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8714 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8716 /* set left button flag */
8717 infoPtr->bLButtonDown = FALSE;
8719 /* if we clicked on a selected item, edit the label */
8720 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8722 /* we want to make sure the user doesn't want to do a double click. So we will
8723 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
8725 infoPtr->itemEdit.fEnabled = TRUE;
8726 infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
8727 SetTimer(infoPtr->hwndSelf,
8728 (UINT_PTR)&infoPtr->itemEdit,
8729 GetDoubleClickTime(),
8730 LISTVIEW_DelayedEditItem);
8733 if (!infoPtr->bFocus)
8734 SetFocus(infoPtr->hwndSelf);
8741 * Destroys the listview control (called after WM_DESTROY).
8744 * [I] infoPtr : valid pointer to the listview structure
8749 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8753 /* delete all items */
8754 LISTVIEW_DeleteAllItems(infoPtr, TRUE);
8756 /* destroy data structure */
8757 DPA_Destroy(infoPtr->hdpaItems);
8758 DPA_Destroy(infoPtr->hdpaPosX);
8759 DPA_Destroy(infoPtr->hdpaPosY);
8760 DPA_Destroy(infoPtr->hdpaColumns);
8761 ranges_destroy(infoPtr->selectionRanges);
8763 /* destroy image lists */
8764 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8766 if (infoPtr->himlNormal)
8767 ImageList_Destroy(infoPtr->himlNormal);
8768 if (infoPtr->himlSmall)
8769 ImageList_Destroy(infoPtr->himlSmall);
8770 if (infoPtr->himlState)
8771 ImageList_Destroy(infoPtr->himlState);
8774 /* destroy font, bkgnd brush */
8776 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8777 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8779 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8781 /* free listview info pointer*/
8789 * Handles notifications from header.
8792 * [I] infoPtr : valid pointer to the listview structure
8793 * [I] nCtrlId : control identifier
8794 * [I] lpnmh : notification information
8799 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8801 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8802 HWND hwndSelf = infoPtr->hwndSelf;
8804 TRACE("(lpnmh=%p)\n", lpnmh);
8806 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8808 switch (lpnmh->hdr.code)
8813 COLUMN_INFO *lpColumnInfo;
8817 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8820 /* remove the old line (if any) */
8821 LISTVIEW_DrawTrackLine(infoPtr);
8823 /* compute & draw the new line */
8824 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8825 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
8826 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8827 infoPtr->xTrackLine = x + ptOrigin.x;
8828 LISTVIEW_DrawTrackLine(infoPtr);
8834 /* remove the track line (if any) */
8835 LISTVIEW_DrawTrackLine(infoPtr);
8836 infoPtr->xTrackLine = -1;
8840 FIXME("Changing column order not implemented\n");
8843 case HDN_ITEMCHANGINGW:
8844 case HDN_ITEMCHANGINGA:
8845 return notify_forward_header(infoPtr, lpnmh);
8847 case HDN_ITEMCHANGEDW:
8848 case HDN_ITEMCHANGEDA:
8850 COLUMN_INFO *lpColumnInfo;
8853 notify_forward_header(infoPtr, lpnmh);
8854 if (!IsWindow(hwndSelf))
8857 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8861 hdi.mask = HDI_WIDTH;
8862 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, &hdi)) return 0;
8866 cxy = lpnmh->pitem->cxy;
8868 /* determine how much we change since the last know position */
8869 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8870 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8873 lpColumnInfo->rcHeader.right += dx;
8874 if (lpnmh->iItem + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
8875 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8878 /* only needs to update the scrolls */
8879 infoPtr->nItemWidth += dx;
8880 LISTVIEW_UpdateScroll(infoPtr);
8882 LISTVIEW_UpdateItemSize(infoPtr);
8883 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8886 RECT rcCol = lpColumnInfo->rcHeader;
8888 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8889 OffsetRect(&rcCol, ptOrigin.x, 0);
8891 rcCol.top = infoPtr->rcList.top;
8892 rcCol.bottom = infoPtr->rcList.bottom;
8894 /* resizing left-aligned columns leaves most of the left side untouched */
8895 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8897 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
8900 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
8903 /* when shrinking the last column clear the now unused field */
8904 if (lpnmh->iItem == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1 && dx < 0)
8907 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8913 case HDN_ITEMCLICKW:
8914 case HDN_ITEMCLICKA:
8916 /* Handle sorting by Header Column */
8919 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8921 nmlv.iSubItem = lpnmh->iItem;
8922 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8926 case HDN_DIVIDERDBLCLICKW:
8927 case HDN_DIVIDERDBLCLICKA:
8928 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
8937 * Paint non-client area of control.
8940 * [I] infoPtr : valid pointer to the listview structureof the sender
8941 * [I] region : update region
8944 * TRUE - frame was painted
8945 * FALSE - call default window proc
8947 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
8949 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
8953 int cxEdge = GetSystemMetrics (SM_CXEDGE),
8954 cyEdge = GetSystemMetrics (SM_CYEDGE);
8956 if (!theme) return FALSE;
8958 GetWindowRect(infoPtr->hwndSelf, &r);
8960 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
8961 r.right - cxEdge, r.bottom - cyEdge);
8962 if (region != (HRGN)1)
8963 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
8964 OffsetRect(&r, -r.left, -r.top);
8966 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
8967 OffsetRect(&r, -r.left, -r.top);
8969 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
8970 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
8971 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
8972 ReleaseDC(infoPtr->hwndSelf, dc);
8974 /* Call default proc to get the scrollbars etc. painted */
8975 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
8982 * Determines the type of structure to use.
8985 * [I] infoPtr : valid pointer to the listview structureof the sender
8986 * [I] hwndFrom : listview window handle
8987 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8992 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8994 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8996 if (nCommand == NF_REQUERY)
8997 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8999 return infoPtr->notifyFormat;
9004 * Paints/Repaints the listview control.
9007 * [I] infoPtr : valid pointer to the listview structure
9008 * [I] hdc : device context handle
9013 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
9015 TRACE("(hdc=%p)\n", hdc);
9017 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
9019 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9021 infoPtr->bNoItemMetrics = FALSE;
9022 LISTVIEW_UpdateItemSize(infoPtr);
9023 if (uView == LVS_ICON || uView == LVS_SMALLICON)
9024 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9025 LISTVIEW_UpdateScroll(infoPtr);
9028 UpdateWindow(infoPtr->hwndHeader);
9031 LISTVIEW_Refresh(infoPtr, hdc, NULL);
9036 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
9038 LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
9039 EndPaint(infoPtr->hwndSelf, &ps);
9048 * Paints/Repaints the listview control.
9051 * [I] infoPtr : valid pointer to the listview structure
9052 * [I] hdc : device context handle
9053 * [I] options : drawing options
9058 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
9060 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
9062 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
9065 if (options & PRF_ERASEBKGND)
9066 LISTVIEW_EraseBkgnd(infoPtr, hdc);
9068 if (options & PRF_CLIENT)
9069 LISTVIEW_Paint(infoPtr, hdc);
9077 * Processes double click messages (right mouse button).
9080 * [I] infoPtr : valid pointer to the listview structure
9081 * [I] wKey : key flag
9082 * [I] x,y : mouse coordinate
9087 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9089 LVHITTESTINFO lvHitTestInfo;
9091 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9093 /* send NM_RELEASEDCAPTURE notification */
9094 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9096 /* send NM_RDBLCLK notification */
9097 lvHitTestInfo.pt.x = x;
9098 lvHitTestInfo.pt.y = y;
9099 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9100 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
9107 * Processes mouse down messages (right mouse button).
9110 * [I] infoPtr : valid pointer to the listview structure
9111 * [I] wKey : key flag
9112 * [I] x,y : mouse coordinate
9117 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9119 LVHITTESTINFO lvHitTestInfo;
9122 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9124 /* send NM_RELEASEDCAPTURE notification */
9125 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9127 /* make sure the listview control window has the focus */
9128 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
9130 /* set right button down flag */
9131 infoPtr->bRButtonDown = TRUE;
9133 /* determine the index of the selected item */
9134 lvHitTestInfo.pt.x = x;
9135 lvHitTestInfo.pt.y = y;
9136 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
9138 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9140 LISTVIEW_SetItemFocus(infoPtr, nItem);
9141 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9142 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9143 LISTVIEW_SetSelection(infoPtr, nItem);
9147 LISTVIEW_DeselectAll(infoPtr);
9155 * Processes mouse up messages (right mouse button).
9158 * [I] infoPtr : valid pointer to the listview structure
9159 * [I] wKey : key flag
9160 * [I] x,y : mouse coordinate
9165 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9167 LVHITTESTINFO lvHitTestInfo;
9170 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9172 if (!infoPtr->bRButtonDown) return 0;
9174 /* set button flag */
9175 infoPtr->bRButtonDown = FALSE;
9177 /* Send NM_RClICK notification */
9178 lvHitTestInfo.pt.x = x;
9179 lvHitTestInfo.pt.y = y;
9180 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9181 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
9183 /* Change to screen coordinate for WM_CONTEXTMENU */
9184 pt = lvHitTestInfo.pt;
9185 ClientToScreen(infoPtr->hwndSelf, &pt);
9187 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9188 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
9189 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
9200 * [I] infoPtr : valid pointer to the listview structure
9201 * [I] hwnd : window handle of window containing the cursor
9202 * [I] nHittest : hit-test code
9203 * [I] wMouseMsg : ideintifier of the mouse message
9206 * TRUE if cursor is set
9209 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
9211 LVHITTESTINFO lvHitTestInfo;
9213 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
9215 if(!infoPtr->hHotCursor) return FALSE;
9217 GetCursorPos(&lvHitTestInfo.pt);
9218 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
9220 SetCursor(infoPtr->hHotCursor);
9230 * [I] infoPtr : valid pointer to the listview structure
9231 * [I] hwndLoseFocus : handle of previously focused window
9236 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
9238 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
9240 /* if we have the focus already, there's nothing to do */
9241 if (infoPtr->bFocus) return 0;
9243 /* send NM_SETFOCUS notification */
9244 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
9246 /* set window focus flag */
9247 infoPtr->bFocus = TRUE;
9249 /* put the focus rect back on */
9250 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
9252 /* redraw all visible selected items */
9253 LISTVIEW_InvalidateSelectedItems(infoPtr);
9263 * [I] infoPtr : valid pointer to the listview structure
9264 * [I] fRedraw : font handle
9265 * [I] fRedraw : redraw flag
9270 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
9272 HFONT oldFont = infoPtr->hFont;
9274 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
9276 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9277 if (infoPtr->hFont == oldFont) return 0;
9279 LISTVIEW_SaveTextMetrics(infoPtr);
9281 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
9283 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
9284 LISTVIEW_UpdateSize(infoPtr);
9285 LISTVIEW_UpdateScroll(infoPtr);
9288 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
9295 * Message handling for WM_SETREDRAW.
9296 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9299 * [I] infoPtr : valid pointer to the listview structure
9300 * [I] bRedraw: state of redraw flag
9303 * DefWinProc return value
9305 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
9307 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
9309 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9310 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
9312 infoPtr->bRedraw = bRedraw;
9314 if(!bRedraw) return 0;
9316 if (is_autoarrange(infoPtr))
9317 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9318 LISTVIEW_UpdateScroll(infoPtr);
9320 /* despite what the WM_SETREDRAW docs says, apps expect us
9321 * to invalidate the listview here... stupid! */
9322 LISTVIEW_InvalidateList(infoPtr);
9329 * Resizes the listview control. This function processes WM_SIZE
9330 * messages. At this time, the width and height are not used.
9333 * [I] infoPtr : valid pointer to the listview structure
9334 * [I] Width : new width
9335 * [I] Height : new height
9340 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
9342 RECT rcOld = infoPtr->rcList;
9344 TRACE("(width=%d, height=%d)\n", Width, Height);
9346 LISTVIEW_UpdateSize(infoPtr);
9347 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
9349 /* do not bother with display related stuff if we're not redrawing */
9350 if (!is_redrawing(infoPtr)) return 0;
9352 if (is_autoarrange(infoPtr))
9353 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9355 LISTVIEW_UpdateScroll(infoPtr);
9357 /* refresh all only for lists whose height changed significantly */
9358 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
9359 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
9360 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
9361 LISTVIEW_InvalidateList(infoPtr);
9368 * Sets the size information.
9371 * [I] infoPtr : valid pointer to the listview structure
9376 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
9378 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9380 TRACE("uView=%d, rcList(old)=%s\n", uView, wine_dbgstr_rect(&infoPtr->rcList));
9382 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
9384 if (uView == LVS_LIST)
9386 /* Apparently the "LIST" style is supposed to have the same
9387 * number of items in a column even if there is no scroll bar.
9388 * Since if a scroll bar already exists then the bottom is already
9389 * reduced, only reduce if the scroll bar does not currently exist.
9390 * The "2" is there to mimic the native control. I think it may be
9391 * related to either padding or edges. (GLA 7/2002)
9393 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
9394 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
9395 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
9397 else if (uView == LVS_REPORT)
9402 hl.prc = &infoPtr->rcList;
9404 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9405 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp.flags, wp.x, wp.y, wp.cx, wp.cy);
9406 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy,
9407 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9408 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9409 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy);
9411 infoPtr->rcList.top = max(wp.cy, 0);
9412 infoPtr->rcList.top += (infoPtr->dwLvExStyle & LVS_EX_GRIDLINES) ? 2 : 0;
9415 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
9420 * Processes WM_STYLECHANGED messages.
9423 * [I] infoPtr : valid pointer to the listview structure
9424 * [I] wStyleType : window style type (normal or extended)
9425 * [I] lpss : window style information
9430 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9431 const STYLESTRUCT *lpss)
9433 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9434 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9437 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9438 wStyleType, lpss->styleOld, lpss->styleNew);
9440 if (wStyleType != GWL_STYLE) return 0;
9442 /* FIXME: if LVS_NOSORTHEADER changed, update header */
9443 /* what if LVS_OWNERDATA changed? */
9444 /* or LVS_SINGLESEL */
9445 /* or LVS_SORT{AS,DES}CENDING */
9447 infoPtr->dwStyle = lpss->styleNew;
9449 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9450 ((lpss->styleNew & WS_HSCROLL) == 0))
9451 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9453 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9454 ((lpss->styleNew & WS_VSCROLL) == 0))
9455 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9457 if (uNewView != uOldView)
9459 SIZE oldIconSize = infoPtr->iconSize;
9462 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9463 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9465 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9466 SetRectEmpty(&infoPtr->rcFocus);
9468 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9469 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
9471 if (uNewView == LVS_ICON)
9473 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
9475 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9476 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9477 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
9480 else if (uNewView == LVS_REPORT)
9485 hl.prc = &infoPtr->rcList;
9487 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9488 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
9489 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9490 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9493 LISTVIEW_UpdateItemSize(infoPtr);
9496 if (uNewView == LVS_REPORT)
9498 if ((lpss->styleOld ^ lpss->styleNew) & LVS_NOCOLUMNHEADER)
9500 if (lpss->styleNew & LVS_NOCOLUMNHEADER)
9502 /* Turn off the header control */
9503 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
9504 TRACE("Hide header control, was 0x%08x\n", style);
9505 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style | HDS_HIDDEN);
9507 /* Turn on the header control */
9508 if ((style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE)) & HDS_HIDDEN)
9510 TRACE("Show header control, was 0x%08x\n", style);
9511 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, (style & ~HDS_HIDDEN) | WS_VISIBLE);
9517 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9518 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9519 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9521 /* update the size of the client area */
9522 LISTVIEW_UpdateSize(infoPtr);
9524 /* add scrollbars if needed */
9525 LISTVIEW_UpdateScroll(infoPtr);
9527 /* invalidate client area + erase background */
9528 LISTVIEW_InvalidateList(infoPtr);
9535 * Window procedure of the listview control.
9538 static LRESULT WINAPI
9539 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9541 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9543 TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg, wParam, lParam);
9545 if (!infoPtr && (uMsg != WM_NCCREATE))
9546 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9550 case LVM_APPROXIMATEVIEWRECT:
9551 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9552 LOWORD(lParam), HIWORD(lParam));
9554 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9556 /* case LVM_CANCELEDITLABEL: */
9558 case LVM_CREATEDRAGIMAGE:
9559 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9561 case LVM_DELETEALLITEMS:
9562 return LISTVIEW_DeleteAllItems(infoPtr, FALSE);
9564 case LVM_DELETECOLUMN:
9565 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9567 case LVM_DELETEITEM:
9568 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9570 case LVM_EDITLABELW:
9571 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9573 case LVM_EDITLABELA:
9574 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9576 /* case LVM_ENABLEGROUPVIEW: */
9578 case LVM_ENSUREVISIBLE:
9579 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9582 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9585 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9587 case LVM_GETBKCOLOR:
9588 return infoPtr->clrBk;
9590 /* case LVM_GETBKIMAGE: */
9592 case LVM_GETCALLBACKMASK:
9593 return infoPtr->uCallbackMask;
9595 case LVM_GETCOLUMNA:
9596 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9598 case LVM_GETCOLUMNW:
9599 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9601 case LVM_GETCOLUMNORDERARRAY:
9602 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9604 case LVM_GETCOLUMNWIDTH:
9605 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9607 case LVM_GETCOUNTPERPAGE:
9608 return LISTVIEW_GetCountPerPage(infoPtr);
9610 case LVM_GETEDITCONTROL:
9611 return (LRESULT)infoPtr->hwndEdit;
9613 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9614 return infoPtr->dwLvExStyle;
9616 /* case LVM_GETGROUPINFO: */
9618 /* case LVM_GETGROUPMETRICS: */
9621 return (LRESULT)infoPtr->hwndHeader;
9623 case LVM_GETHOTCURSOR:
9624 return (LRESULT)infoPtr->hHotCursor;
9626 case LVM_GETHOTITEM:
9627 return infoPtr->nHotItem;
9629 case LVM_GETHOVERTIME:
9630 return infoPtr->dwHoverTime;
9632 case LVM_GETIMAGELIST:
9633 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9635 /* case LVM_GETINSERTMARK: */
9637 /* case LVM_GETINSERTMARKCOLOR: */
9639 /* case LVM_GETINSERTMARKRECT: */
9641 case LVM_GETISEARCHSTRINGA:
9642 case LVM_GETISEARCHSTRINGW:
9643 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9647 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9650 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9652 case LVM_GETITEMCOUNT:
9653 return infoPtr->nItemCount;
9655 case LVM_GETITEMPOSITION:
9656 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9658 case LVM_GETITEMRECT:
9659 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9661 case LVM_GETITEMSPACING:
9662 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9664 case LVM_GETITEMSTATE:
9665 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9667 case LVM_GETITEMTEXTA:
9668 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9670 case LVM_GETITEMTEXTW:
9671 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9673 case LVM_GETNEXTITEM:
9674 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9676 case LVM_GETNUMBEROFWORKAREAS:
9677 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9681 if (!lParam) return FALSE;
9682 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT ||
9683 (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST) return FALSE;
9684 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9687 /* case LVM_GETOUTLINECOLOR: */
9689 /* case LVM_GETSELECTEDCOLUMN: */
9691 case LVM_GETSELECTEDCOUNT:
9692 return LISTVIEW_GetSelectedCount(infoPtr);
9694 case LVM_GETSELECTIONMARK:
9695 return infoPtr->nSelectionMark;
9697 case LVM_GETSTRINGWIDTHA:
9698 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9700 case LVM_GETSTRINGWIDTHW:
9701 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9703 case LVM_GETSUBITEMRECT:
9704 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
9706 case LVM_GETTEXTBKCOLOR:
9707 return infoPtr->clrTextBk;
9709 case LVM_GETTEXTCOLOR:
9710 return infoPtr->clrText;
9712 /* case LVM_GETTILEINFO: */
9714 /* case LVM_GETTILEVIEWINFO: */
9716 case LVM_GETTOOLTIPS:
9717 if( !infoPtr->hwndToolTip )
9718 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9719 return (LRESULT)infoPtr->hwndToolTip;
9721 case LVM_GETTOPINDEX:
9722 return LISTVIEW_GetTopIndex(infoPtr);
9724 case LVM_GETUNICODEFORMAT:
9725 return (infoPtr->notifyFormat == NFR_UNICODE);
9727 /* case LVM_GETVIEW: */
9729 case LVM_GETVIEWRECT:
9730 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9732 case LVM_GETWORKAREAS:
9733 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9736 /* case LVM_HASGROUP: */
9739 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9741 case LVM_INSERTCOLUMNA:
9742 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9744 case LVM_INSERTCOLUMNW:
9745 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9747 /* case LVM_INSERTGROUP: */
9749 /* case LVM_INSERTGROUPSORTED: */
9751 case LVM_INSERTITEMA:
9752 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9754 case LVM_INSERTITEMW:
9755 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9757 /* case LVM_INSERTMARKHITTEST: */
9759 /* case LVM_ISGROUPVIEWENABLED: */
9761 /* case LVM_MAPIDTOINDEX: */
9763 /* case LVM_MAPINDEXTOID: */
9765 /* case LVM_MOVEGROUP: */
9767 /* case LVM_MOVEITEMTOGROUP: */
9769 case LVM_REDRAWITEMS:
9770 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9772 /* case LVM_REMOVEALLGROUPS: */
9774 /* case LVM_REMOVEGROUP: */
9777 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9779 case LVM_SETBKCOLOR:
9780 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9782 /* case LVM_SETBKIMAGE: */
9784 case LVM_SETCALLBACKMASK:
9785 infoPtr->uCallbackMask = (UINT)wParam;
9788 case LVM_SETCOLUMNA:
9789 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9791 case LVM_SETCOLUMNW:
9792 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9794 case LVM_SETCOLUMNORDERARRAY:
9795 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9797 case LVM_SETCOLUMNWIDTH:
9798 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9800 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9801 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9803 /* case LVM_SETGROUPINFO: */
9805 /* case LVM_SETGROUPMETRICS: */
9807 case LVM_SETHOTCURSOR:
9808 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9810 case LVM_SETHOTITEM:
9811 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9813 case LVM_SETHOVERTIME:
9814 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9816 case LVM_SETICONSPACING:
9817 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9819 case LVM_SETIMAGELIST:
9820 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9822 /* case LVM_SETINFOTIP: */
9824 /* case LVM_SETINSERTMARK: */
9826 /* case LVM_SETINSERTMARKCOLOR: */
9829 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9832 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9834 case LVM_SETITEMCOUNT:
9835 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9837 case LVM_SETITEMPOSITION:
9840 pt.x = (short)LOWORD(lParam);
9841 pt.y = (short)HIWORD(lParam);
9842 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9845 case LVM_SETITEMPOSITION32:
9846 if (lParam == 0) return FALSE;
9847 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9849 case LVM_SETITEMSTATE:
9850 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9852 case LVM_SETITEMTEXTA:
9853 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9855 case LVM_SETITEMTEXTW:
9856 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9858 /* case LVM_SETOUTLINECOLOR: */
9860 /* case LVM_SETSELECTEDCOLUMN: */
9862 case LVM_SETSELECTIONMARK:
9863 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9865 case LVM_SETTEXTBKCOLOR:
9866 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9868 case LVM_SETTEXTCOLOR:
9869 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9871 /* case LVM_SETTILEINFO: */
9873 /* case LVM_SETTILEVIEWINFO: */
9875 /* case LVM_SETTILEWIDTH: */
9877 case LVM_SETTOOLTIPS:
9878 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9880 case LVM_SETUNICODEFORMAT:
9881 return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
9883 /* case LVM_SETVIEW: */
9885 /* case LVM_SETWORKAREAS: */
9887 /* case LVM_SORTGROUPS: */
9890 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9892 /* LVM_SORTITEMSEX: */
9894 case LVM_SUBITEMHITTEST:
9895 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9898 return LISTVIEW_Update(infoPtr, (INT)wParam);
9901 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9904 return LISTVIEW_Command(infoPtr, wParam, lParam);
9907 return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
9910 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9913 return LISTVIEW_Destroy(infoPtr);
9916 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
9919 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9922 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9925 return (LRESULT)infoPtr->hFont;
9928 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9931 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9934 return LISTVIEW_KillFocus(infoPtr);
9936 case WM_LBUTTONDBLCLK:
9937 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9939 case WM_LBUTTONDOWN:
9940 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9943 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9946 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9949 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9952 return LISTVIEW_NCDestroy(infoPtr);
9955 if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
9960 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9961 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9964 case WM_NOTIFYFORMAT:
9965 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9967 case WM_PRINTCLIENT:
9968 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
9971 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9973 case WM_RBUTTONDBLCLK:
9974 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9976 case WM_RBUTTONDOWN:
9977 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9980 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9983 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9988 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9991 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9994 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9997 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9999 case WM_STYLECHANGED:
10000 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10002 case WM_SYSCOLORCHANGE:
10003 COMCTL32_RefreshSysColors();
10006 /* case WM_TIMER: */
10007 case WM_THEMECHANGED:
10008 return LISTVIEW_ThemeChanged(infoPtr);
10011 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10013 case WM_MOUSEWHEEL:
10014 if (wParam & (MK_SHIFT | MK_CONTROL))
10015 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10016 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
10018 case WM_WINDOWPOSCHANGED:
10019 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
10021 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
10022 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
10023 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
10025 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
10027 MEASUREITEMSTRUCT mis;
10028 mis.CtlType = ODT_LISTVIEW;
10029 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
10033 mis.itemHeight= infoPtr->nItemHeight;
10034 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
10035 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
10036 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
10039 LISTVIEW_UpdateSize(infoPtr);
10040 LISTVIEW_UpdateScroll(infoPtr);
10042 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10044 /* case WM_WININICHANGE: */
10047 if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
10048 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
10051 /* call default window procedure */
10052 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10059 * Registers the window class.
10067 void LISTVIEW_Register(void)
10069 WNDCLASSW wndClass;
10071 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
10072 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
10073 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
10074 wndClass.cbClsExtra = 0;
10075 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
10076 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
10077 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
10078 wndClass.lpszClassName = WC_LISTVIEWW;
10079 RegisterClassW(&wndClass);
10084 * Unregisters the window class.
10092 void LISTVIEW_Unregister(void)
10094 UnregisterClassW(WC_LISTVIEWW, NULL);
10099 * Handle any WM_COMMAND messages
10102 * [I] infoPtr : valid pointer to the listview structure
10103 * [I] wParam : the first message parameter
10104 * [I] lParam : the second message parameter
10109 static LRESULT LISTVIEW_Command(const LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
10111 switch (HIWORD(wParam))
10116 * Adjust the edit window size
10118 WCHAR buffer[1024];
10119 HDC hdc = GetDC(infoPtr->hwndEdit);
10120 HFONT hFont, hOldFont = 0;
10124 if (!infoPtr->hwndEdit || !hdc) return 0;
10125 GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
10126 GetWindowRect(infoPtr->hwndEdit, &rect);
10128 /* Select font to get the right dimension of the string */
10129 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
10132 hOldFont = SelectObject(hdc, hFont);
10135 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
10137 TEXTMETRICW textMetric;
10139 /* Add Extra spacing for the next character */
10140 GetTextMetricsW(hdc, &textMetric);
10141 sz.cx += (textMetric.tmMaxCharWidth * 2);
10149 rect.bottom - rect.top,
10150 SWP_DRAWFRAME|SWP_NOMOVE);
10153 SelectObject(hdc, hOldFont);
10155 ReleaseDC(infoPtr->hwndEdit, hdc);
10161 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
10170 * Subclassed edit control windproc function
10173 * [I] hwnd : the edit window handle
10174 * [I] uMsg : the message that is to be processed
10175 * [I] wParam : first message parameter
10176 * [I] lParam : second message parameter
10177 * [I] isW : TRUE if input is Unicode
10182 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
10184 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
10185 BOOL cancel = FALSE;
10187 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10188 hwnd, uMsg, wParam, lParam, isW);
10192 case WM_GETDLGCODE:
10193 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
10200 WNDPROC editProc = infoPtr->EditWndProc;
10201 infoPtr->EditWndProc = 0;
10202 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
10203 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
10207 if (VK_ESCAPE == (INT)wParam)
10212 else if (VK_RETURN == (INT)wParam)
10216 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
10219 /* kill the edit */
10220 if (infoPtr->hwndEdit)
10222 LPWSTR buffer = NULL;
10224 infoPtr->hwndEdit = 0;
10227 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
10231 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
10233 if (isW) GetWindowTextW(hwnd, buffer, len+1);
10234 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
10238 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
10243 SendMessageW(hwnd, WM_CLOSE, 0, 0);
10249 * Subclassed edit control Unicode windproc function
10252 * [I] hwnd : the edit window handle
10253 * [I] uMsg : the message that is to be processed
10254 * [I] wParam : first message parameter
10255 * [I] lParam : second message parameter
10259 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10261 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
10266 * Subclassed edit control ANSI windproc function
10269 * [I] hwnd : the edit window handle
10270 * [I] uMsg : the message that is to be processed
10271 * [I] wParam : first message parameter
10272 * [I] lParam : second message parameter
10276 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10278 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
10283 * Creates a subclassed edit control
10286 * [I] infoPtr : valid pointer to the listview structure
10287 * [I] text : initial text for the edit
10288 * [I] style : the window style
10289 * [I] isW : TRUE if input is Unicode
10293 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
10294 INT x, INT y, INT width, INT height, BOOL isW)
10296 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
10301 TEXTMETRICW textMetric;
10302 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
10304 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
10306 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
10307 hdc = GetDC(infoPtr->hwndSelf);
10309 /* Select the font to get appropriate metric dimensions */
10310 if(infoPtr->hFont != 0)
10311 hOldFont = SelectObject(hdc, infoPtr->hFont);
10313 /*Get String Length in pixels */
10314 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
10316 /*Add Extra spacing for the next character */
10317 GetTextMetricsW(hdc, &textMetric);
10318 sz.cx += (textMetric.tmMaxCharWidth * 2);
10320 if(infoPtr->hFont != 0)
10321 SelectObject(hdc, hOldFont);
10323 ReleaseDC(infoPtr->hwndSelf, hdc);
10325 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10327 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10329 if (!hedit) return 0;
10331 infoPtr->EditWndProc = (WNDPROC)
10332 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
10333 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
10335 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);