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
99 * -- LVS_EX_SIMPLESELECT
100 * -- LVS_EX_TWOCLICKACTIVATE
101 * -- LVS_EX_UNDERLINECOLD
102 * -- LVS_EX_UNDERLINEHOT
105 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
108 * -- LVN_MARQUEEBEGIN
114 * -- LVM_CANCELEDITLABEL
115 * -- LVM_ENABLEGROUPVIEW
116 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
117 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
118 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
119 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
120 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
121 * -- LVM_GETINSERTMARKRECT
122 * -- LVM_GETNUMBEROFWORKAREAS
123 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
124 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
125 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
126 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
127 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
128 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
129 * -- LVM_GETVIEW, LVM_SETVIEW
130 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
131 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
132 * -- LVM_INSERTGROUPSORTED
133 * -- LVM_INSERTMARKHITTEST
134 * -- LVM_ISGROUPVIEWENABLED
135 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
137 * -- LVM_MOVEITEMTOGROUP
139 * -- LVM_SETTILEWIDTH
144 * -- ListView_GetCheckSate, ListView_SetCheckState
145 * -- ListView_GetHoverTime, ListView_SetHoverTime
146 * -- ListView_GetISearchString
147 * -- ListView_GetNumberOfWorkAreas
148 * -- ListView_GetOrigin
149 * -- ListView_GetTextBkColor
150 * -- ListView_GetUnicodeFormat, ListView_SetUnicodeFormat
151 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
152 * -- ListView_SortItemsEx
157 * Known differences in message stream from native control (not known if
158 * these differences cause problems):
159 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
160 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
161 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
162 * processing for "USEDOUBLECLICKTIME".
166 #include "wine/port.h"
181 #include "commctrl.h"
182 #include "comctl32.h"
185 #include "wine/debug.h"
186 #include "wine/unicode.h"
188 WINE_DEFAULT_DEBUG_CHANNEL(listview);
190 /* make sure you set this to 0 for production use! */
191 #define DEBUG_RANGES 1
193 typedef struct tagCOLUMN_INFO
195 RECT rcHeader; /* tracks the header's rectangle */
196 int fmt; /* same as LVCOLUMN.fmt */
199 typedef struct tagITEMHDR
203 } ITEMHDR, *LPITEMHDR;
205 typedef struct tagSUBITEM_INFO
211 typedef struct tagITEM_INFO
219 typedef struct tagRANGE
225 typedef struct tagRANGES
230 typedef struct tagITERATOR
239 typedef struct tagDELAYED_ITEM_EDIT
245 typedef struct tagLISTVIEW_INFO
252 HIMAGELIST himlNormal;
253 HIMAGELIST himlSmall;
254 HIMAGELIST himlState;
258 POINT ptClickPos; /* point where the user clicked */
259 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
262 RANGES selectionRanges;
267 RECT rcList; /* This rectangle is really the window
268 * client rectangle possibly reduced by the
269 * horizontal scroll bar and/or header - see
270 * LISTVIEW_UpdateSize. This rectangle offset
271 * by the LISTVIEW_GetOrigin value is in
272 * client coordinates */
281 INT ntmHeight; /* Some cached metrics of the font used */
282 INT ntmMaxCharWidth; /* by the listview to draw items */
284 BOOL bRedraw; /* Turns on/off repaints & invalidations */
285 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
287 BOOL bDoChangeNotify; /* send change notification messages? */
290 DWORD dwStyle; /* the cached window GWL_STYLE */
291 DWORD dwLvExStyle; /* extended listview style */
292 INT nItemCount; /* the number of items in the list */
293 HDPA hdpaItems; /* array ITEM_INFO pointers */
294 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
295 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
296 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
297 POINT currIconPos; /* this is the position next icon will be placed */
298 PFNLVCOMPARE pfnCompare;
306 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
308 DWORD lastKeyPressTimestamp;
310 INT nSearchParamLength;
311 WCHAR szSearchParam[ MAX_PATH ];
313 INT nMeasureItemHeight;
314 INT xTrackLine; /* The x coefficient of the track line or -1 if none */
315 DELAYED_ITEM_EDIT itemEdit; /* Pointer to this structure will be the timer ID */
321 /* How many we debug buffer to allocate */
322 #define DEBUG_BUFFERS 20
323 /* The size of a single debug bbuffer */
324 #define DEBUG_BUFFER_SIZE 256
326 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
327 #define SB_INTERNAL -1
329 /* maximum size of a label */
330 #define DISP_TEXT_SIZE 512
332 /* padding for items in list and small icon display modes */
333 #define WIDTH_PADDING 12
335 /* padding for items in list, report and small icon display modes */
336 #define HEIGHT_PADDING 1
338 /* offset of items in report display mode */
339 #define REPORT_MARGINX 2
341 /* padding for icon in large icon display mode
342 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
343 * that HITTEST will see.
344 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
345 * ICON_TOP_PADDING - sum of the two above.
346 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
347 * LABEL_HOR_PADDING - between text and sides of box
348 * LABEL_VERT_PADDING - between bottom of text and end of box
350 * ICON_LR_PADDING - additional width above icon size.
351 * ICON_LR_HALF - half of the above value
353 #define ICON_TOP_PADDING_NOTHITABLE 2
354 #define ICON_TOP_PADDING_HITABLE 2
355 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
356 #define ICON_BOTTOM_PADDING 4
357 #define LABEL_HOR_PADDING 5
358 #define LABEL_VERT_PADDING 7
359 #define ICON_LR_PADDING 16
360 #define ICON_LR_HALF (ICON_LR_PADDING/2)
362 /* default label width for items in list and small icon display modes */
363 #define DEFAULT_LABEL_WIDTH 40
365 /* default column width for items in list display mode */
366 #define DEFAULT_COLUMN_WIDTH 128
368 /* Size of "line" scroll for V & H scrolls */
369 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
371 /* Padding between image and label */
372 #define IMAGE_PADDING 2
374 /* Padding behind the label */
375 #define TRAILING_LABEL_PADDING 12
376 #define TRAILING_HEADER_PADDING 11
378 /* Border for the icon caption */
379 #define CAPTION_BORDER 2
381 /* Standard DrawText flags */
382 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
383 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
384 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
386 /* Image index from state */
387 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
389 /* The time in milliseconds to reset the search in the list */
390 #define KEY_DELAY 450
392 /* Dump the LISTVIEW_INFO structure to the debug channel */
393 #define LISTVIEW_DUMP(iP) do { \
394 TRACE("hwndSelf=%p, clrBk=0x%06x, clrText=0x%06x, clrTextBk=0x%06x, ItemHeight=%d, ItemWidth=%d, Style=0x%08x\n", \
395 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
396 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
397 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08x, Focus=%d\n", \
398 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
399 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
400 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%d, icSz.cy=%d, icSp.cx=%d, icSp.cy=%d, notifyFmt=%d\n", \
401 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
402 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
403 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
406 static const WCHAR themeClass[] = {'L','i','s','t','V','i','e','w',0};
409 * forward declarations
411 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *, LPLVITEMW, BOOL);
412 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *, INT, LPRECT);
413 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *, INT, LPPOINT);
414 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *, INT, LPPOINT);
415 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *, INT, LPRECT);
416 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *, INT);
417 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *, LPPOINT);
418 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *, LPRECT);
419 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
420 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, LVITEMW *, BOOL);
421 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *);
422 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
423 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
424 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
425 static LRESULT LISTVIEW_Command(const LISTVIEW_INFO *, WPARAM, LPARAM);
426 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
427 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *, LPCWSTR, BOOL);
428 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT, BOOL);
429 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *, INT, UINT);
430 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
431 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
432 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
433 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *);
434 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
435 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
436 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
437 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
439 /******** Text handling functions *************************************/
441 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
442 * text string. The string may be ANSI or Unicode, in which case
443 * the boolean isW tells us the type of the string.
445 * The name of the function tell what type of strings it expects:
446 * W: Unicode, T: ANSI/Unicode - function of isW
449 static inline BOOL is_textW(LPCWSTR text)
451 return text != NULL && text != LPSTR_TEXTCALLBACKW;
454 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
456 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
457 return is_textW(text);
460 static inline int textlenT(LPCWSTR text, BOOL isW)
462 return !is_textT(text, isW) ? 0 :
463 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
466 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
469 if (isSrcW) lstrcpynW(dest, src, max);
470 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
472 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
473 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
476 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
478 LPWSTR wstr = (LPWSTR)text;
480 if (!isW && is_textT(text, isW))
482 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
483 wstr = Alloc(len * sizeof(WCHAR));
484 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
486 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
490 static inline void textfreeT(LPWSTR wstr, BOOL isW)
492 if (!isW && is_textT(wstr, isW)) Free (wstr);
496 * dest is a pointer to a Unicode string
497 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
499 static BOOL textsetptrT(LPWSTR *dest, LPCWSTR src, BOOL isW)
503 if (src == LPSTR_TEXTCALLBACKW)
505 if (is_textW(*dest)) Free(*dest);
506 *dest = LPSTR_TEXTCALLBACKW;
510 LPWSTR pszText = textdupTtoW(src, isW);
511 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
512 bResult = Str_SetPtrW(dest, pszText);
513 textfreeT(pszText, isW);
519 * compares a Unicode to a Unicode/ANSI text string
521 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
523 if (!aw) return bt ? -1 : 0;
524 if (!bt) return aw ? 1 : 0;
525 if (aw == LPSTR_TEXTCALLBACKW)
526 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
527 if (bt != LPSTR_TEXTCALLBACKW)
529 LPWSTR bw = textdupTtoW(bt, isW);
530 int r = bw ? lstrcmpW(aw, bw) : 1;
538 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
542 n = min(min(n, lstrlenW(s1)), lstrlenW(s2));
543 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
544 return res ? res - sizeof(WCHAR) : res;
547 /******** Debugging functions *****************************************/
549 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
551 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
552 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
555 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
557 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
558 n = min(textlenT(text, isW), n);
559 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
562 static char* debug_getbuf(void)
564 static int index = 0;
565 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
566 return buffers[index++ % DEBUG_BUFFERS];
569 static inline const char* debugrange(const RANGE *lprng)
571 if (!lprng) return "(null)";
572 return wine_dbg_sprintf("[%d, %d]", lprng->lower, lprng->upper);
575 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
577 char* buf = debug_getbuf(), *text = buf;
578 int len, size = DEBUG_BUFFER_SIZE;
580 if (pScrollInfo == NULL) return "(null)";
581 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
582 if (len == -1) goto end; buf += len; size -= len;
583 if (pScrollInfo->fMask & SIF_RANGE)
584 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
586 if (len == -1) goto end; buf += len; size -= len;
587 if (pScrollInfo->fMask & SIF_PAGE)
588 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
590 if (len == -1) goto end; buf += len; size -= len;
591 if (pScrollInfo->fMask & SIF_POS)
592 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
594 if (len == -1) goto end; buf += len; size -= len;
595 if (pScrollInfo->fMask & SIF_TRACKPOS)
596 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
598 if (len == -1) goto end; buf += len; size -= len;
601 buf = text + strlen(text);
603 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
607 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
609 if (!plvnm) return "(null)";
610 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
611 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld",
612 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
613 plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam);
616 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
618 char* buf = debug_getbuf(), *text = buf;
619 int len, size = DEBUG_BUFFER_SIZE;
621 if (lpLVItem == NULL) return "(null)";
622 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
623 if (len == -1) goto end; buf += len; size -= len;
624 if (lpLVItem->mask & LVIF_STATE)
625 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
627 if (len == -1) goto end; buf += len; size -= len;
628 if (lpLVItem->mask & LVIF_TEXT)
629 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
631 if (len == -1) goto end; buf += len; size -= len;
632 if (lpLVItem->mask & LVIF_IMAGE)
633 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
635 if (len == -1) goto end; buf += len; size -= len;
636 if (lpLVItem->mask & LVIF_PARAM)
637 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
639 if (len == -1) goto end; buf += len; size -= len;
640 if (lpLVItem->mask & LVIF_INDENT)
641 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
643 if (len == -1) goto end; buf += len; size -= len;
646 buf = text + strlen(text);
648 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
652 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
654 char* buf = debug_getbuf(), *text = buf;
655 int len, size = DEBUG_BUFFER_SIZE;
657 if (lpColumn == NULL) return "(null)";
658 len = snprintf(buf, size, "{");
659 if (len == -1) goto end; buf += len; size -= len;
660 if (lpColumn->mask & LVCF_SUBITEM)
661 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
663 if (len == -1) goto end; buf += len; size -= len;
664 if (lpColumn->mask & LVCF_FMT)
665 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
667 if (len == -1) goto end; buf += len; size -= len;
668 if (lpColumn->mask & LVCF_WIDTH)
669 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
671 if (len == -1) goto end; buf += len; size -= len;
672 if (lpColumn->mask & LVCF_TEXT)
673 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
675 if (len == -1) goto end; buf += len; size -= len;
676 if (lpColumn->mask & LVCF_IMAGE)
677 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
679 if (len == -1) goto end; buf += len; size -= len;
680 if (lpColumn->mask & LVCF_ORDER)
681 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
683 if (len == -1) goto end; buf += len; size -= len;
686 buf = text + strlen(text);
688 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
692 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
694 if (!lpht) return "(null)";
696 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
697 wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
700 /* Return the corresponding text for a given scroll value */
701 static inline LPCSTR debugscrollcode(int nScrollCode)
705 case SB_LINELEFT: return "SB_LINELEFT";
706 case SB_LINERIGHT: return "SB_LINERIGHT";
707 case SB_PAGELEFT: return "SB_PAGELEFT";
708 case SB_PAGERIGHT: return "SB_PAGERIGHT";
709 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
710 case SB_THUMBTRACK: return "SB_THUMBTRACK";
711 case SB_ENDSCROLL: return "SB_ENDSCROLL";
712 case SB_INTERNAL: return "SB_INTERNAL";
713 default: return "unknown";
718 /******** Notification functions i************************************/
720 static LRESULT notify_forward_header(const LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
722 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
723 (WPARAM)lpnmh->hdr.idFrom, (LPARAM)lpnmh);
726 static LRESULT notify_hdr(const LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
730 TRACE("(code=%d)\n", code);
732 pnmh->hwndFrom = infoPtr->hwndSelf;
733 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
735 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, pnmh->idFrom, (LPARAM)pnmh);
737 TRACE(" <= %ld\n", result);
742 static inline BOOL notify(const LISTVIEW_INFO *infoPtr, INT code)
745 HWND hwnd = infoPtr->hwndSelf;
746 notify_hdr(infoPtr, code, &nmh);
747 return IsWindow(hwnd);
750 static inline void notify_itemactivate(const LISTVIEW_INFO *infoPtr, const LVHITTESTINFO *htInfo)
761 item.mask = LVIF_PARAM|LVIF_STATE;
762 item.iItem = htInfo->iItem;
764 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
765 nmia.lParam = item.lParam;
766 nmia.uOldState = item.state;
767 nmia.uNewState = item.state | LVIS_ACTIVATING;
768 nmia.uChanged = LVIF_STATE;
771 nmia.iItem = htInfo->iItem;
772 nmia.iSubItem = htInfo->iSubItem;
773 nmia.ptAction = htInfo->pt;
775 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
776 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
777 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
779 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
782 static inline LRESULT notify_listview(const LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
784 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
785 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
788 static BOOL notify_click(const LISTVIEW_INFO *infoPtr, INT code, const LVHITTESTINFO *lvht)
792 HWND hwnd = infoPtr->hwndSelf;
794 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
795 ZeroMemory(&nmlv, sizeof(nmlv));
796 nmlv.iItem = lvht->iItem;
797 nmlv.iSubItem = lvht->iSubItem;
798 nmlv.ptAction = lvht->pt;
799 item.mask = LVIF_PARAM;
800 item.iItem = lvht->iItem;
802 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
803 notify_listview(infoPtr, code, &nmlv);
804 return IsWindow(hwnd);
807 static BOOL notify_deleteitem(const LISTVIEW_INFO *infoPtr, INT nItem)
811 HWND hwnd = infoPtr->hwndSelf;
813 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
815 item.mask = LVIF_PARAM;
818 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
819 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
820 return IsWindow(hwnd);
823 static int get_ansi_notification(UINT unicodeNotificationCode)
825 switch (unicodeNotificationCode)
827 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
828 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
829 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
830 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
831 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
832 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
834 ERR("unknown notification %x\n", unicodeNotificationCode);
840 Send notification. depends on dispinfoW having same
841 structure as dispinfoA.
842 infoPtr : listview struct
843 notificationCode : *Unicode* notification code
844 pdi : dispinfo structure (can be unicode or ansi)
845 isW : TRUE if dispinfo is Unicode
847 static BOOL notify_dispinfoT(const LISTVIEW_INFO *infoPtr, UINT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
849 BOOL bResult = FALSE;
850 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
851 INT cchTempBufMax = 0, savCchTextMax = 0;
853 LPWSTR pszTempBuf = NULL, savPszText = NULL;
855 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
857 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
858 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
861 if (convertToAnsi || convertToUnicode)
863 if (notificationCode != LVN_GETDISPINFOW)
865 cchTempBufMax = convertToUnicode ?
866 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
867 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
871 cchTempBufMax = pdi->item.cchTextMax;
872 *pdi->item.pszText = 0; /* make sure we don't process garbage */
875 pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
876 if (!pszTempBuf) return FALSE;
878 if (convertToUnicode)
879 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
880 pszTempBuf, cchTempBufMax);
882 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
883 cchTempBufMax, NULL, NULL);
885 savCchTextMax = pdi->item.cchTextMax;
886 savPszText = pdi->item.pszText;
887 pdi->item.pszText = pszTempBuf;
888 pdi->item.cchTextMax = cchTempBufMax;
891 if (infoPtr->notifyFormat == NFR_ANSI)
892 realNotifCode = get_ansi_notification(notificationCode);
894 realNotifCode = notificationCode;
895 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
896 bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
898 if (convertToUnicode || convertToAnsi)
900 if (convertToUnicode) /* note : pointer can be changed by app ! */
901 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
902 savCchTextMax, NULL, NULL);
904 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
905 savPszText, savCchTextMax);
906 pdi->item.pszText = savPszText; /* restores our buffer */
907 pdi->item.cchTextMax = savCchTextMax;
913 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, const LISTVIEW_INFO *infoPtr, HDC hdc,
914 const RECT *rcBounds, const LVITEMW *lplvItem)
916 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
917 lpnmlvcd->nmcd.hdc = hdc;
918 lpnmlvcd->nmcd.rc = *rcBounds;
919 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
920 lpnmlvcd->clrText = infoPtr->clrText;
921 if (!lplvItem) return;
922 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
923 lpnmlvcd->iSubItem = lplvItem->iSubItem;
924 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
925 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
926 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
927 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
930 static inline DWORD notify_customdraw (const LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
932 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
935 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
936 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
937 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
938 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
939 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
940 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
944 static void prepaint_setup (const LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd, BOOL SubItem)
946 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
947 lpnmlvcd->clrTextBk = comctl32_color.clrWindow;
948 if (lpnmlvcd->clrText == CLR_DEFAULT)
949 lpnmlvcd->clrText = comctl32_color.clrWindowText;
951 /* apparently, for selected items, we have to override the returned values */
954 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
958 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
959 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
961 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
963 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
964 lpnmlvcd->clrText = comctl32_color.clrBtnText;
969 /* Set the text attributes */
970 if (lpnmlvcd->clrTextBk != CLR_NONE)
972 SetBkMode(hdc, OPAQUE);
973 SetBkColor(hdc,lpnmlvcd->clrTextBk);
976 SetBkMode(hdc, TRANSPARENT);
977 SetTextColor(hdc, lpnmlvcd->clrText);
980 static inline DWORD notify_postpaint (const LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
982 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
985 /******** Item iterator functions **********************************/
987 static RANGES ranges_create(int count);
988 static void ranges_destroy(RANGES ranges);
989 static BOOL ranges_add(RANGES ranges, RANGE range);
990 static BOOL ranges_del(RANGES ranges, RANGE range);
991 static void ranges_dump(RANGES ranges);
993 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
995 RANGE range = { nItem, nItem + 1 };
997 return ranges_add(ranges, range);
1000 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
1002 RANGE range = { nItem, nItem + 1 };
1004 return ranges_del(ranges, range);
1008 * ITERATOR DOCUMENTATION
1010 * The iterator functions allow for easy, and convenient iteration
1011 * over items of interest in the list. Typically, you create a
1012 * iterator, use it, and destroy it, as such:
1015 * iterator_xxxitems(&i, ...);
1016 * while (iterator_{prev,next}(&i)
1018 * //code which uses i.nItem
1020 * iterator_destroy(&i);
1022 * where xxx is either: framed, or visible.
1023 * Note that it is important that the code destroys the iterator
1024 * after it's done with it, as the creation of the iterator may
1025 * allocate memory, which thus needs to be freed.
1027 * You can iterate both forwards, and backwards through the list,
1028 * by using iterator_next or iterator_prev respectively.
1030 * Lower numbered items are draw on top of higher number items in
1031 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1032 * items may overlap). So, to test items, you should use
1034 * which lists the items top to bottom (in Z-order).
1035 * For drawing items, you should use
1037 * which lists the items bottom to top (in Z-order).
1038 * If you keep iterating over the items after the end-of-items
1039 * marker (-1) is returned, the iterator will start from the
1040 * beginning. Typically, you don't need to test for -1,
1041 * because iterator_{next,prev} will return TRUE if more items
1042 * are to be iterated over, or FALSE otherwise.
1044 * Note: the iterator is defined to be bidirectional. That is,
1045 * any number of prev followed by any number of next, or
1046 * five versa, should leave the iterator at the same item:
1047 * prev * n, next * n = next * n, prev * n
1049 * The iterator has a notion of an out-of-order, special item,
1050 * which sits at the start of the list. This is used in
1051 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1052 * which needs to be first, as it may overlap other items.
1054 * The code is a bit messy because we have:
1055 * - a special item to deal with
1056 * - simple range, or composite range
1058 * If you find bugs, or want to add features, please make sure you
1059 * always check/modify *both* iterator_prev, and iterator_next.
1063 * This function iterates through the items in increasing order,
1064 * but prefixed by the special item, then -1. That is:
1065 * special, 1, 2, 3, ..., n, -1.
1066 * Each item is listed only once.
1068 static inline BOOL iterator_next(ITERATOR* i)
1072 i->nItem = i->nSpecial;
1073 if (i->nItem != -1) return TRUE;
1075 if (i->nItem == i->nSpecial)
1077 if (i->ranges) i->index = 0;
1083 if (i->nItem == i->nSpecial) i->nItem++;
1084 if (i->nItem < i->range.upper) return TRUE;
1089 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1090 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1093 else if (i->nItem >= i->range.upper) goto end;
1095 i->nItem = i->range.lower;
1096 if (i->nItem >= 0) goto testitem;
1103 * This function iterates through the items in decreasing order,
1104 * followed by the special item, then -1. That is:
1105 * n, n-1, ..., 3, 2, 1, special, -1.
1106 * Each item is listed only once.
1108 static inline BOOL iterator_prev(ITERATOR* i)
1115 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1118 if (i->nItem == i->nSpecial)
1126 if (i->nItem == i->nSpecial) i->nItem--;
1127 if (i->nItem >= i->range.lower) return TRUE;
1133 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1136 else if (!start && i->nItem < i->range.lower) goto end;
1138 i->nItem = i->range.upper;
1139 if (i->nItem > 0) goto testitem;
1141 return (i->nItem = i->nSpecial) != -1;
1144 static RANGE iterator_range(const ITERATOR *i)
1148 if (!i->ranges) return i->range;
1150 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1152 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1153 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1155 else range.lower = range.upper = 0;
1161 * Releases resources associated with this ierator.
1163 static inline void iterator_destroy(const ITERATOR *i)
1165 ranges_destroy(i->ranges);
1169 * Create an empty iterator.
1171 static inline BOOL iterator_empty(ITERATOR* i)
1173 ZeroMemory(i, sizeof(*i));
1174 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1179 * Create an iterator over a range.
1181 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1189 * Create an iterator over a bunch of ranges.
1190 * Please note that the iterator will take ownership of the ranges,
1191 * and will free them upon destruction.
1193 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1201 * Creates an iterator over the items which intersect lprc.
1203 static BOOL iterator_frameditems(ITERATOR* i, const LISTVIEW_INFO* infoPtr, const RECT *lprc)
1205 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1206 RECT frame = *lprc, rcItem, rcTemp;
1209 /* in case we fail, we want to return an empty iterator */
1210 if (!iterator_empty(i)) return FALSE;
1212 LISTVIEW_GetOrigin(infoPtr, &Origin);
1214 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc));
1215 OffsetRect(&frame, -Origin.x, -Origin.y);
1217 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1221 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1223 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1224 if (IntersectRect(&rcTemp, &rcItem, lprc))
1225 i->nSpecial = infoPtr->nFocusedItem;
1227 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1228 /* to do better here, we need to have PosX, and PosY sorted */
1229 TRACE("building icon ranges:\n");
1230 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1232 rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1233 rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1234 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1235 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1236 if (IntersectRect(&rcTemp, &rcItem, &frame))
1237 ranges_additem(i->ranges, nItem);
1241 else if (uView == LVS_REPORT)
1245 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1246 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1248 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1249 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1250 if (range.upper <= range.lower) return TRUE;
1251 if (!iterator_rangeitems(i, range)) return FALSE;
1252 TRACE(" report=%s\n", debugrange(&i->range));
1256 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1257 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1258 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1259 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1260 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1261 INT lower = nFirstCol * nPerCol + nFirstRow;
1265 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1266 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1268 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1270 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1271 TRACE("building list ranges:\n");
1272 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1274 item_range.lower = nCol * nPerCol + nFirstRow;
1275 if(item_range.lower >= infoPtr->nItemCount) break;
1276 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1277 TRACE(" list=%s\n", debugrange(&item_range));
1278 ranges_add(i->ranges, item_range);
1286 * Creates an iterator over the items which intersect the visible region of hdc.
1288 static BOOL iterator_visibleitems(ITERATOR *i, const LISTVIEW_INFO *infoPtr, HDC hdc)
1290 POINT Origin, Position;
1291 RECT rcItem, rcClip;
1294 rgntype = GetClipBox(hdc, &rcClip);
1295 if (rgntype == NULLREGION) return iterator_empty(i);
1296 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1297 if (rgntype == SIMPLEREGION) return TRUE;
1299 /* first deal with the special item */
1300 if (i->nSpecial != -1)
1302 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1303 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1306 /* if we can't deal with the region, we'll just go with the simple range */
1307 LISTVIEW_GetOrigin(infoPtr, &Origin);
1308 TRACE("building visible range:\n");
1309 if (!i->ranges && i->range.lower < i->range.upper)
1311 if (!(i->ranges = ranges_create(50))) return TRUE;
1312 if (!ranges_add(i->ranges, i->range))
1314 ranges_destroy(i->ranges);
1320 /* now delete the invisible items from the list */
1321 while(iterator_next(i))
1323 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1324 rcItem.left = Position.x + Origin.x;
1325 rcItem.top = Position.y + Origin.y;
1326 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1327 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1328 if (!RectVisible(hdc, &rcItem))
1329 ranges_delitem(i->ranges, i->nItem);
1331 /* the iterator should restart on the next iterator_next */
1337 /******** Misc helper functions ************************************/
1339 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1340 WPARAM wParam, LPARAM lParam, BOOL isW)
1342 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1343 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1346 static inline BOOL is_autoarrange(const LISTVIEW_INFO *infoPtr)
1348 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1350 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1351 (uView == LVS_ICON || uView == LVS_SMALLICON);
1354 static void toggle_checkbox_state(LISTVIEW_INFO *infoPtr, INT nItem)
1356 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
1357 if(state == 1 || state == 2)
1361 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
1362 lvitem.stateMask = LVIS_STATEIMAGEMASK;
1363 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
1367 /******** Internal API functions ************************************/
1369 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(const LISTVIEW_INFO *infoPtr, INT nSubItem)
1371 static COLUMN_INFO mainItem;
1373 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1374 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1375 return DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1378 static INT LISTVIEW_CreateHeader(LISTVIEW_INFO *infoPtr)
1380 DWORD dFlags = WS_CHILD | HDS_HORZ | HDS_FULLDRAG | HDS_DRAGDROP;
1383 if (infoPtr->hwndHeader) return 0;
1385 /* setup creation flags */
1386 dFlags |= (LVS_NOSORTHEADER & infoPtr->dwStyle) ? 0 : HDS_BUTTONS;
1387 dFlags |= (LVS_NOCOLUMNHEADER & infoPtr->dwStyle) ? HDS_HIDDEN : 0;
1389 hInst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
1392 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL, dFlags,
1393 0, 0, 0, 0, infoPtr->hwndSelf, NULL, hInst, NULL);
1394 if (!infoPtr->hwndHeader) return -1;
1396 /* set header unicode format */
1397 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, TRUE, 0);
1399 /* set header font */
1400 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
1405 static inline void LISTVIEW_GetHeaderRect(const LISTVIEW_INFO *infoPtr, INT nSubItem, LPRECT lprc)
1407 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1410 static inline BOOL LISTVIEW_GetItemW(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1412 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1415 /* Listview invalidation functions: use _only_ these functions to invalidate */
1417 static inline BOOL is_redrawing(const LISTVIEW_INFO *infoPtr)
1419 return infoPtr->bRedraw;
1422 static inline void LISTVIEW_InvalidateRect(const LISTVIEW_INFO *infoPtr, const RECT* rect)
1424 if(!is_redrawing(infoPtr)) return;
1425 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect));
1426 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1429 static inline void LISTVIEW_InvalidateItem(const LISTVIEW_INFO *infoPtr, INT nItem)
1433 if(!is_redrawing(infoPtr)) return;
1434 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1435 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1438 static inline void LISTVIEW_InvalidateSubItem(const LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1440 POINT Origin, Position;
1443 if(!is_redrawing(infoPtr)) return;
1444 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1445 LISTVIEW_GetOrigin(infoPtr, &Origin);
1446 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1447 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1449 rcBox.bottom = infoPtr->nItemHeight;
1450 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1451 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1454 static inline void LISTVIEW_InvalidateList(const LISTVIEW_INFO *infoPtr)
1456 LISTVIEW_InvalidateRect(infoPtr, NULL);
1459 static inline void LISTVIEW_InvalidateColumn(const LISTVIEW_INFO *infoPtr, INT nColumn)
1463 if(!is_redrawing(infoPtr)) return;
1464 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1465 rcCol.top = infoPtr->rcList.top;
1466 rcCol.bottom = infoPtr->rcList.bottom;
1467 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1472 * Retrieves the number of items that can fit vertically in the client area.
1475 * [I] infoPtr : valid pointer to the listview structure
1478 * Number of items per row.
1480 static inline INT LISTVIEW_GetCountPerRow(const LISTVIEW_INFO *infoPtr)
1482 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1484 return max(nListWidth/infoPtr->nItemWidth, 1);
1489 * Retrieves the number of items that can fit horizontally in the client
1493 * [I] infoPtr : valid pointer to the listview structure
1496 * Number of items per column.
1498 static inline INT LISTVIEW_GetCountPerColumn(const LISTVIEW_INFO *infoPtr)
1500 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1502 return max(nListHeight / infoPtr->nItemHeight, 1);
1506 /*************************************************************************
1507 * LISTVIEW_ProcessLetterKeys
1509 * Processes keyboard messages generated by pressing the letter keys
1511 * What this does is perform a case insensitive search from the
1512 * current position with the following quirks:
1513 * - If two chars or more are pressed in quick succession we search
1514 * for the corresponding string (e.g. 'abc').
1515 * - If there is a delay we wipe away the current search string and
1516 * restart with just that char.
1517 * - If the user keeps pressing the same character, whether slowly or
1518 * fast, so that the search string is entirely composed of this
1519 * character ('aaaaa' for instance), then we search for first item
1520 * that starting with that character.
1521 * - If the user types the above character in quick succession, then
1522 * we must also search for the corresponding string ('aaaaa'), and
1523 * go to that string if there is a match.
1526 * [I] hwnd : handle to the window
1527 * [I] charCode : the character code, the actual character
1528 * [I] keyData : key data
1536 * - The current implementation has a list of characters it will
1537 * accept and it ignores everything else. In particular it will
1538 * ignore accentuated characters which seems to match what
1539 * Windows does. But I'm not sure it makes sense to follow
1541 * - We don't sound a beep when the search fails.
1545 * TREEVIEW_ProcessLetterKeys
1547 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1552 WCHAR buffer[MAX_PATH];
1553 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1555 /* simple parameter checking */
1556 if (!charCode || !keyData) return 0;
1558 /* only allow the valid WM_CHARs through */
1559 if (!isalnumW(charCode) &&
1560 charCode != '.' && charCode != '`' && charCode != '!' &&
1561 charCode != '@' && charCode != '#' && charCode != '$' &&
1562 charCode != '%' && charCode != '^' && charCode != '&' &&
1563 charCode != '*' && charCode != '(' && charCode != ')' &&
1564 charCode != '-' && charCode != '_' && charCode != '+' &&
1565 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1566 charCode != '}' && charCode != '[' && charCode != '{' &&
1567 charCode != '/' && charCode != '?' && charCode != '>' &&
1568 charCode != '<' && charCode != ',' && charCode != '~')
1571 /* if there's one item or less, there is no where to go */
1572 if (infoPtr->nItemCount <= 1) return 0;
1574 /* update the search parameters */
1575 infoPtr->lastKeyPressTimestamp = GetTickCount();
1576 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1577 if (infoPtr->nSearchParamLength < MAX_PATH-1)
1578 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1579 if (infoPtr->charCode != charCode)
1580 infoPtr->charCode = charCode = 0;
1582 infoPtr->charCode=charCode;
1583 infoPtr->szSearchParam[0]=charCode;
1584 infoPtr->nSearchParamLength=1;
1585 /* Redundant with the 1 char string */
1589 /* and search from the current position */
1591 if (infoPtr->nFocusedItem >= 0) {
1592 endidx=infoPtr->nFocusedItem;
1594 /* if looking for single character match,
1595 * then we must always move forward
1597 if (infoPtr->nSearchParamLength == 1)
1600 endidx=infoPtr->nItemCount;
1604 /* Let application handle this for virtual listview */
1605 if (infoPtr->dwStyle & LVS_OWNERDATA)
1610 ZeroMemory(&lvfi, sizeof(lvfi));
1611 lvfi.flags = (LVFI_WRAP | LVFI_PARTIAL);
1612 infoPtr->szSearchParam[infoPtr->nSearchParamLength] = '\0';
1613 lvfi.psz = infoPtr->szSearchParam;
1617 nItem = notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
1620 LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1626 if (idx == infoPtr->nItemCount) {
1627 if (endidx == infoPtr->nItemCount || endidx == 0)
1633 item.mask = LVIF_TEXT;
1636 item.pszText = buffer;
1637 item.cchTextMax = MAX_PATH;
1638 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1640 /* check for a match */
1641 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1644 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1645 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1646 /* This would work but we must keep looking for a longer match */
1650 } while (idx != endidx);
1653 LISTVIEW_KeySelection(infoPtr, nItem, FALSE);
1658 /*************************************************************************
1659 * LISTVIEW_UpdateHeaderSize [Internal]
1661 * Function to resize the header control
1664 * [I] hwnd : handle to a window
1665 * [I] nNewScrollPos : scroll pos to set
1670 static void LISTVIEW_UpdateHeaderSize(const LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1675 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1677 GetWindowRect(infoPtr->hwndHeader, &winRect);
1678 point[0].x = winRect.left;
1679 point[0].y = winRect.top;
1680 point[1].x = winRect.right;
1681 point[1].y = winRect.bottom;
1683 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1684 point[0].x = -nNewScrollPos;
1685 point[1].x += nNewScrollPos;
1687 SetWindowPos(infoPtr->hwndHeader,0,
1688 point[0].x,point[0].y,point[1].x,point[1].y,
1689 (infoPtr->dwStyle & LVS_NOCOLUMNHEADER) ? SWP_HIDEWINDOW : SWP_SHOWWINDOW |
1690 SWP_NOZORDER | SWP_NOACTIVATE);
1695 * Update the scrollbars. This functions should be called whenever
1696 * the content, size or view changes.
1699 * [I] infoPtr : valid pointer to the listview structure
1704 static void LISTVIEW_UpdateScroll(const LISTVIEW_INFO *infoPtr)
1706 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1707 SCROLLINFO horzInfo, vertInfo;
1710 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1712 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1713 horzInfo.cbSize = sizeof(SCROLLINFO);
1714 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1716 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1717 if (uView == LVS_LIST)
1719 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1720 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1722 /* scroll by at least one column per page */
1723 if(horzInfo.nPage < infoPtr->nItemWidth)
1724 horzInfo.nPage = infoPtr->nItemWidth;
1726 horzInfo.nPage /= infoPtr->nItemWidth;
1728 else if (uView == LVS_REPORT)
1730 horzInfo.nMax = infoPtr->nItemWidth;
1732 else /* LVS_ICON, or LVS_SMALLICON */
1736 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1739 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1740 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1741 dx = GetScrollPos(infoPtr->hwndSelf, SB_HORZ);
1742 dx -= SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1743 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1745 /* Setting the horizontal scroll can change the listview size
1746 * (and potentially everything else) so we need to recompute
1747 * everything again for the vertical scroll
1750 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1751 vertInfo.cbSize = sizeof(SCROLLINFO);
1752 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1754 if (uView == LVS_REPORT)
1756 vertInfo.nMax = infoPtr->nItemCount;
1758 /* scroll by at least one page */
1759 if(vertInfo.nPage < infoPtr->nItemHeight)
1760 vertInfo.nPage = infoPtr->nItemHeight;
1762 if (infoPtr->nItemHeight > 0)
1763 vertInfo.nPage /= infoPtr->nItemHeight;
1765 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1769 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1772 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1773 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1774 dy = GetScrollPos(infoPtr->hwndSelf, SB_VERT);
1775 dy -= SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1776 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1778 /* Change of the range may have changed the scroll pos. If so move the content */
1779 if (dx != 0 || dy != 0)
1782 listRect = infoPtr->rcList;
1783 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &listRect, &listRect, 0, 0,
1784 SW_ERASE | SW_INVALIDATE);
1787 /* Update the Header Control */
1788 if (uView == LVS_REPORT)
1790 horzInfo.fMask = SIF_POS;
1791 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1792 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1799 * Shows/hides the focus rectangle.
1802 * [I] infoPtr : valid pointer to the listview structure
1803 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1808 static void LISTVIEW_ShowFocusRect(const LISTVIEW_INFO *infoPtr, BOOL fShow)
1810 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1813 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1815 if (infoPtr->nFocusedItem < 0) return;
1817 /* we need some gymnastics in ICON mode to handle large items */
1818 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1822 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1823 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1825 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1830 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1832 /* for some reason, owner draw should work only in report mode */
1833 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1838 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
1839 HFONT hOldFont = SelectObject(hdc, hFont);
1841 item.iItem = infoPtr->nFocusedItem;
1843 item.mask = LVIF_PARAM;
1844 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1846 ZeroMemory(&dis, sizeof(dis));
1847 dis.CtlType = ODT_LISTVIEW;
1848 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1849 dis.itemID = item.iItem;
1850 dis.itemAction = ODA_FOCUS;
1851 if (fShow) dis.itemState |= ODS_FOCUS;
1852 dis.hwndItem = infoPtr->hwndSelf;
1854 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1855 dis.itemData = item.lParam;
1857 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1859 SelectObject(hdc, hOldFont);
1863 DrawFocusRect(hdc, &infoPtr->rcFocus);
1866 ReleaseDC(infoPtr->hwndSelf, hdc);
1870 * Invalidates all visible selected items.
1872 static void LISTVIEW_InvalidateSelectedItems(const LISTVIEW_INFO *infoPtr)
1876 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1877 while(iterator_next(&i))
1879 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1880 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1882 iterator_destroy(&i);
1887 * DESCRIPTION: [INTERNAL]
1888 * Computes an item's (left,top) corner, relative to rcView.
1889 * That is, the position has NOT been made relative to the Origin.
1890 * This is deliberate, to avoid computing the Origin over, and
1891 * over again, when this function is called in a loop. Instead,
1892 * one can factor the computation of the Origin before the loop,
1893 * and offset the value returned by this function, on every iteration.
1896 * [I] infoPtr : valid pointer to the listview structure
1897 * [I] nItem : item number
1898 * [O] lpptOrig : item top, left corner
1903 static void LISTVIEW_GetItemOrigin(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1905 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1907 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1909 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1911 lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1912 lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1914 else if (uView == LVS_LIST)
1916 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1917 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1918 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1920 else /* LVS_REPORT */
1922 lpptPosition->x = 0;
1923 lpptPosition->y = nItem * infoPtr->nItemHeight;
1928 * DESCRIPTION: [INTERNAL]
1929 * Compute the rectangles of an item. This is to localize all
1930 * the computations in one place. If you are not interested in some
1931 * of these values, simply pass in a NULL -- the function is smart
1932 * enough to compute only what's necessary. The function computes
1933 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1934 * one, the BOX rectangle. This rectangle is very cheap to compute,
1935 * and is guaranteed to contain all the other rectangles. Computing
1936 * the ICON rect is also cheap, but all the others are potentially
1937 * expensive. This gives an easy and effective optimization when
1938 * searching (like point inclusion, or rectangle intersection):
1939 * first test against the BOX, and if TRUE, test against the desired
1941 * If the function does not have all the necessary information
1942 * to computed the requested rectangles, will crash with a
1943 * failed assertion. This is done so we catch all programming
1944 * errors, given that the function is called only from our code.
1946 * We have the following 'special' meanings for a few fields:
1947 * * If LVIS_FOCUSED is set, we assume the item has the focus
1948 * This is important in ICON mode, where it might get a larger
1949 * then usual rectangle
1951 * Please note that subitem support works only in REPORT mode.
1954 * [I] infoPtr : valid pointer to the listview structure
1955 * [I] lpLVItem : item to compute the measures for
1956 * [O] lprcBox : ptr to Box rectangle
1957 * Same as LVM_GETITEMRECT with LVIR_BOUNDS
1958 * [0] lprcSelectBox : ptr to select box rectangle
1959 * Same as LVM_GETITEMRECT with LVIR_SELECTEDBOUNDS
1960 * [O] lprcIcon : ptr to Icon rectangle
1961 * Same as LVM_GETITEMRECT with LVIR_ICON
1962 * [O] lprcStateIcon: ptr to State Icon rectangle
1963 * [O] lprcLabel : ptr to Label rectangle
1964 * Same as LVM_GETITEMRECT with LVIR_LABEL
1969 static void LISTVIEW_GetItemMetrics(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1970 LPRECT lprcBox, LPRECT lprcSelectBox,
1971 LPRECT lprcIcon, LPRECT lprcStateIcon, LPRECT lprcLabel)
1973 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1974 BOOL doSelectBox = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1975 RECT Box, SelectBox, Icon, Label;
1976 COLUMN_INFO *lpColumnInfo = NULL;
1977 SIZE labelSize = { 0, 0 };
1979 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1981 /* Be smart and try to figure out the minimum we have to do */
1982 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1983 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1985 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1986 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1988 if (lprcSelectBox) doSelectBox = TRUE;
1989 if (lprcLabel) doLabel = TRUE;
1990 if (doLabel || lprcIcon || lprcStateIcon) doIcon = TRUE;
1997 /************************************************************/
1998 /* compute the box rectangle (it should be cheap to do) */
1999 /************************************************************/
2000 if (lpLVItem->iSubItem || uView == LVS_REPORT)
2001 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
2003 if (lpLVItem->iSubItem)
2005 Box = lpColumnInfo->rcHeader;
2010 Box.right = infoPtr->nItemWidth;
2013 Box.bottom = infoPtr->nItemHeight;
2015 /******************************************************************/
2016 /* compute ICON bounding box (ala LVM_GETITEMRECT) and STATEICON */
2017 /******************************************************************/
2020 LONG state_width = 0;
2022 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
2023 state_width = infoPtr->iconStateSize.cx;
2025 if (uView == LVS_ICON)
2027 Icon.left = Box.left + state_width;
2028 if (infoPtr->himlNormal)
2029 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx - state_width) / 2;
2030 Icon.top = Box.top + ICON_TOP_PADDING;
2031 Icon.right = Icon.left;
2032 Icon.bottom = Icon.top;
2033 if (infoPtr->himlNormal)
2035 Icon.right += infoPtr->iconSize.cx;
2036 Icon.bottom += infoPtr->iconSize.cy;
2039 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2041 Icon.left = Box.left + state_width;
2043 if (uView == LVS_REPORT)
2044 Icon.left += REPORT_MARGINX;
2047 Icon.right = Icon.left;
2048 if (infoPtr->himlSmall &&
2049 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
2050 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
2051 Icon.right += infoPtr->iconSize.cx;
2052 Icon.bottom = Icon.top + infoPtr->iconSize.cy;
2054 if(lprcIcon) *lprcIcon = Icon;
2055 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon));
2057 /* TODO: is this correct? */
2060 lprcStateIcon->left = Icon.left - state_width;
2061 lprcStateIcon->right = Icon.left;
2062 lprcStateIcon->top = Icon.top;
2063 lprcStateIcon->bottom = lprcStateIcon->top + infoPtr->iconSize.cy;
2064 TRACE(" - state icon=%s\n", wine_dbgstr_rect(lprcStateIcon));
2067 else Icon.right = 0;
2069 /************************************************************/
2070 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2071 /************************************************************/
2074 /* calculate how far to the right can the label stretch */
2075 Label.right = Box.right;
2076 if (uView == LVS_REPORT)
2078 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
2081 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
2083 labelSize.cx = infoPtr->nItemWidth;
2084 labelSize.cy = infoPtr->nItemHeight;
2088 /* we need the text in non owner draw mode */
2089 assert(lpLVItem->mask & LVIF_TEXT);
2090 if (is_textT(lpLVItem->pszText, TRUE))
2092 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2093 HDC hdc = GetDC(infoPtr->hwndSelf);
2094 HFONT hOldFont = SelectObject(hdc, hFont);
2098 /* compute rough rectangle where the label will go */
2099 SetRectEmpty(&rcText);
2100 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2101 rcText.bottom = infoPtr->nItemHeight;
2102 if (uView == LVS_ICON)
2103 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2105 /* now figure out the flags */
2106 if (uView == LVS_ICON)
2107 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2109 uFormat = LV_SL_DT_FLAGS;
2111 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2113 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2114 labelSize.cy = rcText.bottom - rcText.top;
2116 SelectObject(hdc, hOldFont);
2117 ReleaseDC(infoPtr->hwndSelf, hdc);
2121 if (uView == LVS_ICON)
2123 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2124 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2125 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2126 Label.right = Label.left + labelSize.cx;
2127 Label.bottom = Label.top + infoPtr->nItemHeight;
2128 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2130 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2131 labelSize.cy /= infoPtr->ntmHeight;
2132 labelSize.cy = max(labelSize.cy, 1);
2133 labelSize.cy *= infoPtr->ntmHeight;
2135 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2137 else if (uView == LVS_REPORT)
2139 Label.left = Icon.right;
2140 Label.top = Box.top;
2141 Label.right = lpColumnInfo->rcHeader.right;
2142 Label.bottom = Label.top + infoPtr->nItemHeight;
2144 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2146 Label.left = Icon.right;
2147 Label.top = Box.top;
2148 Label.right = min(Label.left + labelSize.cx, Label.right);
2149 Label.bottom = Label.top + infoPtr->nItemHeight;
2152 if (lprcLabel) *lprcLabel = Label;
2153 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label));
2156 /************************************************************/
2157 /* compute STATEICON bounding box */
2158 /************************************************************/
2161 if (uView == LVS_REPORT)
2163 SelectBox.left = Icon.right; /* FIXME: should be Icon.left */
2164 SelectBox.top = Box.top;
2165 SelectBox.bottom = Box.bottom;
2166 if (lpLVItem->iSubItem == 0)
2168 /* we need the indent in report mode */
2169 assert(lpLVItem->mask & LVIF_INDENT);
2170 SelectBox.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
2172 SelectBox.right = min(SelectBox.left + labelSize.cx, Label.right);
2176 UnionRect(&SelectBox, &Icon, &Label);
2178 if (lprcSelectBox) *lprcSelectBox = SelectBox;
2179 TRACE(" - select box=%s\n", wine_dbgstr_rect(&SelectBox));
2182 /* Fix the Box if necessary */
2185 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2186 else *lprcBox = Box;
2188 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box));
2192 * DESCRIPTION: [INTERNAL]
2195 * [I] infoPtr : valid pointer to the listview structure
2196 * [I] nItem : item number
2197 * [O] lprcBox : ptr to Box rectangle
2202 static void LISTVIEW_GetItemBox(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2204 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2205 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2206 POINT Position, Origin;
2209 LISTVIEW_GetOrigin(infoPtr, &Origin);
2210 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2212 /* Be smart and try to figure out the minimum we have to do */
2214 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2215 lvItem.mask |= LVIF_TEXT;
2216 lvItem.iItem = nItem;
2217 lvItem.iSubItem = 0;
2218 lvItem.pszText = szDispText;
2219 lvItem.cchTextMax = DISP_TEXT_SIZE;
2220 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2221 if (uView == LVS_ICON)
2223 lvItem.mask |= LVIF_STATE;
2224 lvItem.stateMask = LVIS_FOCUSED;
2225 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2227 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0, 0);
2229 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2235 * Returns the current icon position, and advances it along the top.
2236 * The returned position is not offset by Origin.
2239 * [I] infoPtr : valid pointer to the listview structure
2240 * [O] lpPos : will get the current icon position
2245 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2247 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2249 *lpPos = infoPtr->currIconPos;
2251 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2252 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2254 infoPtr->currIconPos.x = 0;
2255 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2261 * Returns the current icon position, and advances it down the left edge.
2262 * The returned position is not offset by Origin.
2265 * [I] infoPtr : valid pointer to the listview structure
2266 * [O] lpPos : will get the current icon position
2271 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2273 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2275 *lpPos = infoPtr->currIconPos;
2277 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2278 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2280 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2281 infoPtr->currIconPos.y = 0;
2287 * Moves an icon to the specified position.
2288 * It takes care of invalidating the item, etc.
2291 * [I] infoPtr : valid pointer to the listview structure
2292 * [I] nItem : the item to move
2293 * [I] lpPos : the new icon position
2294 * [I] isNew : flags the item as being new
2300 static BOOL LISTVIEW_MoveIconTo(const LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2306 old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2307 old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2309 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2310 LISTVIEW_InvalidateItem(infoPtr, nItem);
2313 /* Allocating a POINTER for every item is too resource intensive,
2314 * so we'll keep the (x,y) in different arrays */
2315 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2316 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2318 LISTVIEW_InvalidateItem(infoPtr, nItem);
2325 * Arranges listview items in icon display mode.
2328 * [I] infoPtr : valid pointer to the listview structure
2329 * [I] nAlignCode : alignment code
2335 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2337 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2338 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2342 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2344 TRACE("nAlignCode=%d\n", nAlignCode);
2346 if (nAlignCode == LVA_DEFAULT)
2348 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2349 else nAlignCode = LVA_ALIGNTOP;
2354 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2355 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2356 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2357 default: return FALSE;
2360 infoPtr->bAutoarrange = TRUE;
2361 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2362 for (i = 0; i < infoPtr->nItemCount; i++)
2364 next_pos(infoPtr, &pos);
2365 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2373 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2376 * [I] infoPtr : valid pointer to the listview structure
2377 * [O] lprcView : bounding rectangle
2383 static void LISTVIEW_GetAreaRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2387 SetRectEmpty(lprcView);
2389 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2393 for (i = 0; i < infoPtr->nItemCount; i++)
2395 x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2396 y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2397 lprcView->right = max(lprcView->right, x);
2398 lprcView->bottom = max(lprcView->bottom, y);
2400 if (infoPtr->nItemCount > 0)
2402 lprcView->right += infoPtr->nItemWidth;
2403 lprcView->bottom += infoPtr->nItemHeight;
2408 y = LISTVIEW_GetCountPerColumn(infoPtr);
2409 x = infoPtr->nItemCount / y;
2410 if (infoPtr->nItemCount % y) x++;
2411 lprcView->right = x * infoPtr->nItemWidth;
2412 lprcView->bottom = y * infoPtr->nItemHeight;
2416 lprcView->right = infoPtr->nItemWidth;
2417 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2424 * Retrieves the bounding rectangle of all the items.
2427 * [I] infoPtr : valid pointer to the listview structure
2428 * [O] lprcView : bounding rectangle
2434 static BOOL LISTVIEW_GetViewRect(const LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2438 TRACE("(lprcView=%p)\n", lprcView);
2440 if (!lprcView) return FALSE;
2442 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2443 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2444 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2446 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView));
2453 * Retrieves the subitem pointer associated with the subitem index.
2456 * [I] hdpaSubItems : DPA handle for a specific item
2457 * [I] nSubItem : index of subitem
2460 * SUCCESS : subitem pointer
2463 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2465 SUBITEM_INFO *lpSubItem;
2468 /* we should binary search here if need be */
2469 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2471 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
2472 if (lpSubItem->iSubItem == nSubItem)
2482 * Calculates the desired item width.
2485 * [I] infoPtr : valid pointer to the listview structure
2488 * The desired item width.
2490 static INT LISTVIEW_CalculateItemWidth(const LISTVIEW_INFO *infoPtr)
2492 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2495 TRACE("uView=%d\n", uView);
2497 if (uView == LVS_ICON)
2498 nItemWidth = infoPtr->iconSpacing.cx;
2499 else if (uView == LVS_REPORT)
2503 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2505 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2506 nItemWidth = rcHeader.right;
2509 else /* LVS_SMALLICON, or LVS_LIST */
2513 for (i = 0; i < infoPtr->nItemCount; i++)
2514 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2516 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2517 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2519 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2522 return max(nItemWidth, 1);
2527 * Calculates the desired item height.
2530 * [I] infoPtr : valid pointer to the listview structure
2533 * The desired item height.
2535 static INT LISTVIEW_CalculateItemHeight(const LISTVIEW_INFO *infoPtr)
2537 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2540 TRACE("uView=%d\n", uView);
2542 if (uView == LVS_ICON)
2543 nItemHeight = infoPtr->iconSpacing.cy;
2546 nItemHeight = infoPtr->ntmHeight;
2547 if (uView == LVS_REPORT && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
2549 if (infoPtr->himlState)
2550 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2551 if (infoPtr->himlSmall)
2552 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2553 if (infoPtr->himlState || infoPtr->himlSmall)
2554 nItemHeight += HEIGHT_PADDING;
2555 if (infoPtr->nMeasureItemHeight > 0)
2556 nItemHeight = infoPtr->nMeasureItemHeight;
2559 return max(nItemHeight, 1);
2564 * Updates the width, and height of an item.
2567 * [I] infoPtr : valid pointer to the listview structure
2572 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2574 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2575 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2581 * Retrieves and saves important text metrics info for the current
2585 * [I] infoPtr : valid pointer to the listview structure
2588 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2590 HDC hdc = GetDC(infoPtr->hwndSelf);
2591 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2592 HFONT hOldFont = SelectObject(hdc, hFont);
2596 if (GetTextMetricsW(hdc, &tm))
2598 infoPtr->ntmHeight = tm.tmHeight;
2599 infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
2602 if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
2603 infoPtr->nEllipsisWidth = sz.cx;
2605 SelectObject(hdc, hOldFont);
2606 ReleaseDC(infoPtr->hwndSelf, hdc);
2608 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2613 * A compare function for ranges
2616 * [I] range1 : pointer to range 1;
2617 * [I] range2 : pointer to range 2;
2621 * > 0 : if range 1 > range 2
2622 * < 0 : if range 2 > range 1
2623 * = 0 : if range intersects range 2
2625 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2629 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2631 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2636 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange(range1), debugrange(range2), cmp);
2642 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2644 #define ranges_check(ranges, desc) do { } while(0)
2647 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2652 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2654 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2655 ranges_dump(ranges);
2656 prev = DPA_GetPtr(ranges->hdpa, 0);
2657 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2658 assert (prev->lower >= 0 && prev->lower < prev->upper);
2659 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2661 curr = DPA_GetPtr(ranges->hdpa, i);
2662 assert (prev->upper <= curr->lower);
2663 assert (curr->lower < curr->upper);
2666 TRACE("--- Done checking---\n");
2669 static RANGES ranges_create(int count)
2671 RANGES ranges = Alloc(sizeof(struct tagRANGES));
2672 if (!ranges) return NULL;
2673 ranges->hdpa = DPA_Create(count);
2674 if (ranges->hdpa) return ranges;
2679 static void ranges_clear(RANGES ranges)
2683 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2684 Free(DPA_GetPtr(ranges->hdpa, i));
2685 DPA_DeleteAllPtrs(ranges->hdpa);
2689 static void ranges_destroy(RANGES ranges)
2691 if (!ranges) return;
2692 ranges_clear(ranges);
2693 DPA_Destroy(ranges->hdpa);
2697 static RANGES ranges_clone(RANGES ranges)
2702 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2704 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2706 RANGE *newrng = Alloc(sizeof(RANGE));
2707 if (!newrng) goto fail;
2708 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2709 DPA_SetPtr(clone->hdpa, i, newrng);
2714 TRACE ("clone failed\n");
2715 ranges_destroy(clone);
2719 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2723 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2724 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2729 static void ranges_dump(RANGES ranges)
2733 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2734 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2737 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2739 RANGE srchrng = { nItem, nItem + 1 };
2741 TRACE("(nItem=%d)\n", nItem);
2742 ranges_check(ranges, "before contain");
2743 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2746 static INT ranges_itemcount(RANGES ranges)
2750 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2752 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2753 count += sel->upper - sel->lower;
2759 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2761 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2764 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2765 if (index == -1) return TRUE;
2767 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2769 chkrng = DPA_GetPtr(ranges->hdpa, index);
2770 if (chkrng->lower >= nItem)
2771 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2772 if (chkrng->upper > nItem)
2773 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2778 static BOOL ranges_add(RANGES ranges, RANGE range)
2783 TRACE("(%s)\n", debugrange(&range));
2784 ranges_check(ranges, "before add");
2786 /* try find overlapping regions first */
2787 srchrgn.lower = range.lower - 1;
2788 srchrgn.upper = range.upper + 1;
2789 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2795 TRACE("Adding new range\n");
2797 /* create the brand new range to insert */
2798 newrgn = Alloc(sizeof(RANGE));
2799 if(!newrgn) goto fail;
2802 /* figure out where to insert it */
2803 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2804 TRACE("index=%d\n", index);
2805 if (index == -1) index = 0;
2807 /* and get it over with */
2808 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2816 RANGE *chkrgn, *mrgrgn;
2817 INT fromindex, mergeindex;
2819 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2820 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2822 chkrgn->lower = min(range.lower, chkrgn->lower);
2823 chkrgn->upper = max(range.upper, chkrgn->upper);
2825 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2827 /* merge now common ranges */
2829 srchrgn.lower = chkrgn->lower - 1;
2830 srchrgn.upper = chkrgn->upper + 1;
2834 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2835 if (mergeindex == -1) break;
2836 if (mergeindex == index)
2838 fromindex = index + 1;
2842 TRACE("Merge with index %i\n", mergeindex);
2844 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2845 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2846 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2848 DPA_DeletePtr(ranges->hdpa, mergeindex);
2849 if (mergeindex < index) index --;
2853 ranges_check(ranges, "after add");
2857 ranges_check(ranges, "failed add");
2861 static BOOL ranges_del(RANGES ranges, RANGE range)
2866 TRACE("(%s)\n", debugrange(&range));
2867 ranges_check(ranges, "before del");
2869 /* we don't use DPAS_SORTED here, since we need *
2870 * to find the first overlapping range */
2871 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2874 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2876 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2878 /* case 1: Same range */
2879 if ( (chkrgn->upper == range.upper) &&
2880 (chkrgn->lower == range.lower) )
2882 DPA_DeletePtr(ranges->hdpa, index);
2885 /* case 2: engulf */
2886 else if ( (chkrgn->upper <= range.upper) &&
2887 (chkrgn->lower >= range.lower) )
2889 DPA_DeletePtr(ranges->hdpa, index);
2891 /* case 3: overlap upper */
2892 else if ( (chkrgn->upper <= range.upper) &&
2893 (chkrgn->lower < range.lower) )
2895 chkrgn->upper = range.lower;
2897 /* case 4: overlap lower */
2898 else if ( (chkrgn->upper > range.upper) &&
2899 (chkrgn->lower >= range.lower) )
2901 chkrgn->lower = range.upper;
2904 /* case 5: fully internal */
2907 RANGE tmprgn = *chkrgn, *newrgn;
2909 if (!(newrgn = Alloc(sizeof(RANGE)))) goto fail;
2910 newrgn->lower = chkrgn->lower;
2911 newrgn->upper = range.lower;
2912 chkrgn->lower = range.upper;
2913 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2922 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2925 ranges_check(ranges, "after del");
2929 ranges_check(ranges, "failed del");
2935 * Removes all selection ranges
2938 * [I] infoPtr : valid pointer to the listview structure
2939 * [I] toSkip : item range to skip removing the selection
2945 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2954 lvItem.stateMask = LVIS_SELECTED;
2956 /* need to clone the DPA because callbacks can change it */
2957 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2958 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2959 while(iterator_next(&i))
2960 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2961 /* note that the iterator destructor will free the cloned range */
2962 iterator_destroy(&i);
2967 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2971 if (!(toSkip = ranges_create(1))) return FALSE;
2972 if (nItem != -1) ranges_additem(toSkip, nItem);
2973 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2974 ranges_destroy(toSkip);
2978 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2980 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2985 * Retrieves the number of items that are marked as selected.
2988 * [I] infoPtr : valid pointer to the listview structure
2991 * Number of items selected.
2993 static INT LISTVIEW_GetSelectedCount(const LISTVIEW_INFO *infoPtr)
2995 INT nSelectedCount = 0;
2997 if (infoPtr->uCallbackMask & LVIS_SELECTED)
3000 for (i = 0; i < infoPtr->nItemCount; i++)
3002 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
3007 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
3009 TRACE("nSelectedCount=%d\n", nSelectedCount);
3010 return nSelectedCount;
3015 * Manages the item focus.
3018 * [I] infoPtr : valid pointer to the listview structure
3019 * [I] nItem : item index
3022 * TRUE : focused item changed
3023 * FALSE : focused item has NOT changed
3025 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
3027 INT oldFocus = infoPtr->nFocusedItem;
3030 if (nItem == infoPtr->nFocusedItem) return FALSE;
3032 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
3033 lvItem.stateMask = LVIS_FOCUSED;
3034 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
3036 return oldFocus != infoPtr->nFocusedItem;
3039 /* Helper function for LISTVIEW_ShiftIndices *only* */
3040 static INT shift_item(const LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
3042 if (nShiftItem < nItem) return nShiftItem;
3044 if (nShiftItem > nItem) return nShiftItem + direction;
3046 if (direction > 0) return nShiftItem + direction;
3048 return min(nShiftItem, infoPtr->nItemCount - 1);
3053 * Updates the various indices after an item has been inserted or deleted.
3056 * [I] infoPtr : valid pointer to the listview structure
3057 * [I] nItem : item index
3058 * [I] direction : Direction of shift, +1 or -1.
3063 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
3068 /* temporarily disable change notification while shifting items */
3069 bOldChange = infoPtr->bDoChangeNotify;
3070 infoPtr->bDoChangeNotify = FALSE;
3072 TRACE("Shifting %iu, %i steps\n", nItem, direction);
3074 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
3076 assert(abs(direction) == 1);
3078 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
3080 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
3081 if (nNewFocus != infoPtr->nFocusedItem)
3082 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
3084 /* But we are not supposed to modify nHotItem! */
3086 infoPtr->bDoChangeNotify = bOldChange;
3092 * Adds a block of selections.
3095 * [I] infoPtr : valid pointer to the listview structure
3096 * [I] nItem : item index
3099 * Whether the window is still valid.
3101 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3103 INT nFirst = min(infoPtr->nSelectionMark, nItem);
3104 INT nLast = max(infoPtr->nSelectionMark, nItem);
3105 HWND hwndSelf = infoPtr->hwndSelf;
3106 NMLVODSTATECHANGE nmlv;
3111 /* Temporarily disable change notification
3112 * If the control is LVS_OWNERDATA, we need to send
3113 * only one LVN_ODSTATECHANGED notification.
3114 * See MSDN documentation for LVN_ITEMCHANGED.
3116 bOldChange = infoPtr->bDoChangeNotify;
3117 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3119 if (nFirst == -1) nFirst = nItem;
3121 item.state = LVIS_SELECTED;
3122 item.stateMask = LVIS_SELECTED;
3124 for (i = nFirst; i <= nLast; i++)
3125 LISTVIEW_SetItemState(infoPtr,i,&item);
3127 ZeroMemory(&nmlv, sizeof(nmlv));
3128 nmlv.iFrom = nFirst;
3131 nmlv.uOldState = item.state;
3133 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3134 if (!IsWindow(hwndSelf))
3136 infoPtr->bDoChangeNotify = bOldChange;
3143 * Sets a single group selection.
3146 * [I] infoPtr : valid pointer to the listview structure
3147 * [I] nItem : item index
3152 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3154 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3160 if (!(selection = ranges_create(100))) return;
3162 item.state = LVIS_SELECTED;
3163 item.stateMask = LVIS_SELECTED;
3165 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3167 if (infoPtr->nSelectionMark == -1)
3169 infoPtr->nSelectionMark = nItem;
3170 ranges_additem(selection, nItem);
3176 sel.lower = min(infoPtr->nSelectionMark, nItem);
3177 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3178 ranges_add(selection, sel);
3183 RECT rcItem, rcSel, rcSelMark;
3186 rcItem.left = LVIR_BOUNDS;
3187 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3188 rcSelMark.left = LVIR_BOUNDS;
3189 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3190 UnionRect(&rcSel, &rcItem, &rcSelMark);
3191 iterator_frameditems(&i, infoPtr, &rcSel);
3192 while(iterator_next(&i))
3194 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3195 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3197 iterator_destroy(&i);
3200 /* disable per item notifications on LVS_OWNERDATA style
3201 FIXME: single LVN_ODSTATECHANGED should be used */
3202 bOldChange = infoPtr->bDoChangeNotify;
3203 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3205 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3208 iterator_rangesitems(&i, selection);
3209 while(iterator_next(&i))
3210 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3211 /* this will also destroy the selection */
3212 iterator_destroy(&i);
3214 infoPtr->bDoChangeNotify = bOldChange;
3216 LISTVIEW_SetItemFocus(infoPtr, nItem);
3221 * Sets a single selection.
3224 * [I] infoPtr : valid pointer to the listview structure
3225 * [I] nItem : item index
3230 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3234 TRACE("nItem=%d\n", nItem);
3236 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3238 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3239 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3240 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3242 infoPtr->nSelectionMark = nItem;
3247 * Set selection(s) with keyboard.
3250 * [I] infoPtr : valid pointer to the listview structure
3251 * [I] nItem : item index
3252 * [I] space : VK_SPACE code sent
3255 * SUCCESS : TRUE (needs to be repainted)
3256 * FAILURE : FALSE (nothing has changed)
3258 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem, BOOL space)
3260 /* FIXME: pass in the state */
3261 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3262 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3263 BOOL bResult = FALSE;
3265 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3266 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3268 if (infoPtr->dwStyle & LVS_SINGLESEL)
3271 LISTVIEW_SetSelection(infoPtr, nItem);
3278 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3283 lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3284 lvItem.stateMask = LVIS_SELECTED;
3287 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3288 if (lvItem.state & LVIS_SELECTED)
3289 infoPtr->nSelectionMark = nItem;
3291 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3296 LISTVIEW_SetSelection(infoPtr, nItem);
3299 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3302 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3306 static BOOL LISTVIEW_GetItemAtPt(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3308 LVHITTESTINFO lvHitTestInfo;
3310 ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3311 lvHitTestInfo.pt.x = pt.x;
3312 lvHitTestInfo.pt.y = pt.y;
3314 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3316 lpLVItem->mask = LVIF_PARAM;
3317 lpLVItem->iItem = lvHitTestInfo.iItem;
3318 lpLVItem->iSubItem = 0;
3320 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3323 static inline BOOL LISTVIEW_isHotTracking(const LISTVIEW_INFO *infoPtr)
3325 return ((infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) ||
3326 (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE) ||
3327 (infoPtr->dwLvExStyle & LVS_EX_TWOCLICKACTIVATE));
3332 * Called when the mouse is being actively tracked and has hovered for a specified
3336 * [I] infoPtr : valid pointer to the listview structure
3337 * [I] fwKeys : key indicator
3338 * [I] x,y : mouse position
3341 * 0 if the message was processed, non-zero if there was an error
3344 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3345 * over the item for a certain period of time.
3348 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3350 if (LISTVIEW_isHotTracking(infoPtr))
3358 if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3359 LISTVIEW_SetSelection(infoPtr, item.iItem);
3367 * Called whenever WM_MOUSEMOVE is received.
3370 * [I] infoPtr : valid pointer to the listview structure
3371 * [I] fwKeys : key indicator
3372 * [I] x,y : mouse position
3375 * 0 if the message is processed, non-zero if there was an error
3377 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3379 TRACKMOUSEEVENT trackinfo;
3381 if (!(fwKeys & MK_LBUTTON))
3382 infoPtr->bLButtonDown = FALSE;
3384 if (infoPtr->bLButtonDown)
3388 WORD wDragWidth = GetSystemMetrics(SM_CXDRAG);
3389 WORD wDragHeight= GetSystemMetrics(SM_CYDRAG);
3391 rect.left = infoPtr->ptClickPos.x - wDragWidth;
3392 rect.right = infoPtr->ptClickPos.x + wDragWidth;
3393 rect.top = infoPtr->ptClickPos.y - wDragHeight;
3394 rect.bottom = infoPtr->ptClickPos.y + wDragHeight;
3399 if (!PtInRect(&rect, tmp))
3401 LVHITTESTINFO lvHitTestInfo;
3404 lvHitTestInfo.pt = infoPtr->ptClickPos;
3405 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3407 ZeroMemory(&nmlv, sizeof(nmlv));
3408 nmlv.iItem = lvHitTestInfo.iItem;
3409 nmlv.ptAction = infoPtr->ptClickPos;
3411 if (!infoPtr->bDragging)
3413 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3414 infoPtr->bDragging = TRUE;
3421 infoPtr->bLButtonDown = FALSE;
3423 /* see if we are supposed to be tracking mouse hovering */
3424 if (LISTVIEW_isHotTracking(infoPtr)) {
3425 /* fill in the trackinfo struct */
3426 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3427 trackinfo.dwFlags = TME_QUERY;
3428 trackinfo.hwndTrack = infoPtr->hwndSelf;
3429 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3431 /* see if we are already tracking this hwnd */
3432 _TrackMouseEvent(&trackinfo);
3434 if(!(trackinfo.dwFlags & TME_HOVER)) {
3435 trackinfo.dwFlags = TME_HOVER;
3437 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3438 _TrackMouseEvent(&trackinfo);
3447 * Tests whether the item is assignable to a list with style lStyle
3449 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3451 if ( (lpLVItem->mask & LVIF_TEXT) &&
3452 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3453 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3461 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3464 * [I] infoPtr : valid pointer to the listview structure
3465 * [I] lpLVItem : valid pointer to new item attributes
3466 * [I] isNew : the item being set is being inserted
3467 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3468 * [O] bChanged : will be set to TRUE if the item really changed
3474 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3476 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3484 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3486 if (lpLVItem->mask == 0) return TRUE;
3488 if (infoPtr->dwStyle & LVS_OWNERDATA)
3490 /* a virtual listview only stores selection and focus */
3491 if (lpLVItem->mask & ~LVIF_STATE)
3497 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3498 lpItem = DPA_GetPtr(hdpaSubItems, 0);
3502 /* we need to get the lParam and state of the item */
3503 item.iItem = lpLVItem->iItem;
3504 item.iSubItem = lpLVItem->iSubItem;
3505 item.mask = LVIF_STATE | LVIF_PARAM;
3506 item.stateMask = ~0;
3509 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3511 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3512 /* determine what fields will change */
3513 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3514 uChanged |= LVIF_STATE;
3516 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3517 uChanged |= LVIF_IMAGE;
3519 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3520 uChanged |= LVIF_PARAM;
3522 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3523 uChanged |= LVIF_INDENT;
3525 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3526 uChanged |= LVIF_TEXT;
3528 TRACE("uChanged=0x%x\n", uChanged);
3529 if (!uChanged) return TRUE;
3532 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3533 nmlv.iItem = lpLVItem->iItem;
3534 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3535 nmlv.uOldState = item.state;
3536 nmlv.uChanged = uChanged;
3537 nmlv.lParam = item.lParam;
3539 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3540 /* and we are _NOT_ virtual (LVS_OWNERDATA), and change notifications */
3542 if(lpItem && !isNew && infoPtr->bDoChangeNotify)
3544 HWND hwndSelf = infoPtr->hwndSelf;
3546 if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3548 if (!IsWindow(hwndSelf))
3552 /* copy information */
3553 if (lpLVItem->mask & LVIF_TEXT)
3554 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3556 if (lpLVItem->mask & LVIF_IMAGE)
3557 lpItem->hdr.iImage = lpLVItem->iImage;
3559 if (lpLVItem->mask & LVIF_PARAM)
3560 lpItem->lParam = lpLVItem->lParam;
3562 if (lpLVItem->mask & LVIF_INDENT)
3563 lpItem->iIndent = lpLVItem->iIndent;
3565 if (uChanged & LVIF_STATE)
3567 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3569 lpItem->state &= ~lpLVItem->stateMask;
3570 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3572 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3574 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3575 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3577 else if (lpLVItem->stateMask & LVIS_SELECTED)
3578 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3580 /* if we are asked to change focus, and we manage it, do it */
3581 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3583 if (lpLVItem->state & LVIS_FOCUSED)
3585 LISTVIEW_SetItemFocus(infoPtr, -1);
3586 infoPtr->nFocusedItem = lpLVItem->iItem;
3587 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3589 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3590 infoPtr->nFocusedItem = -1;
3594 /* if we're inserting the item, we're done */
3595 if (isNew) return TRUE;
3597 /* send LVN_ITEMCHANGED notification */
3598 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3599 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3606 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3609 * [I] infoPtr : valid pointer to the listview structure
3610 * [I] lpLVItem : valid pointer to new subitem attributes
3611 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3612 * [O] bChanged : will be set to TRUE if the item really changed
3618 static BOOL set_sub_item(const LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3621 SUBITEM_INFO *lpSubItem;
3623 /* we do not support subitems for virtual listviews */
3624 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3626 /* set subitem only if column is present */
3627 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3629 /* First do some sanity checks */
3630 /* The LVIF_STATE flag is valid for subitems, but does not appear to be
3631 particularly useful. We currently do not actually do anything with
3632 the flag on subitems.
3634 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE | LVIF_STATE)) return FALSE;
3635 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE | LVIF_STATE))) return TRUE;
3637 /* get the subitem structure, and create it if not there */
3638 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3639 assert (hdpaSubItems);
3641 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3644 SUBITEM_INFO *tmpSubItem;
3647 lpSubItem = Alloc(sizeof(SUBITEM_INFO));
3648 if (!lpSubItem) return FALSE;
3649 /* we could binary search here, if need be...*/
3650 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3652 tmpSubItem = DPA_GetPtr(hdpaSubItems, i);
3653 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3655 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3660 lpSubItem->iSubItem = lpLVItem->iSubItem;
3661 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3665 if (lpLVItem->mask & LVIF_IMAGE)
3666 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3668 lpSubItem->hdr.iImage = lpLVItem->iImage;
3672 if (lpLVItem->mask & LVIF_TEXT)
3673 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3675 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3684 * Sets item attributes.
3687 * [I] infoPtr : valid pointer to the listview structure
3688 * [I] lpLVItem : new item attributes
3689 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3695 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, LVITEMW *lpLVItem, BOOL isW)
3697 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3698 HWND hwndSelf = infoPtr->hwndSelf;
3699 LPWSTR pszText = NULL;
3700 BOOL bResult, bChanged = FALSE;
3702 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3704 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3707 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3708 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3710 pszText = lpLVItem->pszText;
3711 lpLVItem->pszText = textdupTtoW(lpLVItem->pszText, isW);
3714 /* actually set the fields */
3715 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3717 if (lpLVItem->iSubItem)
3718 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3720 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3721 if (!IsWindow(hwndSelf))
3724 /* redraw item, if necessary */
3725 if (bChanged && !infoPtr->bIsDrawing)
3727 /* this little optimization eliminates some nasty flicker */
3728 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3729 !(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) &&
3730 lpLVItem->iSubItem > 0 && lpLVItem->iSubItem <= DPA_GetPtrCount(infoPtr->hdpaColumns) )
3731 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3733 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3738 textfreeT(lpLVItem->pszText, isW);
3739 lpLVItem->pszText = pszText;
3747 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3750 * [I] infoPtr : valid pointer to the listview structure
3755 static INT LISTVIEW_GetTopIndex(const LISTVIEW_INFO *infoPtr)
3757 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3759 SCROLLINFO scrollInfo;
3761 scrollInfo.cbSize = sizeof(SCROLLINFO);
3762 scrollInfo.fMask = SIF_POS;
3764 if (uView == LVS_LIST)
3766 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3767 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3769 else if (uView == LVS_REPORT)
3771 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3772 nItem = scrollInfo.nPos;
3776 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3777 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3780 TRACE("nItem=%d\n", nItem);
3788 * Erases the background of the given rectangle
3791 * [I] infoPtr : valid pointer to the listview structure
3792 * [I] hdc : device context handle
3793 * [I] lprcBox : clipping rectangle
3799 static inline BOOL LISTVIEW_FillBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3801 if (!infoPtr->hBkBrush) return FALSE;
3803 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
3805 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3813 * [I] infoPtr : valid pointer to the listview structure
3814 * [I] hdc : device context handle
3815 * [I] nItem : item index
3816 * [I] nSubItem : subitem index
3817 * [I] pos : item position in client coordinates
3818 * [I] cdmode : custom draw mode
3824 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3826 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3827 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3828 static WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3829 DWORD cdsubitemmode = CDRF_DODEFAULT;
3831 RECT rcSelect, rcBox, rcIcon, rcLabel, rcStateIcon;
3832 NMLVCUSTOMDRAW nmlvcd;
3837 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
3839 /* get information needed for drawing the item */
3840 lvItem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
3841 if (nSubItem == 0) lvItem.mask |= LVIF_STATE;
3842 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3843 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3844 lvItem.iItem = nItem;
3845 lvItem.iSubItem = nSubItem;
3848 lvItem.cchTextMax = DISP_TEXT_SIZE;
3849 lvItem.pszText = szDispText;
3850 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3851 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3852 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3853 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = szCallback;
3854 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3856 /* now check if we need to update the focus rectangle */
3857 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3859 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3860 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcSelect, &rcIcon, &rcStateIcon, &rcLabel);
3861 OffsetRect(&rcBox, pos.x, pos.y);
3862 OffsetRect(&rcSelect, pos.x, pos.y);
3863 OffsetRect(&rcIcon, pos.x, pos.y);
3864 OffsetRect(&rcStateIcon, pos.x, pos.y);
3865 OffsetRect(&rcLabel, pos.x, pos.y);
3866 TRACE(" rcBox=%s, rcSelect=%s, rcIcon=%s. rcLabel=%s\n",
3867 wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcSelect),
3868 wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
3870 /* fill in the custom draw structure */
3871 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3873 hOldFont = GetCurrentObject(hdc, OBJ_FONT);
3874 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3875 if (cdmode & CDRF_SKIPDEFAULT) goto postpaint;
3876 if (cdmode & CDRF_NOTIFYITEMDRAW)
3877 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3878 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3879 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3880 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3881 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3883 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3884 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3886 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3887 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
3888 else if ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) == FALSE)
3889 prepaint_setup(infoPtr, hdc, &nmlvcd, TRUE);
3891 /* in full row select, subitems, will just use main item's colors */
3892 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3893 nmlvcd.clrTextBk = CLR_NONE;
3896 if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && (nSubItem == 0))
3898 UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
3901 TRACE("uStateImage=%d\n", uStateImage);
3902 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc,
3903 rcStateIcon.left, rcStateIcon.top, ILD_NORMAL);
3908 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3909 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3911 TRACE("iImage=%d\n", lvItem.iImage);
3912 ImageList_DrawEx(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3913 rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk, CLR_DEFAULT,
3914 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3917 /* Don't bother painting item being edited */
3918 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3920 /* FIXME: temporary hack */
3921 rcSelect.left = rcLabel.left;
3923 /* draw the selection background, if we're drawing the main item */
3926 /* in icon mode, the label rect is really what we want to draw the
3928 if (uView == LVS_ICON)
3931 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3932 rcSelect.right = rcBox.right;
3934 if (nmlvcd.clrTextBk != CLR_NONE)
3935 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3936 if(lprcFocus) *lprcFocus = rcSelect;
3939 /* figure out the text drawing flags */
3940 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3941 if (uView == LVS_ICON)
3942 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3945 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3947 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3948 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3949 default: uFormat |= DT_LEFT;
3952 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3954 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3955 else rcLabel.left += LABEL_HOR_PADDING;
3957 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3959 /* for GRIDLINES reduce the bottom so the text formats correctly */
3960 if (uView == LVS_REPORT && infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
3963 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3966 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3967 notify_postpaint(infoPtr, &nmlvcd);
3968 if (cdsubitemmode & CDRF_NEWFONT)
3969 SelectObject(hdc, hOldFont);
3975 * Draws listview items when in owner draw mode.
3978 * [I] infoPtr : valid pointer to the listview structure
3979 * [I] hdc : device context handle
3984 static void LISTVIEW_RefreshOwnerDraw(const LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3986 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3987 DWORD cditemmode = CDRF_DODEFAULT;
3988 NMLVCUSTOMDRAW nmlvcd;
3989 POINT Origin, Position;
3995 ZeroMemory(&dis, sizeof(dis));
3997 /* Get scroll info once before loop */
3998 LISTVIEW_GetOrigin(infoPtr, &Origin);
4000 /* iterate through the invalidated rows */
4001 while(iterator_next(i))
4003 item.iItem = i->nItem;
4005 item.mask = LVIF_PARAM | LVIF_STATE;
4006 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4007 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
4009 dis.CtlType = ODT_LISTVIEW;
4011 dis.itemID = item.iItem;
4012 dis.itemAction = ODA_DRAWENTIRE;
4014 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
4015 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
4016 dis.hwndItem = infoPtr->hwndSelf;
4018 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
4019 dis.rcItem.left = Position.x + Origin.x;
4020 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
4021 dis.rcItem.top = Position.y + Origin.y;
4022 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
4023 dis.itemData = item.lParam;
4025 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
4028 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
4029 * structure for the rest. of the paint cycle
4031 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
4032 if (cdmode & CDRF_NOTIFYITEMDRAW)
4033 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4035 if (!(cditemmode & CDRF_SKIPDEFAULT))
4037 prepaint_setup (infoPtr, hdc, &nmlvcd, FALSE);
4038 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
4041 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
4042 notify_postpaint(infoPtr, &nmlvcd);
4048 * Draws listview items when in report display mode.
4051 * [I] infoPtr : valid pointer to the listview structure
4052 * [I] hdc : device context handle
4053 * [I] cdmode : custom draw mode
4058 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4061 RECT rcClip, rcItem;
4062 POINT Origin, Position;
4068 /* figure out what to draw */
4069 rgntype = GetClipBox(hdc, &rcClip);
4070 if (rgntype == NULLREGION) return;
4072 /* Get scroll info once before loop */
4073 LISTVIEW_GetOrigin(infoPtr, &Origin);
4075 /* narrow down the columns we need to paint */
4076 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
4078 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
4079 if (rcItem.right + Origin.x >= rcClip.left) break;
4081 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
4083 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
4084 if (rcItem.left + Origin.x < rcClip.right) break;
4086 iterator_rangeitems(&j, colRange);
4088 /* in full row select, we _have_ to draw the main item */
4089 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
4092 /* iterate through the invalidated rows */
4093 while(iterator_next(i))
4095 /* iterate through the invalidated columns */
4096 while(iterator_next(&j))
4098 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4099 Position.x += Origin.x;
4100 Position.y += Origin.y;
4102 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
4104 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4106 rcItem.bottom = infoPtr->nItemHeight;
4107 OffsetRect(&rcItem, Position.x, Position.y);
4108 if (!RectVisible(hdc, &rcItem)) continue;
4111 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
4114 iterator_destroy(&j);
4119 * Draws the gridlines if necessary when in report display mode.
4122 * [I] infoPtr : valid pointer to the listview structure
4123 * [I] hdc : device context handle
4128 static void LISTVIEW_RefreshReportGrid(LISTVIEW_INFO *infoPtr, HDC hdc)
4133 RECT rcClip, rcItem;
4140 /* figure out what to draw */
4141 rgntype = GetClipBox(hdc, &rcClip);
4142 if (rgntype == NULLREGION) return;
4144 /* Get scroll info once before loop */
4145 LISTVIEW_GetOrigin(infoPtr, &Origin);
4147 /* narrow down the columns we need to paint */
4148 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
4150 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
4151 if (rcItem.right + Origin.x >= rcClip.left) break;
4153 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
4155 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
4156 if (rcItem.left + Origin.x < rcClip.right) break;
4158 iterator_rangeitems(&j, colRange);
4160 if ((hPen = CreatePen( PS_SOLID, 1, comctl32_color.clr3dFace )))
4162 hOldPen = SelectObject ( hdc, hPen );
4164 /* draw the vertical lines for the columns */
4165 iterator_rangeitems(&j, colRange);
4166 while(iterator_next(&j))
4168 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
4169 if (rcItem.left == 0) continue; /* skip first column */
4170 rcItem.left += Origin.x;
4171 rcItem.right += Origin.x;
4172 rcItem.top = infoPtr->rcList.top;
4173 rcItem.bottom = infoPtr->rcList.bottom;
4174 TRACE("vert col=%d, rcItem=%s\n", j.nItem, wine_dbgstr_rect(&rcItem));
4175 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4176 LineTo (hdc, rcItem.left, rcItem.bottom);
4178 iterator_destroy(&j);
4180 /* draw the horizontial lines for the rows */
4181 itemheight = LISTVIEW_CalculateItemHeight(infoPtr);
4182 rcItem.left = infoPtr->rcList.left + Origin.x;
4183 rcItem.right = infoPtr->rcList.right + Origin.x;
4184 rcItem.bottom = rcItem.top = Origin.y - 1;
4185 MoveToEx(hdc, rcItem.left, rcItem.top, NULL);
4186 LineTo(hdc, rcItem.right, rcItem.top);
4187 for(y=itemheight-1+Origin.y; y<=infoPtr->rcList.bottom; y+=itemheight)
4189 rcItem.bottom = rcItem.top = y;
4190 TRACE("horz rcItem=%s\n", wine_dbgstr_rect(&rcItem));
4191 MoveToEx (hdc, rcItem.left, rcItem.top, NULL);
4192 LineTo (hdc, rcItem.right, rcItem.top);
4195 SelectObject( hdc, hOldPen );
4196 DeleteObject( hPen );
4202 * Draws listview items when in list display mode.
4205 * [I] infoPtr : valid pointer to the listview structure
4206 * [I] hdc : device context handle
4207 * [I] cdmode : custom draw mode
4212 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
4214 POINT Origin, Position;
4216 /* Get scroll info once before loop */
4217 LISTVIEW_GetOrigin(infoPtr, &Origin);
4219 while(iterator_prev(i))
4221 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
4222 Position.x += Origin.x;
4223 Position.y += Origin.y;
4225 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
4232 * Draws listview items.
4235 * [I] infoPtr : valid pointer to the listview structure
4236 * [I] hdc : device context handle
4237 * [I] prcErase : rect to be erased before refresh (may be NULL)
4242 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *prcErase)
4244 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4245 COLORREF oldTextColor = 0, oldBkColor = 0, oldClrTextBk, oldClrText;
4246 NMLVCUSTOMDRAW nmlvcd;
4253 HBITMAP hbmp = NULL;
4255 LISTVIEW_DUMP(infoPtr);
4257 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4258 TRACE("double buffering\n");
4260 hdc = CreateCompatibleDC(hdcOrig);
4262 ERR("Failed to create DC for backbuffer\n");
4265 hbmp = CreateCompatibleBitmap(hdcOrig, infoPtr->rcList.right,
4266 infoPtr->rcList.bottom);
4268 ERR("Failed to create bitmap for backbuffer\n");
4273 SelectObject(hdc, hbmp);
4274 SelectObject(hdc, infoPtr->hFont);
4276 /* Save dc values we're gonna trash while drawing
4277 * FIXME: Should be done in LISTVIEW_DrawItem() */
4278 hOldFont = SelectObject(hdc, infoPtr->hFont);
4279 oldBkMode = GetBkMode(hdc);
4280 oldBkColor = GetBkColor(hdc);
4281 oldTextColor = GetTextColor(hdc);
4284 infoPtr->bIsDrawing = TRUE;
4287 LISTVIEW_FillBkgnd(infoPtr, hdc, prcErase);
4288 } else if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) {
4289 /* If no erasing was done (usually because RedrawWindow was called
4290 * with RDW_INVALIDATE only) we need to copy the old contents into
4291 * the backbuffer before continuing. */
4292 BitBlt(hdc, infoPtr->rcList.left, infoPtr->rcList.top,
4293 infoPtr->rcList.right - infoPtr->rcList.left,
4294 infoPtr->rcList.bottom - infoPtr->rcList.top,
4295 hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4298 /* FIXME: Shouldn't need to do this */
4299 oldClrTextBk = infoPtr->clrTextBk;
4300 oldClrText = infoPtr->clrText;
4302 infoPtr->cditemmode = CDRF_DODEFAULT;
4304 GetClientRect(infoPtr->hwndSelf, &rcClient);
4305 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4306 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4307 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4308 prepaint_setup(infoPtr, hdc, &nmlvcd, FALSE);
4310 /* Use these colors to draw the items */
4311 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4312 infoPtr->clrText = nmlvcd.clrText;
4314 /* nothing to draw */
4315 if(infoPtr->nItemCount == 0) goto enddraw;
4317 /* figure out what we need to draw */
4318 iterator_visibleitems(&i, infoPtr, hdc);
4320 /* send cache hint notification */
4321 if (infoPtr->dwStyle & LVS_OWNERDATA)
4323 RANGE range = iterator_range(&i);
4326 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4327 nmlv.iFrom = range.lower;
4328 nmlv.iTo = range.upper - 1;
4329 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4332 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4333 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4336 if (uView == LVS_REPORT)
4337 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4338 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4339 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4341 /* if we have a focus rect, draw it */
4342 if (infoPtr->bFocus)
4343 DrawFocusRect(hdc, &infoPtr->rcFocus);
4345 iterator_destroy(&i);
4348 /* For LVS_EX_GRIDLINES go and draw lines */
4349 /* This includes the case where there were *no* items */
4350 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT &&
4351 infoPtr->dwLvExStyle & LVS_EX_GRIDLINES)
4352 LISTVIEW_RefreshReportGrid(infoPtr, hdc);
4354 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4355 notify_postpaint(infoPtr, &nmlvcd);
4357 infoPtr->clrTextBk = oldClrTextBk;
4358 infoPtr->clrText = oldClrText;
4361 BitBlt(hdcOrig, infoPtr->rcList.left, infoPtr->rcList.top,
4362 infoPtr->rcList.right - infoPtr->rcList.left,
4363 infoPtr->rcList.bottom - infoPtr->rcList.top,
4364 hdc, infoPtr->rcList.left, infoPtr->rcList.top, SRCCOPY);
4369 SelectObject(hdc, hOldFont);
4370 SetBkMode(hdc, oldBkMode);
4371 SetBkColor(hdc, oldBkColor);
4372 SetTextColor(hdc, oldTextColor);
4375 infoPtr->bIsDrawing = FALSE;
4381 * Calculates the approximate width and height of a given number of items.
4384 * [I] infoPtr : valid pointer to the listview structure
4385 * [I] nItemCount : number of items
4386 * [I] wWidth : width
4387 * [I] wHeight : height
4390 * Returns a DWORD. The width in the low word and the height in high word.
4392 static DWORD LISTVIEW_ApproximateViewRect(const LISTVIEW_INFO *infoPtr, INT nItemCount,
4393 WORD wWidth, WORD wHeight)
4395 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4396 INT nItemCountPerColumn = 1;
4397 INT nColumnCount = 0;
4398 DWORD dwViewRect = 0;
4400 if (nItemCount == -1)
4401 nItemCount = infoPtr->nItemCount;
4403 if (uView == LVS_LIST)
4405 if (wHeight == 0xFFFF)
4407 /* use current height */
4408 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4411 if (wHeight < infoPtr->nItemHeight)
4412 wHeight = infoPtr->nItemHeight;
4416 if (infoPtr->nItemHeight > 0)
4418 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4419 if (nItemCountPerColumn == 0)
4420 nItemCountPerColumn = 1;
4422 if (nItemCount % nItemCountPerColumn != 0)
4423 nColumnCount = nItemCount / nItemCountPerColumn;
4425 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4429 /* Microsoft padding magic */
4430 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4431 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4433 dwViewRect = MAKELONG(wWidth, wHeight);
4435 else if (uView == LVS_REPORT)
4439 if (infoPtr->nItemCount > 0)
4441 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4442 wWidth = rcBox.right - rcBox.left;
4443 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4447 /* use current height and width */
4448 if (wHeight == 0xffff)
4449 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4450 if (wWidth == 0xffff)
4451 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4454 dwViewRect = MAKELONG(wWidth, wHeight);
4456 else if (uView == LVS_SMALLICON)
4457 FIXME("uView == LVS_SMALLICON: not implemented\n");
4458 else if (uView == LVS_ICON)
4459 FIXME("uView == LVS_ICON: not implemented\n");
4467 * Create a drag image list for the specified item.
4470 * [I] infoPtr : valid pointer to the listview structure
4471 * [I] iItem : index of item
4472 * [O] lppt : Upperr-left corner of the image
4475 * Returns a handle to the image list if successful, NULL otherwise.
4477 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4483 HBITMAP hbmp, hOldbmp;
4484 HIMAGELIST dragList = 0;
4485 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
4487 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4490 rcItem.left = LVIR_BOUNDS;
4491 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4494 lppt->x = rcItem.left;
4495 lppt->y = rcItem.top;
4497 size.cx = rcItem.right - rcItem.left;
4498 size.cy = rcItem.bottom - rcItem.top;
4500 hdcOrig = GetDC(infoPtr->hwndSelf);
4501 hdc = CreateCompatibleDC(hdcOrig);
4502 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4503 hOldbmp = SelectObject(hdc, hbmp);
4505 rcItem.left = rcItem.top = 0;
4506 rcItem.right = size.cx;
4507 rcItem.bottom = size.cy;
4508 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4511 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4513 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4514 SelectObject(hdc, hOldbmp);
4515 ImageList_Add(dragList, hbmp, 0);
4518 SelectObject(hdc, hOldbmp);
4522 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4524 TRACE("ret=%p\n", dragList);
4532 * Removes all listview items and subitems.
4535 * [I] infoPtr : valid pointer to the listview structure
4541 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr, BOOL destroy)
4544 HDPA hdpaSubItems = NULL;
4551 /* we do it directly, to avoid notifications */
4552 ranges_clear(infoPtr->selectionRanges);
4553 infoPtr->nSelectionMark = -1;
4554 infoPtr->nFocusedItem = -1;
4555 SetRectEmpty(&infoPtr->rcFocus);
4556 /* But we are supposed to leave nHotItem as is! */
4559 /* send LVN_DELETEALLITEMS notification */
4560 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4562 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4564 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4566 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4568 /* send LVN_DELETEITEM notification, if not suppressed
4569 and if it is not a virtual listview */
4570 if (!bSuppress) notify_deleteitem(infoPtr, i);
4571 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
4572 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4574 hdrItem = DPA_GetPtr(hdpaSubItems, j);
4575 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4578 DPA_Destroy(hdpaSubItems);
4579 DPA_DeletePtr(infoPtr->hdpaItems, i);
4581 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4582 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4583 infoPtr->nItemCount --;
4588 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4589 LISTVIEW_UpdateScroll(infoPtr);
4591 LISTVIEW_InvalidateList(infoPtr);
4598 * Scrolls, and updates the columns, when a column is changing width.
4601 * [I] infoPtr : valid pointer to the listview structure
4602 * [I] nColumn : column to scroll
4603 * [I] dx : amount of scroll, in pixels
4608 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4610 COLUMN_INFO *lpColumnInfo;
4615 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4616 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4617 rcCol = lpColumnInfo->rcHeader;
4618 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4619 rcCol.left = rcCol.right;
4621 /* adjust the other columns */
4622 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4624 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4625 lpColumnInfo->rcHeader.left += dx;
4626 lpColumnInfo->rcHeader.right += dx;
4629 /* do not update screen if not in report mode */
4630 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4632 /* if we have a focus, we must first erase the focus rect */
4633 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4635 /* Need to reset the item width when inserting a new column */
4636 infoPtr->nItemWidth += dx;
4638 LISTVIEW_UpdateScroll(infoPtr);
4639 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4641 /* scroll to cover the deleted column, and invalidate for redraw */
4642 rcOld = infoPtr->rcList;
4643 rcOld.left = ptOrigin.x + rcCol.left + dx;
4644 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4646 /* we can restore focus now */
4647 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4652 * Removes a column from the listview control.
4655 * [I] infoPtr : valid pointer to the listview structure
4656 * [I] nColumn : column index
4662 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4666 TRACE("nColumn=%d\n", nColumn);
4668 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4669 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4671 /* While the MSDN specifically says that column zero should not be deleted,
4672 what actually happens is that the column itself is deleted but no items or subitems
4676 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4678 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4681 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4682 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4684 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4686 SUBITEM_INFO *lpSubItem, *lpDelItem;
4688 INT nItem, nSubItem, i;
4690 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4692 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
4695 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4697 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
4698 if (lpSubItem->iSubItem == nColumn)
4701 lpDelItem = lpSubItem;
4703 else if (lpSubItem->iSubItem > nColumn)
4705 lpSubItem->iSubItem--;
4709 /* if we found our subitem, zapp it */
4713 if (is_textW(lpDelItem->hdr.pszText))
4714 Free(lpDelItem->hdr.pszText);
4719 /* free dpa memory */
4720 DPA_DeletePtr(hdpaSubItems, nSubItem);
4725 /* update the other column info */
4726 LISTVIEW_UpdateItemSize(infoPtr);
4727 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4728 LISTVIEW_InvalidateList(infoPtr);
4730 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4737 * Invalidates the listview after an item's insertion or deletion.
4740 * [I] infoPtr : valid pointer to the listview structure
4741 * [I] nItem : item index
4742 * [I] dir : -1 if deleting, 1 if inserting
4747 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4749 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4750 INT nPerCol, nItemCol, nItemRow;
4754 /* if we don't refresh, what's the point of scrolling? */
4755 if (!is_redrawing(infoPtr)) return;
4757 assert (abs(dir) == 1);
4759 /* arrange icons if autoarrange is on */
4760 if (is_autoarrange(infoPtr))
4762 BOOL arrange = TRUE;
4763 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4764 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4765 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4768 /* scrollbars need updating */
4769 LISTVIEW_UpdateScroll(infoPtr);
4771 /* figure out the item's position */
4772 if (uView == LVS_REPORT)
4773 nPerCol = infoPtr->nItemCount + 1;
4774 else if (uView == LVS_LIST)
4775 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4776 else /* LVS_ICON, or LVS_SMALLICON */
4779 nItemCol = nItem / nPerCol;
4780 nItemRow = nItem % nPerCol;
4781 LISTVIEW_GetOrigin(infoPtr, &Origin);
4783 /* move the items below up a slot */
4784 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4785 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4786 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4787 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4788 OffsetRect(&rcScroll, Origin.x, Origin.y);
4789 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
4790 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4792 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
4793 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4794 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4797 /* report has only that column, so we're done */
4798 if (uView == LVS_REPORT) return;
4800 /* now for LISTs, we have to deal with the columns to the right */
4801 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4803 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4804 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4805 OffsetRect(&rcScroll, Origin.x, Origin.y);
4806 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4807 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4808 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4813 * Removes an item from the listview control.
4816 * [I] infoPtr : valid pointer to the listview structure
4817 * [I] nItem : item index
4823 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4826 const UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4827 const BOOL is_icon = (uView == LVS_SMALLICON || uView == LVS_ICON);
4829 TRACE("(nItem=%d)\n", nItem);
4831 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4833 /* remove selection, and focus */
4835 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4836 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4838 /* send LVN_DELETEITEM notification. */
4839 if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
4841 /* we need to do this here, because we'll be deleting stuff */
4843 LISTVIEW_InvalidateItem(infoPtr, nItem);
4845 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4851 hdpaSubItems = DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4852 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4854 hdrItem = DPA_GetPtr(hdpaSubItems, i);
4855 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4858 DPA_Destroy(hdpaSubItems);
4863 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4864 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4867 infoPtr->nItemCount--;
4868 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4870 /* now is the invalidation fun */
4872 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4879 * Callback implementation for editlabel control
4882 * [I] infoPtr : valid pointer to the listview structure
4883 * [I] pszText : modified text
4884 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4890 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4892 HWND hwndSelf = infoPtr->hwndSelf;
4893 NMLVDISPINFOW dispInfo;
4894 INT editedItem = infoPtr->nEditLabelItem;
4897 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4899 infoPtr->nEditLabelItem = -1;
4901 ZeroMemory(&dispInfo, sizeof(dispInfo));
4902 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4903 dispInfo.item.iItem = editedItem;
4904 dispInfo.item.iSubItem = 0;
4905 dispInfo.item.stateMask = ~0;
4906 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4909 bSame = (lstrcmpW(dispInfo.item.pszText, pszText) == 0);
4912 LPWSTR tmp = textdupTtoW(pszText, FALSE);
4913 bSame = (lstrcmpW(dispInfo.item.pszText, tmp) == 0);
4914 textfreeT(tmp, FALSE);
4916 if (bSame) return TRUE;
4918 /* add the text from the edit in */
4919 dispInfo.item.mask |= LVIF_TEXT;
4920 dispInfo.item.pszText = pszText;
4921 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4923 /* Do we need to update the Item Text */
4924 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4925 if (!IsWindow(hwndSelf))
4927 if (!pszText) return TRUE;
4929 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4931 HDPA hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, editedItem);
4932 ITEM_INFO* lpItem = DPA_GetPtr(hdpaSubItems, 0);
4933 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4935 LISTVIEW_InvalidateItem(infoPtr, editedItem);
4940 ZeroMemory(&dispInfo, sizeof(dispInfo));
4941 dispInfo.item.mask = LVIF_TEXT;
4942 dispInfo.item.iItem = editedItem;
4943 dispInfo.item.iSubItem = 0;
4944 dispInfo.item.pszText = pszText;
4945 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4946 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4951 * Begin in place editing of specified list view item
4954 * [I] infoPtr : valid pointer to the listview structure
4955 * [I] nItem : item index
4956 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4962 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4964 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4965 NMLVDISPINFOW dispInfo;
4967 HWND hwndSelf = infoPtr->hwndSelf;
4969 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4971 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4972 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4974 infoPtr->nEditLabelItem = nItem;
4976 /* Is the EditBox still there, if so remove it */
4977 if(infoPtr->hwndEdit != 0)
4979 SetFocus(infoPtr->hwndSelf);
4980 infoPtr->hwndEdit = 0;
4983 LISTVIEW_SetSelection(infoPtr, nItem);
4984 LISTVIEW_SetItemFocus(infoPtr, nItem);
4985 LISTVIEW_InvalidateItem(infoPtr, nItem);
4987 rect.left = LVIR_LABEL;
4988 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4990 ZeroMemory(&dispInfo, sizeof(dispInfo));
4991 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4992 dispInfo.item.iItem = nItem;
4993 dispInfo.item.iSubItem = 0;
4994 dispInfo.item.stateMask = ~0;
4995 dispInfo.item.pszText = szDispText;
4996 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4997 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4999 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
5000 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
5001 if (!infoPtr->hwndEdit) return 0;
5003 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
5005 if (!IsWindow(hwndSelf))
5007 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
5008 infoPtr->hwndEdit = 0;
5012 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
5013 SetFocus(infoPtr->hwndEdit);
5014 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
5015 return infoPtr->hwndEdit;
5021 * Ensures the specified item is visible, scrolling into view if necessary.
5024 * [I] infoPtr : valid pointer to the listview structure
5025 * [I] nItem : item index
5026 * [I] bPartial : partially or entirely visible
5032 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
5034 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5035 INT nScrollPosHeight = 0;
5036 INT nScrollPosWidth = 0;
5037 INT nHorzAdjust = 0;
5038 INT nVertAdjust = 0;
5041 RECT rcItem, rcTemp;
5043 rcItem.left = LVIR_BOUNDS;
5044 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
5046 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
5048 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
5050 /* scroll left/right, but in LVS_REPORT mode */
5051 if (uView == LVS_LIST)
5052 nScrollPosWidth = infoPtr->nItemWidth;
5053 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5054 nScrollPosWidth = 1;
5056 if (rcItem.left < infoPtr->rcList.left)
5059 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
5064 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
5068 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
5070 /* scroll up/down, but not in LVS_LIST mode */
5071 if (uView == LVS_REPORT)
5072 nScrollPosHeight = infoPtr->nItemHeight;
5073 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
5074 nScrollPosHeight = 1;
5076 if (rcItem.top < infoPtr->rcList.top)
5079 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
5084 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
5088 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
5090 if (nScrollPosWidth)
5092 INT diff = nHorzDiff / nScrollPosWidth;
5093 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
5094 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
5097 if (nScrollPosHeight)
5099 INT diff = nVertDiff / nScrollPosHeight;
5100 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
5101 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
5109 * Searches for an item with specific characteristics.
5112 * [I] hwnd : window handle
5113 * [I] nStart : base item index
5114 * [I] lpFindInfo : item information to look for
5117 * SUCCESS : index of item
5120 static INT LISTVIEW_FindItemW(const LISTVIEW_INFO *infoPtr, INT nStart,
5121 const LVFINDINFOW *lpFindInfo)
5123 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5124 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5125 BOOL bWrap = FALSE, bNearest = FALSE;
5126 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
5127 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
5128 POINT Position, Destination;
5131 /* Search in virtual listviews should be done by application, not by
5132 listview control, so we just send LVN_ODFINDITEMW and return the result */
5133 if (infoPtr->dwStyle & LVS_OWNERDATA)
5137 nmlv.iStart = nStart;
5138 nmlv.lvfi = *lpFindInfo;
5139 return notify_hdr(infoPtr, LVN_ODFINDITEMW, (LPNMHDR)&nmlv.hdr);
5142 if (!lpFindInfo || nItem < 0) return -1;
5145 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
5147 lvItem.mask |= LVIF_TEXT;
5148 lvItem.pszText = szDispText;
5149 lvItem.cchTextMax = DISP_TEXT_SIZE;
5152 if (lpFindInfo->flags & LVFI_WRAP)
5155 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
5156 (uView == LVS_ICON || uView ==LVS_SMALLICON))
5161 LISTVIEW_GetOrigin(infoPtr, &Origin);
5162 Destination.x = lpFindInfo->pt.x - Origin.x;
5163 Destination.y = lpFindInfo->pt.y - Origin.y;
5164 switch(lpFindInfo->vkDirection)
5166 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
5167 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
5168 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
5169 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
5170 case VK_HOME: Destination.x = Destination.y = 0; break;
5171 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5172 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
5174 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
5175 Destination.x = rcArea.right;
5176 Destination.y = rcArea.bottom;
5178 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
5182 else Destination.x = Destination.y = 0;
5184 /* if LVFI_PARAM is specified, all other flags are ignored */
5185 if (lpFindInfo->flags & LVFI_PARAM)
5187 lvItem.mask |= LVIF_PARAM;
5189 lvItem.mask &= ~LVIF_TEXT;
5193 for (; nItem < nLast; nItem++)
5195 lvItem.iItem = nItem;
5196 lvItem.iSubItem = 0;
5197 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
5199 if (lvItem.mask & LVIF_PARAM)
5201 if (lpFindInfo->lParam == lvItem.lParam)
5207 if (lvItem.mask & LVIF_TEXT)
5209 if (lpFindInfo->flags & LVFI_PARTIAL)
5211 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
5215 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
5219 if (!bNearest) return nItem;
5221 /* This is very inefficient. To do a good job here,
5222 * we need a sorted array of (x,y) item positions */
5223 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5225 /* compute the distance^2 to the destination */
5226 xdist = Destination.x - Position.x;
5227 ydist = Destination.y - Position.y;
5228 dist = xdist * xdist + ydist * ydist;
5230 /* remember the distance, and item if it's closer */
5234 nNearestItem = nItem;
5241 nLast = min(nStart + 1, infoPtr->nItemCount);
5246 return nNearestItem;
5251 * Searches for an item with specific characteristics.
5254 * [I] hwnd : window handle
5255 * [I] nStart : base item index
5256 * [I] lpFindInfo : item information to look for
5259 * SUCCESS : index of item
5262 static INT LISTVIEW_FindItemA(const LISTVIEW_INFO *infoPtr, INT nStart,
5263 const LVFINDINFOA *lpFindInfo)
5265 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
5270 memcpy(&fiw, lpFindInfo, sizeof(fiw));
5271 if (hasText) fiw.psz = strW = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
5272 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
5273 textfreeT(strW, FALSE);
5279 * Retrieves the background image of the listview control.
5282 * [I] infoPtr : valid pointer to the listview structure
5283 * [O] lpBkImage : background image attributes
5289 /* static BOOL LISTVIEW_GetBkImage(const LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
5291 /* FIXME (listview, "empty stub!\n"); */
5297 * Retrieves column attributes.
5300 * [I] infoPtr : valid pointer to the listview structure
5301 * [I] nColumn : column index
5302 * [IO] lpColumn : column information
5303 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
5304 * otherwise it is in fact a LPLVCOLUMNA
5310 static BOOL LISTVIEW_GetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
5312 COLUMN_INFO *lpColumnInfo;
5315 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5316 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
5318 /* initialize memory */
5319 ZeroMemory(&hdi, sizeof(hdi));
5321 if (lpColumn->mask & LVCF_TEXT)
5323 hdi.mask |= HDI_TEXT;
5324 hdi.pszText = lpColumn->pszText;
5325 hdi.cchTextMax = lpColumn->cchTextMax;
5328 if (lpColumn->mask & LVCF_IMAGE)
5329 hdi.mask |= HDI_IMAGE;
5331 if (lpColumn->mask & LVCF_ORDER)
5332 hdi.mask |= HDI_ORDER;
5334 if (lpColumn->mask & LVCF_SUBITEM)
5335 hdi.mask |= HDI_LPARAM;
5337 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
5339 if (lpColumn->mask & LVCF_FMT)
5340 lpColumn->fmt = lpColumnInfo->fmt;
5342 if (lpColumn->mask & LVCF_WIDTH)
5343 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
5345 if (lpColumn->mask & LVCF_IMAGE)
5346 lpColumn->iImage = hdi.iImage;
5348 if (lpColumn->mask & LVCF_ORDER)
5349 lpColumn->iOrder = hdi.iOrder;
5351 if (lpColumn->mask & LVCF_SUBITEM)
5352 lpColumn->iSubItem = hdi.lParam;
5358 static BOOL LISTVIEW_GetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5365 /* FIXME: little hack */
5366 for (i = 0; i < iCount; i++)
5374 * Retrieves the column width.
5377 * [I] infoPtr : valid pointer to the listview structure
5378 * [I] int : column index
5381 * SUCCESS : column width
5384 static INT LISTVIEW_GetColumnWidth(const LISTVIEW_INFO *infoPtr, INT nColumn)
5386 INT nColumnWidth = 0;
5389 TRACE("nColumn=%d\n", nColumn);
5391 /* we have a 'column' in LIST and REPORT mode only */
5392 switch(infoPtr->dwStyle & LVS_TYPEMASK)
5395 nColumnWidth = infoPtr->nItemWidth;
5398 /* We are not using LISTVIEW_GetHeaderRect as this data is updated only after a HDM_ITEMCHANGED.
5399 * There is an application that subclasses the listview, calls LVM_GETCOLUMNWIDTH in the
5400 * HDM_ITEMCHANGED handler and goes into infinite recursion if it receives old data.
5402 * TODO: should we do the same in LVM_GETCOLUMN?
5404 hdItem.mask = HDI_WIDTH;
5405 if (!SendMessageW(infoPtr->hwndHeader, HDM_GETITEMW, nColumn, (LPARAM)&hdItem))
5407 WARN("(%p): HDM_GETITEMW failed for item %d\n", infoPtr->hwndSelf, nColumn);
5410 nColumnWidth = hdItem.cxy;
5414 TRACE("nColumnWidth=%d\n", nColumnWidth);
5415 return nColumnWidth;
5420 * In list or report display mode, retrieves the number of items that can fit
5421 * vertically in the visible area. In icon or small icon display mode,
5422 * retrieves the total number of visible items.
5425 * [I] infoPtr : valid pointer to the listview structure
5428 * Number of fully visible items.
5430 static INT LISTVIEW_GetCountPerPage(const LISTVIEW_INFO *infoPtr)
5432 switch (infoPtr->dwStyle & LVS_TYPEMASK)
5436 return infoPtr->nItemCount;
5438 return LISTVIEW_GetCountPerColumn(infoPtr);
5440 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5448 * Retrieves an image list handle.
5451 * [I] infoPtr : valid pointer to the listview structure
5452 * [I] nImageList : image list identifier
5455 * SUCCESS : image list handle
5458 static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImageList)
5462 case LVSIL_NORMAL: return infoPtr->himlNormal;
5463 case LVSIL_SMALL: return infoPtr->himlSmall;
5464 case LVSIL_STATE: return infoPtr->himlState;
5469 /* LISTVIEW_GetISearchString */
5473 * Retrieves item attributes.
5476 * [I] hwnd : window handle
5477 * [IO] lpLVItem : item info
5478 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5479 * if FALSE, then lpLVItem is a LPLVITEMA.
5482 * This is the internal 'GetItem' interface -- it tries to
5483 * be smart and avoid text copies, if possible, by modifying
5484 * lpLVItem->pszText to point to the text string. Please note
5485 * that this is not always possible (e.g. OWNERDATA), so on
5486 * entry you *must* supply valid values for pszText, and cchTextMax.
5487 * The only difference to the documented interface is that upon
5488 * return, you should use *only* the lpLVItem->pszText, rather than
5489 * the buffer pointer you provided on input. Most code already does
5490 * that, so it's not a problem.
5491 * For the two cases when the text must be copied (that is,
5492 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5498 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5500 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5501 NMLVDISPINFOW dispInfo;
5507 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5509 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5512 if (lpLVItem->mask == 0) return TRUE;
5514 /* make a local copy */
5515 isubitem = lpLVItem->iSubItem;
5517 /* a quick optimization if all we're asked is the focus state
5518 * these queries are worth optimising since they are common,
5519 * and can be answered in constant time, without the heavy accesses */
5520 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5521 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5523 lpLVItem->state = 0;
5524 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5525 lpLVItem->state |= LVIS_FOCUSED;
5529 ZeroMemory(&dispInfo, sizeof(dispInfo));
5531 /* if the app stores all the data, handle it separately */
5532 if (infoPtr->dwStyle & LVS_OWNERDATA)
5534 dispInfo.item.state = 0;
5536 /* apparently, we should not callback for lParam in LVS_OWNERDATA */
5537 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5539 /* NOTE: copy only fields which we _know_ are initialized, some apps
5540 * depend on the uninitialized fields being 0 */
5541 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5542 dispInfo.item.iItem = lpLVItem->iItem;
5543 dispInfo.item.iSubItem = isubitem;
5544 if (lpLVItem->mask & LVIF_TEXT)
5546 dispInfo.item.pszText = lpLVItem->pszText;
5547 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5549 if (lpLVItem->mask & LVIF_STATE)
5550 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5551 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5552 dispInfo.item.stateMask = lpLVItem->stateMask;
5553 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5555 /* full size structure expected - _WIN32IE >= 0x560 */
5556 *lpLVItem = dispInfo.item;
5558 else if (lpLVItem->mask & LVIF_INDENT)
5560 /* indent member expected - _WIN32IE >= 0x300 */
5561 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5565 /* minimal structure expected */
5566 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5568 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5571 /* make sure lParam is zeroed out */
5572 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5574 /* we store only a little state, so if we're not asked, we're done */
5575 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5577 /* if focus is handled by us, report it */
5578 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5580 lpLVItem->state &= ~LVIS_FOCUSED;
5581 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5582 lpLVItem->state |= LVIS_FOCUSED;
5585 /* and do the same for selection, if we handle it */
5586 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5588 lpLVItem->state &= ~LVIS_SELECTED;
5589 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5590 lpLVItem->state |= LVIS_SELECTED;
5596 /* find the item and subitem structures before we proceed */
5597 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5598 lpItem = DPA_GetPtr(hdpaSubItems, 0);
5603 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5604 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5607 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5612 pItemHdr = &lpItem->hdr;
5614 /* Do we need to query the state from the app? */
5615 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5617 dispInfo.item.mask |= LVIF_STATE;
5618 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5621 /* Do we need to enquire about the image? */
5622 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5623 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5625 dispInfo.item.mask |= LVIF_IMAGE;
5626 dispInfo.item.iImage = I_IMAGECALLBACK;
5629 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5630 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5632 dispInfo.item.mask |= LVIF_TEXT;
5633 dispInfo.item.pszText = lpLVItem->pszText;
5634 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5635 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5636 *dispInfo.item.pszText = '\0';
5639 /* If we don't have all the requested info, query the application */
5640 if (dispInfo.item.mask != 0)
5642 dispInfo.item.iItem = lpLVItem->iItem;
5643 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5644 dispInfo.item.lParam = lpItem->lParam;
5645 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5646 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5649 /* we should not store values for subitems */
5650 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5652 /* Now, handle the iImage field */
5653 if (dispInfo.item.mask & LVIF_IMAGE)
5655 lpLVItem->iImage = dispInfo.item.iImage;
5656 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5657 pItemHdr->iImage = dispInfo.item.iImage;
5659 else if (lpLVItem->mask & LVIF_IMAGE)
5661 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5662 lpLVItem->iImage = pItemHdr->iImage;
5664 lpLVItem->iImage = 0;
5667 /* The pszText field */
5668 if (dispInfo.item.mask & LVIF_TEXT)
5670 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5671 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5673 lpLVItem->pszText = dispInfo.item.pszText;
5675 else if (lpLVItem->mask & LVIF_TEXT)
5677 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5678 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5681 /* Next is the lParam field */
5682 if (dispInfo.item.mask & LVIF_PARAM)
5684 lpLVItem->lParam = dispInfo.item.lParam;
5685 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5686 lpItem->lParam = dispInfo.item.lParam;
5688 else if (lpLVItem->mask & LVIF_PARAM)
5689 lpLVItem->lParam = lpItem->lParam;
5691 /* if this is a subitem, we're done */
5692 if (isubitem) return TRUE;
5694 /* ... the state field (this one is different due to uCallbackmask) */
5695 if (lpLVItem->mask & LVIF_STATE)
5697 lpLVItem->state = lpItem->state & lpLVItem->stateMask;
5698 if (dispInfo.item.mask & LVIF_STATE)
5700 lpLVItem->state &= ~dispInfo.item.stateMask;
5701 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5703 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5705 lpLVItem->state &= ~LVIS_FOCUSED;
5706 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5707 lpLVItem->state |= LVIS_FOCUSED;
5709 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5711 lpLVItem->state &= ~LVIS_SELECTED;
5712 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5713 lpLVItem->state |= LVIS_SELECTED;
5717 /* and last, but not least, the indent field */
5718 if (lpLVItem->mask & LVIF_INDENT)
5719 lpLVItem->iIndent = lpItem->iIndent;
5726 * Retrieves item attributes.
5729 * [I] hwnd : window handle
5730 * [IO] lpLVItem : item info
5731 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5732 * if FALSE, then lpLVItem is a LPLVITEMA.
5735 * This is the external 'GetItem' interface -- it properly copies
5736 * the text in the provided buffer.
5742 static BOOL LISTVIEW_GetItemExtT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5747 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5750 pszText = lpLVItem->pszText;
5751 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5752 if (bResult && lpLVItem->pszText != pszText)
5753 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5754 lpLVItem->pszText = pszText;
5762 * Retrieves the position (upper-left) of the listview control item.
5763 * Note that for LVS_ICON style, the upper-left is that of the icon
5764 * and not the bounding box.
5767 * [I] infoPtr : valid pointer to the listview structure
5768 * [I] nItem : item index
5769 * [O] lpptPosition : coordinate information
5775 static BOOL LISTVIEW_GetItemPosition(const LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5777 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5780 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5782 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5784 LISTVIEW_GetOrigin(infoPtr, &Origin);
5785 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5787 if (uView == LVS_ICON)
5789 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5790 lpptPosition->y += ICON_TOP_PADDING;
5792 lpptPosition->x += Origin.x;
5793 lpptPosition->y += Origin.y;
5795 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
5802 * Retrieves the bounding rectangle for a listview control item.
5805 * [I] infoPtr : valid pointer to the listview structure
5806 * [I] nItem : item index
5807 * [IO] lprc : bounding rectangle coordinates
5808 * lprc->left specifies the portion of the item for which the bounding
5809 * rectangle will be retrieved.
5811 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5812 * including the icon and label.
5815 * * Experiment shows that native control returns:
5816 * * width = min (48, length of text line)
5817 * * .left = position.x - (width - iconsize.cx)/2
5818 * * .right = .left + width
5819 * * height = #lines of text * ntmHeight + icon height + 8
5820 * * .top = position.y - 2
5821 * * .bottom = .top + height
5822 * * separation between items .y = itemSpacing.cy - height
5823 * * .x = itemSpacing.cx - width
5824 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5827 * * Experiment shows that native control returns:
5828 * * width = iconSize.cx + 16
5829 * * .left = position.x - (width - iconsize.cx)/2
5830 * * .right = .left + width
5831 * * height = iconSize.cy + 4
5832 * * .top = position.y - 2
5833 * * .bottom = .top + height
5834 * * separation between items .y = itemSpacing.cy - height
5835 * * .x = itemSpacing.cx - width
5836 * LVIR_LABEL Returns the bounding rectangle of the item text.
5839 * * Experiment shows that native control returns:
5840 * * width = text length
5841 * * .left = position.x - width/2
5842 * * .right = .left + width
5843 * * height = ntmH * linecount + 2
5844 * * .top = position.y + iconSize.cy + 6
5845 * * .bottom = .top + height
5846 * * separation between items .y = itemSpacing.cy - height
5847 * * .x = itemSpacing.cx - width
5848 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5849 * rectangles, but excludes columns in report view.
5856 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5857 * upon whether the window has the focus currently and on whether the item
5858 * is the one with the focus. Ensure that the control's record of which
5859 * item has the focus agrees with the items' records.
5861 static BOOL LISTVIEW_GetItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5863 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5864 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5865 BOOL doLabel = TRUE, oversizedBox = FALSE;
5866 POINT Position, Origin;
5869 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5871 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5873 LISTVIEW_GetOrigin(infoPtr, &Origin);
5874 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5876 /* Be smart and try to figure out the minimum we have to do */
5877 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5878 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5879 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5880 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5881 oversizedBox = TRUE;
5883 /* get what we need from the item before hand, so we make
5884 * only one request. This can speed up things, if data
5885 * is stored on the app side */
5887 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5888 if (doLabel) lvItem.mask |= LVIF_TEXT;
5889 lvItem.iItem = nItem;
5890 lvItem.iSubItem = 0;
5891 lvItem.pszText = szDispText;
5892 lvItem.cchTextMax = DISP_TEXT_SIZE;
5893 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5894 /* we got the state already up, simulate it here, to avoid a reget */
5895 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5897 lvItem.mask |= LVIF_STATE;
5898 lvItem.stateMask = LVIS_FOCUSED;
5899 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5902 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5903 lprc->left = LVIR_BOUNDS;
5907 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
5911 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, NULL, lprc);
5915 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
5918 case LVIR_SELECTBOUNDS:
5919 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, lprc, NULL, NULL, NULL);
5923 WARN("Unknown value: %d\n", lprc->left);
5927 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5929 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
5936 * Retrieves the spacing between listview control items.
5939 * [I] infoPtr : valid pointer to the listview structure
5940 * [IO] lprc : rectangle to receive the output
5941 * on input, lprc->top = nSubItem
5942 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5944 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5945 * not only those of the first column.
5946 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5952 static BOOL LISTVIEW_GetSubItemRect(const LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5958 if (!lprc) return FALSE;
5960 nColumn = lprc->top;
5962 TRACE("(nItem=%d, nSubItem=%d)\n", nItem, lprc->top);
5963 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5965 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5967 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5969 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5971 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5974 lvItem.iItem = nItem;
5975 lvItem.iSubItem = nColumn;
5977 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5981 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL, NULL);
5986 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL, NULL);
5990 ERR("Unknown bounds=%d\n", lprc->left);
5994 OffsetRect(lprc, Position.x, Position.y);
6001 * Retrieves the width of a label.
6004 * [I] infoPtr : valid pointer to the listview structure
6007 * SUCCESS : string width (in pixels)
6010 static INT LISTVIEW_GetLabelWidth(const LISTVIEW_INFO *infoPtr, INT nItem)
6012 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6015 TRACE("(nItem=%d)\n", nItem);
6017 lvItem.mask = LVIF_TEXT;
6018 lvItem.iItem = nItem;
6019 lvItem.iSubItem = 0;
6020 lvItem.pszText = szDispText;
6021 lvItem.cchTextMax = DISP_TEXT_SIZE;
6022 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6024 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6029 * Retrieves the spacing between listview control items.
6032 * [I] infoPtr : valid pointer to the listview structure
6033 * [I] bSmall : flag for small or large icon
6036 * Horizontal + vertical spacing
6038 static LONG LISTVIEW_GetItemSpacing(const LISTVIEW_INFO *infoPtr, BOOL bSmall)
6044 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6048 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
6049 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
6051 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
6058 * Retrieves the state of a listview control item.
6061 * [I] infoPtr : valid pointer to the listview structure
6062 * [I] nItem : item index
6063 * [I] uMask : state mask
6066 * State specified by the mask.
6068 static UINT LISTVIEW_GetItemState(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
6072 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6074 lvItem.iItem = nItem;
6075 lvItem.iSubItem = 0;
6076 lvItem.mask = LVIF_STATE;
6077 lvItem.stateMask = uMask;
6078 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
6080 return lvItem.state & uMask;
6085 * Retrieves the text of a listview control item or subitem.
6088 * [I] hwnd : window handle
6089 * [I] nItem : item index
6090 * [IO] lpLVItem : item information
6091 * [I] isW : TRUE if lpLVItem is Unicode
6094 * SUCCESS : string length
6097 static INT LISTVIEW_GetItemTextT(const LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
6099 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
6101 lpLVItem->mask = LVIF_TEXT;
6102 lpLVItem->iItem = nItem;
6103 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
6105 return textlenT(lpLVItem->pszText, isW);
6110 * Searches for an item based on properties + relationships.
6113 * [I] infoPtr : valid pointer to the listview structure
6114 * [I] nItem : item index
6115 * [I] uFlags : relationship flag
6118 * SUCCESS : item index
6121 static INT LISTVIEW_GetNextItem(const LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
6123 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6125 LVFINDINFOW lvFindInfo;
6126 INT nCountPerColumn;
6130 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
6131 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
6133 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
6135 if (uFlags & LVNI_CUT)
6138 if (uFlags & LVNI_DROPHILITED)
6139 uMask |= LVIS_DROPHILITED;
6141 if (uFlags & LVNI_FOCUSED)
6142 uMask |= LVIS_FOCUSED;
6144 if (uFlags & LVNI_SELECTED)
6145 uMask |= LVIS_SELECTED;
6147 /* if we're asked for the focused item, that's only one,
6148 * so it's worth optimizing */
6149 if (uFlags & LVNI_FOCUSED)
6151 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
6152 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
6155 if (uFlags & LVNI_ABOVE)
6157 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6162 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6168 /* Special case for autoarrange - move 'til the top of a list */
6169 if (is_autoarrange(infoPtr))
6171 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6172 while (nItem - nCountPerRow >= 0)
6174 nItem -= nCountPerRow;
6175 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6180 lvFindInfo.flags = LVFI_NEARESTXY;
6181 lvFindInfo.vkDirection = VK_UP;
6182 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6183 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6185 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6190 else if (uFlags & LVNI_BELOW)
6192 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
6194 while (nItem < infoPtr->nItemCount)
6197 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6203 /* Special case for autoarrange - move 'til the bottom of a list */
6204 if (is_autoarrange(infoPtr))
6206 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6207 while (nItem + nCountPerRow < infoPtr->nItemCount )
6209 nItem += nCountPerRow;
6210 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6215 lvFindInfo.flags = LVFI_NEARESTXY;
6216 lvFindInfo.vkDirection = VK_DOWN;
6217 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6218 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6220 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6225 else if (uFlags & LVNI_TOLEFT)
6227 if (uView == LVS_LIST)
6229 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6230 while (nItem - nCountPerColumn >= 0)
6232 nItem -= nCountPerColumn;
6233 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6237 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6239 /* Special case for autoarrange - move 'ti the beginning of a row */
6240 if (is_autoarrange(infoPtr))
6242 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6243 while (nItem % nCountPerRow > 0)
6246 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6251 lvFindInfo.flags = LVFI_NEARESTXY;
6252 lvFindInfo.vkDirection = VK_LEFT;
6253 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6254 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6256 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6261 else if (uFlags & LVNI_TORIGHT)
6263 if (uView == LVS_LIST)
6265 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
6266 while (nItem + nCountPerColumn < infoPtr->nItemCount)
6268 nItem += nCountPerColumn;
6269 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
6273 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6275 /* Special case for autoarrange - move 'til the end of a row */
6276 if (is_autoarrange(infoPtr))
6278 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
6279 while (nItem % nCountPerRow < nCountPerRow - 1 )
6282 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6287 lvFindInfo.flags = LVFI_NEARESTXY;
6288 lvFindInfo.vkDirection = VK_RIGHT;
6289 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
6290 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
6292 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
6301 /* search by index */
6302 for (i = nItem; i < infoPtr->nItemCount; i++)
6304 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
6312 /* LISTVIEW_GetNumberOfWorkAreas */
6316 * Retrieves the origin coordinates when in icon or small icon display mode.
6319 * [I] infoPtr : valid pointer to the listview structure
6320 * [O] lpptOrigin : coordinate information
6325 static void LISTVIEW_GetOrigin(const LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
6327 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6328 INT nHorzPos = 0, nVertPos = 0;
6329 SCROLLINFO scrollInfo;
6331 scrollInfo.cbSize = sizeof(SCROLLINFO);
6332 scrollInfo.fMask = SIF_POS;
6334 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6335 nHorzPos = scrollInfo.nPos;
6336 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6337 nVertPos = scrollInfo.nPos;
6339 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6341 lpptOrigin->x = infoPtr->rcList.left;
6342 lpptOrigin->y = infoPtr->rcList.top;
6343 if (uView == LVS_LIST)
6344 nHorzPos *= infoPtr->nItemWidth;
6345 else if (uView == LVS_REPORT)
6346 nVertPos *= infoPtr->nItemHeight;
6348 lpptOrigin->x -= nHorzPos;
6349 lpptOrigin->y -= nVertPos;
6351 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
6356 * Retrieves the width of a string.
6359 * [I] hwnd : window handle
6360 * [I] lpszText : text string to process
6361 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6364 * SUCCESS : string width (in pixels)
6367 static INT LISTVIEW_GetStringWidthT(const LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6372 if (is_textT(lpszText, isW))
6374 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6375 HDC hdc = GetDC(infoPtr->hwndSelf);
6376 HFONT hOldFont = SelectObject(hdc, hFont);
6379 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6381 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6382 SelectObject(hdc, hOldFont);
6383 ReleaseDC(infoPtr->hwndSelf, hdc);
6385 return stringSize.cx;
6390 * Determines which listview item is located at the specified position.
6393 * [I] infoPtr : valid pointer to the listview structure
6394 * [IO] lpht : hit test information
6395 * [I] subitem : fill out iSubItem.
6396 * [I] select : return the index only if the hit selects the item
6399 * (mm 20001022): We must not allow iSubItem to be touched, for
6400 * an app might pass only a structure with space up to iItem!
6401 * (MS Office 97 does that for instance in the file open dialog)
6404 * SUCCESS : item index
6407 static INT LISTVIEW_HitTest(const LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6409 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6410 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6411 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6412 POINT Origin, Position, opt;
6417 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6421 if (subitem) lpht->iSubItem = 0;
6423 if (infoPtr->rcList.left > lpht->pt.x)
6424 lpht->flags |= LVHT_TOLEFT;
6425 else if (infoPtr->rcList.right < lpht->pt.x)
6426 lpht->flags |= LVHT_TORIGHT;
6428 if (infoPtr->rcList.top > lpht->pt.y)
6429 lpht->flags |= LVHT_ABOVE;
6430 else if (infoPtr->rcList.bottom < lpht->pt.y)
6431 lpht->flags |= LVHT_BELOW;
6433 TRACE("lpht->flags=0x%x\n", lpht->flags);
6434 if (lpht->flags) return -1;
6436 lpht->flags |= LVHT_NOWHERE;
6438 LISTVIEW_GetOrigin(infoPtr, &Origin);
6440 /* first deal with the large items */
6441 rcSearch.left = lpht->pt.x;
6442 rcSearch.top = lpht->pt.y;
6443 rcSearch.right = rcSearch.left + 1;
6444 rcSearch.bottom = rcSearch.top + 1;
6446 iterator_frameditems(&i, infoPtr, &rcSearch);
6447 iterator_next(&i); /* go to first item in the sequence */
6449 iterator_destroy(&i);
6451 TRACE("lpht->iItem=%d\n", iItem);
6452 if (iItem == -1) return -1;
6454 lvItem.mask = LVIF_STATE | LVIF_TEXT;
6455 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6456 lvItem.stateMask = LVIS_STATEIMAGEMASK;
6457 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6458 lvItem.iItem = iItem;
6459 lvItem.iSubItem = 0;
6460 lvItem.pszText = szDispText;
6461 lvItem.cchTextMax = DISP_TEXT_SIZE;
6462 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6463 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6465 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, NULL, &rcIcon, &rcState, &rcLabel);
6466 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6467 opt.x = lpht->pt.x - Position.x - Origin.x;
6468 opt.y = lpht->pt.y - Position.y - Origin.y;
6470 if (uView == LVS_REPORT)
6474 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6475 UnionRect(&rcBounds, &rcBounds, &rcState);
6477 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6478 if (!PtInRect(&rcBounds, opt)) return -1;
6480 if (PtInRect(&rcIcon, opt))
6481 lpht->flags |= LVHT_ONITEMICON;
6482 else if (PtInRect(&rcLabel, opt))
6483 lpht->flags |= LVHT_ONITEMLABEL;
6484 else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6485 lpht->flags |= LVHT_ONITEMSTATEICON;
6486 if (lpht->flags & LVHT_ONITEM)
6487 lpht->flags &= ~LVHT_NOWHERE;
6489 TRACE("lpht->flags=0x%x\n", lpht->flags);
6490 if (uView == LVS_REPORT && subitem)
6494 rcBounds.right = rcBounds.left;
6495 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6497 rcBounds.left = rcBounds.right;
6498 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6499 if (PtInRect(&rcBounds, opt))
6507 if (select && !(uView == LVS_REPORT &&
6508 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6509 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6511 if (uView == LVS_REPORT)
6513 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6514 UnionRect(&rcBounds, &rcBounds, &rcState);
6516 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6518 return lpht->iItem = iItem;
6522 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6523 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6524 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6525 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6526 their own sort proc. when sending LVM_SORTITEMS.
6529 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6531 LVS_SORTXXX must be specified,
6532 LVS_OWNERDRAW is not set,
6533 <item>.pszText is not LPSTR_TEXTCALLBACK.
6535 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6536 are sorted based on item text..."
6538 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6540 ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
6541 ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
6542 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6544 /* if we're sorting descending, negate the return value */
6545 return (((const LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6550 * Inserts a new item in the listview control.
6553 * [I] infoPtr : valid pointer to the listview structure
6554 * [I] lpLVItem : item information
6555 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6558 * SUCCESS : new item index
6561 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6563 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6568 BOOL is_sorted, has_changed;
6570 HWND hwndSelf = infoPtr->hwndSelf;
6572 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6574 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6576 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6577 if (!lpLVItem || lpLVItem->iSubItem) return -1;
6579 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6581 if (!(lpItem = Alloc(sizeof(ITEM_INFO)))) return -1;
6583 /* insert item in listview control data structure */
6584 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6585 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6587 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6588 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6590 if (lpLVItem->iItem < 0 && !is_sorted) return -1;
6592 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6593 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6594 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6595 if (nItem == -1) goto fail;
6596 infoPtr->nItemCount++;
6598 /* shift indices first so they don't get tangled */
6599 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6601 /* set the item attributes */
6602 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6604 /* full size structure expected - _WIN32IE >= 0x560 */
6607 else if (lpLVItem->mask & LVIF_INDENT)
6609 /* indent member expected - _WIN32IE >= 0x300 */
6610 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6614 /* minimal structure expected */
6615 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6618 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6620 item.mask |= LVIF_STATE;
6621 item.stateMask |= LVIS_STATEIMAGEMASK;
6622 item.state &= ~LVIS_STATEIMAGEMASK;
6623 item.state |= INDEXTOSTATEIMAGEMASK(1);
6625 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6627 /* if we're sorted, sort the list, and update the index */
6630 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6631 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6632 assert(nItem != -1);
6635 /* make room for the position, if we are in the right mode */
6636 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6638 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6640 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6642 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6647 /* send LVN_INSERTITEM notification */
6648 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6650 nmlv.lParam = lpItem->lParam;
6651 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6652 if (!IsWindow(hwndSelf))
6655 /* align items (set position of each item) */
6656 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6660 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6661 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6663 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6665 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6668 /* now is the invalidation fun */
6669 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6673 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6674 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6675 infoPtr->nItemCount--;
6677 DPA_DeletePtr(hdpaSubItems, 0);
6678 DPA_Destroy (hdpaSubItems);
6685 * Redraws a range of items.
6688 * [I] infoPtr : valid pointer to the listview structure
6689 * [I] nFirst : first item
6690 * [I] nLast : last item
6696 static BOOL LISTVIEW_RedrawItems(const LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6700 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6701 max(nFirst, nLast) >= infoPtr->nItemCount)
6704 for (i = nFirst; i <= nLast; i++)
6705 LISTVIEW_InvalidateItem(infoPtr, i);
6712 * Scroll the content of a listview.
6715 * [I] infoPtr : valid pointer to the listview structure
6716 * [I] dx : horizontal scroll amount in pixels
6717 * [I] dy : vertical scroll amount in pixels
6724 * If the control is in report mode (LVS_REPORT) the control can
6725 * be scrolled only in line increments. "dy" will be rounded to the
6726 * nearest number of pixels that are a whole line. Ex: if line height
6727 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6728 * is passed, then the scroll will be 0. (per MSDN 7/2002)
6730 * For: (per experimentation with native control and CSpy ListView)
6731 * LVS_ICON dy=1 = 1 pixel (vertical only)
6733 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6735 * LVS_LIST dx=1 = 1 column (horizontal only)
6736 * but will only scroll 1 column per message
6737 * no matter what the value.
6738 * dy must be 0 or FALSE returned.
6739 * LVS_REPORT dx=1 = 1 pixel
6743 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6745 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6747 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6748 dy /= infoPtr->nItemHeight;
6751 if (dy != 0) return FALSE;
6758 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6759 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6766 * Sets the background color.
6769 * [I] infoPtr : valid pointer to the listview structure
6770 * [I] clrBk : background color
6776 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6778 TRACE("(clrBk=%x)\n", clrBk);
6780 if(infoPtr->clrBk != clrBk) {
6781 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6782 infoPtr->clrBk = clrBk;
6783 if (clrBk == CLR_NONE)
6784 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6786 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6787 LISTVIEW_InvalidateList(infoPtr);
6793 /* LISTVIEW_SetBkImage */
6795 /*** Helper for {Insert,Set}ColumnT *only* */
6796 static void column_fill_hditem(const LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn,
6797 const LVCOLUMNW *lpColumn, BOOL isW)
6799 if (lpColumn->mask & LVCF_FMT)
6801 /* format member is valid */
6802 lphdi->mask |= HDI_FORMAT;
6804 /* set text alignment (leftmost column must be left-aligned) */
6805 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6806 lphdi->fmt |= HDF_LEFT;
6807 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6808 lphdi->fmt |= HDF_RIGHT;
6809 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6810 lphdi->fmt |= HDF_CENTER;
6812 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6813 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6815 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6817 lphdi->fmt |= HDF_IMAGE;
6818 lphdi->iImage = I_IMAGECALLBACK;
6822 if (lpColumn->mask & LVCF_WIDTH)
6824 lphdi->mask |= HDI_WIDTH;
6825 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6827 /* make it fill the remainder of the controls width */
6831 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6833 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6834 lphdi->cxy += rcHeader.right - rcHeader.left;
6837 /* retrieve the layout of the header */
6838 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6839 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
6841 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6844 lphdi->cxy = lpColumn->cx;
6847 if (lpColumn->mask & LVCF_TEXT)
6849 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6850 lphdi->fmt |= HDF_STRING;
6851 lphdi->pszText = lpColumn->pszText;
6852 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6855 if (lpColumn->mask & LVCF_IMAGE)
6857 lphdi->mask |= HDI_IMAGE;
6858 lphdi->iImage = lpColumn->iImage;
6861 if (lpColumn->mask & LVCF_ORDER)
6863 lphdi->mask |= HDI_ORDER;
6864 lphdi->iOrder = lpColumn->iOrder;
6871 * Inserts a new column.
6874 * [I] infoPtr : valid pointer to the listview structure
6875 * [I] nColumn : column index
6876 * [I] lpColumn : column information
6877 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6880 * SUCCESS : new column index
6883 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6884 const LVCOLUMNW *lpColumn, BOOL isW)
6886 COLUMN_INFO *lpColumnInfo;
6889 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6891 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6893 if (!lpColumn || nColumn < 0) return -1;
6894 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6896 ZeroMemory(&hdi, sizeof(HDITEMW));
6897 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6900 * A mask not including LVCF_WIDTH turns into a mask of width, width 10
6901 * (can be seen in SPY) otherwise column never gets added.
6903 if (!(lpColumn->mask & LVCF_WIDTH)) {
6904 hdi.mask |= HDI_WIDTH;
6909 * when the iSubItem is available Windows copies it to the header lParam. It seems
6910 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
6912 if (lpColumn->mask & LVCF_SUBITEM)
6914 hdi.mask |= HDI_LPARAM;
6915 hdi.lParam = lpColumn->iSubItem;
6918 /* create header if not present */
6919 LISTVIEW_CreateHeader(infoPtr);
6920 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle) &&
6921 (LVS_REPORT == uView) && (WS_VISIBLE & infoPtr->dwStyle))
6923 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
6926 /* insert item in header control */
6927 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6928 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6929 (WPARAM)nColumn, (LPARAM)&hdi);
6930 if (nNewColumn == -1) return -1;
6931 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6933 /* create our own column info */
6934 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6935 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6937 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6938 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6940 /* now we have to actually adjust the data */
6941 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6943 SUBITEM_INFO *lpSubItem;
6947 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6949 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, nItem);
6950 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6952 lpSubItem = DPA_GetPtr(hdpaSubItems, i);
6953 if (lpSubItem->iSubItem >= nNewColumn)
6954 lpSubItem->iSubItem++;
6959 /* make space for the new column */
6960 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6961 LISTVIEW_UpdateItemSize(infoPtr);
6966 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6969 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6977 * Sets the attributes of a header item.
6980 * [I] infoPtr : valid pointer to the listview structure
6981 * [I] nColumn : column index
6982 * [I] lpColumn : column attributes
6983 * [I] isW: if TRUE, then lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6989 static BOOL LISTVIEW_SetColumnT(const LISTVIEW_INFO *infoPtr, INT nColumn,
6990 const LVCOLUMNW *lpColumn, BOOL isW)
6992 HDITEMW hdi, hdiget;
6995 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6997 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6999 ZeroMemory(&hdi, sizeof(HDITEMW));
7000 if (lpColumn->mask & LVCF_FMT)
7002 hdi.mask |= HDI_FORMAT;
7003 hdiget.mask = HDI_FORMAT;
7004 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
7005 hdi.fmt = hdiget.fmt & HDF_STRING;
7007 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
7009 /* set header item attributes */
7010 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
7011 if (!bResult) return FALSE;
7013 if (lpColumn->mask & LVCF_FMT)
7015 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
7016 int oldFmt = lpColumnInfo->fmt;
7018 lpColumnInfo->fmt = lpColumn->fmt;
7019 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
7021 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7022 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
7031 * Sets the column order array
7034 * [I] infoPtr : valid pointer to the listview structure
7035 * [I] iCount : number of elements in column order array
7036 * [I] lpiArray : pointer to column order array
7042 static BOOL LISTVIEW_SetColumnOrderArray(const LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
7044 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
7055 * Sets the width of a column
7058 * [I] infoPtr : valid pointer to the listview structure
7059 * [I] nColumn : column index
7060 * [I] cx : column width
7066 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
7068 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7069 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
7073 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
7075 /* set column width only if in report or list mode */
7076 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
7078 /* take care of invalid cx values */
7079 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
7080 else if (uView == LVS_LIST && cx < 1) return FALSE;
7082 /* resize all columns if in LVS_LIST mode */
7083 if(uView == LVS_LIST)
7085 infoPtr->nItemWidth = cx;
7086 LISTVIEW_InvalidateList(infoPtr);
7090 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
7092 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
7097 lvItem.mask = LVIF_TEXT;
7099 lvItem.iSubItem = nColumn;
7100 lvItem.pszText = szDispText;
7101 lvItem.cchTextMax = DISP_TEXT_SIZE;
7102 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7104 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
7105 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
7106 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
7108 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
7109 max_cx += infoPtr->iconSize.cx;
7110 max_cx += TRAILING_LABEL_PADDING;
7113 /* autosize based on listview items width */
7114 if(cx == LVSCW_AUTOSIZE)
7116 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
7118 /* if iCol is the last column make it fill the remainder of the controls width */
7119 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
7124 LISTVIEW_GetOrigin(infoPtr, &Origin);
7125 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
7127 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
7131 /* Despite what the MS docs say, if this is not the last
7132 column, then MS resizes the column to the width of the
7133 largest text string in the column, including headers
7134 and items. This is different from LVSCW_AUTOSIZE in that
7135 LVSCW_AUTOSIZE ignores the header string length. */
7138 /* retrieve header text */
7139 hdi.mask = HDI_TEXT;
7140 hdi.cchTextMax = DISP_TEXT_SIZE;
7141 hdi.pszText = szDispText;
7142 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
7144 HDC hdc = GetDC(infoPtr->hwndSelf);
7145 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
7148 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
7149 cx = size.cx + TRAILING_HEADER_PADDING;
7150 /* FIXME: Take into account the header image, if one is present */
7151 SelectObject(hdc, old_font);
7152 ReleaseDC(infoPtr->hwndSelf, hdc);
7154 cx = max (cx, max_cx);
7158 if (cx < 0) return FALSE;
7160 /* call header to update the column change */
7161 hdi.mask = HDI_WIDTH;
7163 TRACE("hdi.cxy=%d\n", hdi.cxy);
7164 return Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
7168 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
7171 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(const LISTVIEW_INFO *infoPtr)
7174 HBITMAP hbm_im, hbm_mask, hbm_orig;
7176 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
7177 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
7180 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
7181 ILC_COLOR | ILC_MASK, 2, 2);
7182 hdc_wnd = GetDC(infoPtr->hwndSelf);
7183 hdc = CreateCompatibleDC(hdc_wnd);
7184 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
7185 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
7186 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
7188 rc.left = rc.top = 0;
7189 rc.right = GetSystemMetrics(SM_CXSMICON);
7190 rc.bottom = GetSystemMetrics(SM_CYSMICON);
7192 hbm_orig = SelectObject(hdc, hbm_mask);
7193 FillRect(hdc, &rc, hbr_white);
7194 InflateRect(&rc, -3, -3);
7195 FillRect(hdc, &rc, hbr_black);
7197 SelectObject(hdc, hbm_im);
7198 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
7199 SelectObject(hdc, hbm_orig);
7200 ImageList_Add(himl, hbm_im, hbm_mask);
7202 SelectObject(hdc, hbm_im);
7203 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
7204 SelectObject(hdc, hbm_orig);
7205 ImageList_Add(himl, hbm_im, hbm_mask);
7207 DeleteObject(hbm_mask);
7208 DeleteObject(hbm_im);
7216 * Sets the extended listview style.
7219 * [I] infoPtr : valid pointer to the listview structure
7221 * [I] dwStyle : style
7224 * SUCCESS : previous style
7227 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwExStyle)
7229 DWORD dwOldExStyle = infoPtr->dwLvExStyle;
7233 infoPtr->dwLvExStyle = (dwOldExStyle & ~dwMask) | (dwExStyle & dwMask);
7235 infoPtr->dwLvExStyle = dwExStyle;
7237 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_CHECKBOXES)
7239 HIMAGELIST himl = 0;
7240 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
7243 item.mask = LVIF_STATE;
7244 item.stateMask = LVIS_STATEIMAGEMASK;
7245 item.state = INDEXTOSTATEIMAGEMASK(1);
7246 LISTVIEW_SetItemState(infoPtr, -1, &item);
7248 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
7250 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
7253 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_HEADERDRAGDROP)
7255 DWORD dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
7256 if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
7257 dwStyle |= HDS_DRAGDROP;
7259 dwStyle &= ~HDS_DRAGDROP;
7260 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
7263 /* GRIDLINES adds decoration at top so changes sizes */
7264 if((infoPtr->dwLvExStyle ^ dwOldExStyle) & LVS_EX_GRIDLINES)
7266 LISTVIEW_UpdateSize(infoPtr);
7270 LISTVIEW_InvalidateList(infoPtr);
7271 return dwOldExStyle;
7276 * Sets the new hot cursor used during hot tracking and hover selection.
7279 * [I] infoPtr : valid pointer to the listview structure
7280 * [I] hCursor : the new hot cursor handle
7283 * Returns the previous hot cursor
7285 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
7287 HCURSOR oldCursor = infoPtr->hHotCursor;
7289 infoPtr->hHotCursor = hCursor;
7297 * Sets the hot item index.
7300 * [I] infoPtr : valid pointer to the listview structure
7301 * [I] iIndex : index
7304 * SUCCESS : previous hot item index
7305 * FAILURE : -1 (no hot item)
7307 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
7309 INT iOldIndex = infoPtr->nHotItem;
7311 infoPtr->nHotItem = iIndex;
7319 * Sets the amount of time the cursor must hover over an item before it is selected.
7322 * [I] infoPtr : valid pointer to the listview structure
7323 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
7326 * Returns the previous hover time
7328 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
7330 DWORD oldHoverTime = infoPtr->dwHoverTime;
7332 infoPtr->dwHoverTime = dwHoverTime;
7334 return oldHoverTime;
7339 * Sets spacing for icons of LVS_ICON style.
7342 * [I] infoPtr : valid pointer to the listview structure
7343 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
7344 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
7347 * MAKELONG(oldcx, oldcy)
7349 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
7351 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
7352 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7354 TRACE("requested=(%d,%d)\n", cx, cy);
7356 /* this is supported only for LVS_ICON style */
7357 if (uView != LVS_ICON) return oldspacing;
7359 /* set to defaults, if instructed to */
7360 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
7361 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
7363 /* if 0 then compute width
7364 * FIXME: Should scan each item and determine max width of
7365 * icon or label, then make that the width */
7367 cx = infoPtr->iconSpacing.cx;
7369 /* if 0 then compute height */
7371 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
7372 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
7375 infoPtr->iconSpacing.cx = cx;
7376 infoPtr->iconSpacing.cy = cy;
7378 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%d,%d), ntmH=%d\n",
7379 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
7380 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7381 infoPtr->ntmHeight);
7383 /* these depend on the iconSpacing */
7384 LISTVIEW_UpdateItemSize(infoPtr);
7389 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
7393 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
7400 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
7401 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7410 * [I] infoPtr : valid pointer to the listview structure
7411 * [I] nType : image list type
7412 * [I] himl : image list handle
7415 * SUCCESS : old image list
7418 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7420 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7421 INT oldHeight = infoPtr->nItemHeight;
7422 HIMAGELIST himlOld = 0;
7424 TRACE("(nType=%d, himl=%p\n", nType, himl);
7429 himlOld = infoPtr->himlNormal;
7430 infoPtr->himlNormal = himl;
7431 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7432 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7436 himlOld = infoPtr->himlSmall;
7437 infoPtr->himlSmall = himl;
7438 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7442 himlOld = infoPtr->himlState;
7443 infoPtr->himlState = himl;
7444 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7445 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7449 ERR("Unknown icon type=%d\n", nType);
7453 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7454 if (infoPtr->nItemHeight != oldHeight)
7455 LISTVIEW_UpdateScroll(infoPtr);
7462 * Preallocates memory (does *not* set the actual count of items !)
7465 * [I] infoPtr : valid pointer to the listview structure
7466 * [I] nItems : item count (projected number of items to allocate)
7467 * [I] dwFlags : update flags
7473 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7475 TRACE("(nItems=%d, dwFlags=%x)\n", nItems, dwFlags);
7477 if (infoPtr->dwStyle & LVS_OWNERDATA)
7479 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7480 INT nOldCount = infoPtr->nItemCount;
7482 if (nItems < nOldCount)
7484 RANGE range = { nItems, nOldCount };
7485 ranges_del(infoPtr->selectionRanges, range);
7486 if (infoPtr->nFocusedItem >= nItems)
7488 infoPtr->nFocusedItem = -1;
7489 SetRectEmpty(&infoPtr->rcFocus);
7493 infoPtr->nItemCount = nItems;
7494 LISTVIEW_UpdateScroll(infoPtr);
7496 /* the flags are valid only in ownerdata report and list modes */
7497 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7499 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7500 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7502 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7503 LISTVIEW_InvalidateList(infoPtr);
7510 LISTVIEW_GetOrigin(infoPtr, &Origin);
7511 nFrom = min(nOldCount, nItems);
7512 nTo = max(nOldCount, nItems);
7514 if (uView == LVS_REPORT)
7517 rcErase.top = nFrom * infoPtr->nItemHeight;
7518 rcErase.right = infoPtr->nItemWidth;
7519 rcErase.bottom = nTo * infoPtr->nItemHeight;
7520 OffsetRect(&rcErase, Origin.x, Origin.y);
7521 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7522 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7526 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7528 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7529 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7530 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7531 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7532 OffsetRect(&rcErase, Origin.x, Origin.y);
7533 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7534 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7536 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7538 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7539 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7540 OffsetRect(&rcErase, Origin.x, Origin.y);
7541 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7542 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7548 /* According to MSDN for non-LVS_OWNERDATA this is just
7549 * a performance issue. The control allocates its internal
7550 * data structures for the number of items specified. It
7551 * cuts down on the number of memory allocations. Therefore
7552 * we will just issue a WARN here
7554 WARN("for non-ownerdata performance option not implemented.\n");
7562 * Sets the position of an item.
7565 * [I] infoPtr : valid pointer to the listview structure
7566 * [I] nItem : item index
7567 * [I] pt : coordinate
7573 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7575 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7578 TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7580 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7581 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7583 LISTVIEW_GetOrigin(infoPtr, &Origin);
7585 /* This point value seems to be an undocumented feature.
7586 * The best guess is that it means either at the origin,
7587 * or at true beginning of the list. I will assume the origin. */
7588 if ((pt.x == -1) && (pt.y == -1))
7591 if (uView == LVS_ICON)
7593 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7594 pt.y -= ICON_TOP_PADDING;
7599 infoPtr->bAutoarrange = FALSE;
7601 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7606 * Sets the state of one or many items.
7609 * [I] infoPtr : valid pointer to the listview structure
7610 * [I] nItem : item index
7611 * [I] lpLVItem : item or subitem info
7617 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7619 BOOL bResult = TRUE;
7622 lvItem.iItem = nItem;
7623 lvItem.iSubItem = 0;
7624 lvItem.mask = LVIF_STATE;
7625 lvItem.state = lpLVItem->state;
7626 lvItem.stateMask = lpLVItem->stateMask;
7627 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7631 /* apply to all items */
7632 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7633 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7636 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7639 * Update selection mark
7641 * Investigation on windows 2k showed that selection mark was updated
7642 * whenever a new selection was made, but if the selected item was
7643 * unselected it was not updated.
7645 * we are probably still not 100% accurate, but this at least sets the
7646 * proper selection mark when it is needed
7649 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7650 (infoPtr->nSelectionMark == -1))
7653 for (i = 0; i < infoPtr->nItemCount; i++)
7655 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7657 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7659 infoPtr->nSelectionMark = i;
7663 else if (ranges_contain(infoPtr->selectionRanges, i))
7665 infoPtr->nSelectionMark = i;
7676 * Sets the text of an item or subitem.
7679 * [I] hwnd : window handle
7680 * [I] nItem : item index
7681 * [I] lpLVItem : item or subitem info
7682 * [I] isW : TRUE if input is Unicode
7688 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7692 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7694 lvItem.iItem = nItem;
7695 lvItem.iSubItem = lpLVItem->iSubItem;
7696 lvItem.mask = LVIF_TEXT;
7697 lvItem.pszText = lpLVItem->pszText;
7698 lvItem.cchTextMax = lpLVItem->cchTextMax;
7700 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7702 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7707 * Set item index that marks the start of a multiple selection.
7710 * [I] infoPtr : valid pointer to the listview structure
7711 * [I] nIndex : index
7714 * Index number or -1 if there is no selection mark.
7716 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7718 INT nOldIndex = infoPtr->nSelectionMark;
7720 TRACE("(nIndex=%d)\n", nIndex);
7722 infoPtr->nSelectionMark = nIndex;
7729 * Sets the text background color.
7732 * [I] infoPtr : valid pointer to the listview structure
7733 * [I] clrTextBk : text background color
7739 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7741 TRACE("(clrTextBk=%x)\n", clrTextBk);
7743 if (infoPtr->clrTextBk != clrTextBk)
7745 infoPtr->clrTextBk = clrTextBk;
7746 LISTVIEW_InvalidateList(infoPtr);
7754 * Sets the text foreground color.
7757 * [I] infoPtr : valid pointer to the listview structure
7758 * [I] clrText : text color
7764 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7766 TRACE("(clrText=%x)\n", clrText);
7768 if (infoPtr->clrText != clrText)
7770 infoPtr->clrText = clrText;
7771 LISTVIEW_InvalidateList(infoPtr);
7779 * Determines which listview item is located at the specified position.
7782 * [I] infoPtr : valid pointer to the listview structure
7783 * [I] hwndNewToolTip : handle to new ToolTip
7788 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7790 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7791 infoPtr->hwndToolTip = hwndNewToolTip;
7792 return hwndOldToolTip;
7797 * sets the Unicode character format flag for the control
7799 * [I] infoPtr :valid pointer to the listview structure
7800 * [I] fUnicode :true to switch to UNICODE false to switch to ANSI
7803 * Old Unicode Format
7805 static BOOL LISTVIEW_SetUnicodeFormat( LISTVIEW_INFO *infoPtr, BOOL fUnicode)
7807 BOOL rc = infoPtr->notifyFormat;
7808 infoPtr->notifyFormat = (fUnicode)?NFR_UNICODE:NFR_ANSI;
7812 /* LISTVIEW_SetWorkAreas */
7816 * Callback internally used by LISTVIEW_SortItems()
7819 * [I] first : pointer to first ITEM_INFO to compare
7820 * [I] second : pointer to second ITEM_INFO to compare
7821 * [I] lParam : HWND of control
7824 * if first comes before second : negative
7825 * if first comes after second : positive
7826 * if first and second are equivalent : zero
7828 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7830 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7831 ITEM_INFO* lv_first = DPA_GetPtr( first, 0 );
7832 ITEM_INFO* lv_second = DPA_GetPtr( second, 0 );
7834 /* Forward the call to the client defined callback */
7835 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7840 * Sorts the listview items.
7843 * [I] infoPtr : valid pointer to the listview structure
7844 * [I] pfnCompare : application-defined value
7845 * [I] lParamSort : pointer to comparison callback
7851 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7853 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7856 LPVOID selectionMarkItem;
7860 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7862 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7864 if (!pfnCompare) return FALSE;
7865 if (!infoPtr->hdpaItems) return FALSE;
7867 /* if there are 0 or 1 items, there is no need to sort */
7868 if (infoPtr->nItemCount < 2) return TRUE;
7870 if (infoPtr->nFocusedItem >= 0)
7872 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7873 lpItem = DPA_GetPtr(hdpaSubItems, 0);
7874 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7876 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7877 /* clear the lpItem->state for non-selected ones */
7878 /* remove the selection ranges */
7880 infoPtr->pfnCompare = pfnCompare;
7881 infoPtr->lParamSort = lParamSort;
7882 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7884 /* Adjust selections and indices so that they are the way they should
7885 * be after the sort (otherwise, the list items move around, but
7886 * whatever is at the item's previous original position will be
7889 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7890 for (i=0; i < infoPtr->nItemCount; i++)
7892 hdpaSubItems = DPA_GetPtr(infoPtr->hdpaItems, i);
7893 lpItem = DPA_GetPtr(hdpaSubItems, 0);
7895 if (lpItem->state & LVIS_SELECTED)
7897 item.state = LVIS_SELECTED;
7898 item.stateMask = LVIS_SELECTED;
7899 LISTVIEW_SetItemState(infoPtr, i, &item);
7901 if (lpItem->state & LVIS_FOCUSED)
7903 infoPtr->nFocusedItem = i;
7904 lpItem->state &= ~LVIS_FOCUSED;
7907 if (selectionMarkItem != NULL)
7908 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7909 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7911 /* refresh the display */
7912 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7913 LISTVIEW_InvalidateList(infoPtr);
7920 * Update theme handle after a theme change.
7923 * [I] infoPtr : valid pointer to the listview structure
7927 * FAILURE : something else
7929 static LRESULT LISTVIEW_ThemeChanged(const LISTVIEW_INFO *infoPtr)
7931 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7932 CloseThemeData(theme);
7933 OpenThemeData(infoPtr->hwndSelf, themeClass);
7939 * Updates an items or rearranges the listview control.
7942 * [I] infoPtr : valid pointer to the listview structure
7943 * [I] nItem : item index
7949 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7951 TRACE("(nItem=%d)\n", nItem);
7953 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7955 /* rearrange with default alignment style */
7956 if (is_autoarrange(infoPtr))
7957 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7959 LISTVIEW_InvalidateItem(infoPtr, nItem);
7966 * Draw the track line at the place defined in the infoPtr structure.
7967 * The line is drawn with a XOR pen so drawing the line for the second time
7968 * in the same place erases the line.
7971 * [I] infoPtr : valid pointer to the listview structure
7977 static BOOL LISTVIEW_DrawTrackLine(const LISTVIEW_INFO *infoPtr)
7983 if (infoPtr->xTrackLine == -1)
7986 if (!(hdc = GetDC(infoPtr->hwndSelf)))
7988 hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
7989 oldROP = SetROP2(hdc, R2_XORPEN);
7990 MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
7991 LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
7992 SetROP2(hdc, oldROP);
7993 SelectObject(hdc, hOldPen);
7994 ReleaseDC(infoPtr->hwndSelf, hdc);
8000 * Called when an edit control should be displayed. This function is called after
8001 * we are sure that there was a single click - not a double click (this is a TIMERPROC).
8004 * [I] hwnd : Handle to the listview
8005 * [I] uMsg : WM_TIMER (ignored)
8006 * [I] idEvent : The timer ID interpreted as a pointer to a DELAYED_EDIT_ITEM struct
8007 * [I] dwTimer : The elapsed time (ignored)
8012 static VOID CALLBACK LISTVIEW_DelayedEditItem(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
8014 DELAYED_ITEM_EDIT *editItem = (DELAYED_ITEM_EDIT *)idEvent;
8015 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8017 KillTimer(hwnd, idEvent);
8018 editItem->fEnabled = FALSE;
8019 /* check if the item is still selected */
8020 if (infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, editItem->iItem, LVIS_SELECTED))
8021 LISTVIEW_EditLabelT(infoPtr, editItem->iItem, TRUE);
8026 * Creates the listview control - the WM_NCCREATE phase.
8029 * [I] hwnd : window handle
8030 * [I] lpcs : the create parameters
8036 static LRESULT LISTVIEW_NCCreate(HWND hwnd, const CREATESTRUCTW *lpcs)
8038 LISTVIEW_INFO *infoPtr;
8041 TRACE("(lpcs=%p)\n", lpcs);
8043 /* initialize info pointer */
8044 infoPtr = Alloc(sizeof(LISTVIEW_INFO));
8045 if (!infoPtr) return FALSE;
8047 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
8049 infoPtr->hwndSelf = hwnd;
8050 infoPtr->dwStyle = lpcs->style; /* Note: may be changed in WM_CREATE */
8051 /* determine the type of structures to use */
8052 infoPtr->hwndNotify = lpcs->hwndParent;
8053 /* infoPtr->notifyFormat will be filled in WM_CREATE */
8055 /* initialize color information */
8056 infoPtr->clrBk = CLR_NONE;
8057 infoPtr->clrText = CLR_DEFAULT;
8058 infoPtr->clrTextBk = CLR_DEFAULT;
8059 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
8061 /* set default values */
8062 infoPtr->nFocusedItem = -1;
8063 infoPtr->nSelectionMark = -1;
8064 infoPtr->nHotItem = -1;
8065 infoPtr->bRedraw = TRUE;
8066 infoPtr->bNoItemMetrics = TRUE;
8067 infoPtr->bDoChangeNotify = TRUE;
8068 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
8069 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
8070 infoPtr->nEditLabelItem = -1;
8071 infoPtr->dwHoverTime = -1; /* default system hover time */
8072 infoPtr->nMeasureItemHeight = 0;
8073 infoPtr->xTrackLine = -1; /* no track line */
8074 infoPtr->itemEdit.fEnabled = FALSE;
8076 /* get default font (icon title) */
8077 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
8078 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
8079 infoPtr->hFont = infoPtr->hDefaultFont;
8080 LISTVIEW_SaveTextMetrics(infoPtr);
8082 /* allocate memory for the data structure */
8083 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
8084 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
8085 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
8086 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
8087 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
8091 DestroyWindow(infoPtr->hwndHeader);
8092 ranges_destroy(infoPtr->selectionRanges);
8093 DPA_Destroy(infoPtr->hdpaItems);
8094 DPA_Destroy(infoPtr->hdpaPosX);
8095 DPA_Destroy(infoPtr->hdpaPosY);
8096 DPA_Destroy(infoPtr->hdpaColumns);
8103 * Creates the listview control - the WM_CREATE phase. Most of the data is
8104 * already set up in LISTVIEW_NCCreate
8107 * [I] hwnd : window handle
8108 * [I] lpcs : the create parameters
8114 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
8116 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
8117 UINT uView = lpcs->style & LVS_TYPEMASK;
8119 TRACE("(lpcs=%p)\n", lpcs);
8121 infoPtr->dwStyle = lpcs->style;
8122 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
8123 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
8125 if ((uView == LVS_REPORT) && (lpcs->style & WS_VISIBLE))
8127 if (LISTVIEW_CreateHeader(infoPtr) < 0) return -1;
8130 infoPtr->hwndHeader = 0;
8132 /* init item size to avoid division by 0 */
8133 LISTVIEW_UpdateItemSize (infoPtr);
8135 if (uView == LVS_REPORT)
8137 if (!(LVS_NOCOLUMNHEADER & lpcs->style) && (WS_VISIBLE & lpcs->style))
8139 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
8141 LISTVIEW_UpdateSize(infoPtr);
8142 LISTVIEW_UpdateScroll(infoPtr);
8145 OpenThemeData(hwnd, themeClass);
8147 /* initialize the icon sizes */
8148 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
8149 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
8155 * Destroys the listview control.
8158 * [I] infoPtr : valid pointer to the listview structure
8164 static LRESULT LISTVIEW_Destroy(const LISTVIEW_INFO *infoPtr)
8166 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
8167 CloseThemeData(theme);
8173 * Enables the listview control.
8176 * [I] infoPtr : valid pointer to the listview structure
8177 * [I] bEnable : specifies whether to enable or disable the window
8183 static BOOL LISTVIEW_Enable(const LISTVIEW_INFO *infoPtr, BOOL bEnable)
8185 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
8186 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
8192 * Erases the background of the listview control.
8195 * [I] infoPtr : valid pointer to the listview structure
8196 * [I] hdc : device context handle
8202 static inline BOOL LISTVIEW_EraseBkgnd(const LISTVIEW_INFO *infoPtr, HDC hdc)
8206 TRACE("(hdc=%p)\n", hdc);
8208 if (!GetClipBox(hdc, &rc)) return FALSE;
8210 /* for double buffered controls we need to do this during refresh */
8211 if (infoPtr->dwLvExStyle & LVS_EX_DOUBLEBUFFER) return FALSE;
8213 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
8219 * Helper function for LISTVIEW_[HV]Scroll *only*.
8220 * Performs vertical/horizontal scrolling by a give amount.
8223 * [I] infoPtr : valid pointer to the listview structure
8224 * [I] dx : amount of horizontal scroll
8225 * [I] dy : amount of vertical scroll
8227 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
8229 /* now we can scroll the list */
8230 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
8231 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
8232 /* if we have focus, adjust rect */
8233 OffsetRect(&infoPtr->rcFocus, dx, dy);
8234 UpdateWindow(infoPtr->hwndSelf);
8239 * Performs vertical scrolling.
8242 * [I] infoPtr : valid pointer to the listview structure
8243 * [I] nScrollCode : scroll code
8244 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8245 * [I] hScrollWnd : scrollbar control window handle
8251 * SB_LINEUP/SB_LINEDOWN:
8252 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
8253 * for LVS_REPORT is 1 line
8254 * for LVS_LIST cannot occur
8257 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8258 INT nScrollDiff, HWND hScrollWnd)
8260 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8261 INT nOldScrollPos, nNewScrollPos;
8262 SCROLLINFO scrollInfo;
8265 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8266 debugscrollcode(nScrollCode), nScrollDiff);
8268 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8270 scrollInfo.cbSize = sizeof(SCROLLINFO);
8271 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8273 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
8275 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
8277 nOldScrollPos = scrollInfo.nPos;
8278 switch (nScrollCode)
8284 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
8288 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
8292 nScrollDiff = -scrollInfo.nPage;
8296 nScrollDiff = scrollInfo.nPage;
8299 case SB_THUMBPOSITION:
8301 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8308 /* quit right away if pos isn't changing */
8309 if (nScrollDiff == 0) return 0;
8311 /* calculate new position, and handle overflows */
8312 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8313 if (nScrollDiff > 0) {
8314 if (nNewScrollPos < nOldScrollPos ||
8315 nNewScrollPos > scrollInfo.nMax)
8316 nNewScrollPos = scrollInfo.nMax;
8318 if (nNewScrollPos > nOldScrollPos ||
8319 nNewScrollPos < scrollInfo.nMin)
8320 nNewScrollPos = scrollInfo.nMin;
8323 /* set the new position, and reread in case it changed */
8324 scrollInfo.fMask = SIF_POS;
8325 scrollInfo.nPos = nNewScrollPos;
8326 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
8328 /* carry on only if it really changed */
8329 if (nNewScrollPos == nOldScrollPos) return 0;
8331 /* now adjust to client coordinates */
8332 nScrollDiff = nOldScrollPos - nNewScrollPos;
8333 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
8335 /* and scroll the window */
8336 scroll_list(infoPtr, 0, nScrollDiff);
8343 * Performs horizontal scrolling.
8346 * [I] infoPtr : valid pointer to the listview structure
8347 * [I] nScrollCode : scroll code
8348 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
8349 * [I] hScrollWnd : scrollbar control window handle
8355 * SB_LINELEFT/SB_LINERIGHT:
8356 * for LVS_ICON, LVS_SMALLICON 1 pixel
8357 * for LVS_REPORT is 1 pixel
8358 * for LVS_LIST is 1 column --> which is a 1 because the
8359 * scroll is based on columns not pixels
8362 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
8363 INT nScrollDiff, HWND hScrollWnd)
8365 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8366 INT nOldScrollPos, nNewScrollPos;
8367 SCROLLINFO scrollInfo;
8369 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
8370 debugscrollcode(nScrollCode), nScrollDiff);
8372 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
8374 scrollInfo.cbSize = sizeof(SCROLLINFO);
8375 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
8377 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
8379 nOldScrollPos = scrollInfo.nPos;
8381 switch (nScrollCode)
8395 nScrollDiff = -scrollInfo.nPage;
8399 nScrollDiff = scrollInfo.nPage;
8402 case SB_THUMBPOSITION:
8404 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
8411 /* quit right away if pos isn't changing */
8412 if (nScrollDiff == 0) return 0;
8414 /* calculate new position, and handle overflows */
8415 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
8416 if (nScrollDiff > 0) {
8417 if (nNewScrollPos < nOldScrollPos ||
8418 nNewScrollPos > scrollInfo.nMax)
8419 nNewScrollPos = scrollInfo.nMax;
8421 if (nNewScrollPos > nOldScrollPos ||
8422 nNewScrollPos < scrollInfo.nMin)
8423 nNewScrollPos = scrollInfo.nMin;
8426 /* set the new position, and reread in case it changed */
8427 scrollInfo.fMask = SIF_POS;
8428 scrollInfo.nPos = nNewScrollPos;
8429 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
8431 /* carry on only if it really changed */
8432 if (nNewScrollPos == nOldScrollPos) return 0;
8434 if(uView == LVS_REPORT)
8435 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
8437 /* now adjust to client coordinates */
8438 nScrollDiff = nOldScrollPos - nNewScrollPos;
8439 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
8441 /* and scroll the window */
8442 scroll_list(infoPtr, nScrollDiff, 0);
8447 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
8449 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8450 INT gcWheelDelta = 0;
8451 INT pulScrollLines = 3;
8452 SCROLLINFO scrollInfo;
8454 TRACE("(wheelDelta=%d)\n", wheelDelta);
8456 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8457 gcWheelDelta -= wheelDelta;
8459 scrollInfo.cbSize = sizeof(SCROLLINFO);
8460 scrollInfo.fMask = SIF_POS;
8467 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8468 * should be fixed in the future.
8470 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
8471 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8475 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8477 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
8478 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8479 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
8484 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8495 * [I] infoPtr : valid pointer to the listview structure
8496 * [I] nVirtualKey : virtual key
8497 * [I] lKeyData : key data
8502 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8504 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8505 HWND hwndSelf = infoPtr->hwndSelf;
8507 NMLVKEYDOWN nmKeyDown;
8509 TRACE("(nVirtualKey=%d, lKeyData=%d)\n", nVirtualKey, lKeyData);
8511 /* send LVN_KEYDOWN notification */
8512 nmKeyDown.wVKey = nVirtualKey;
8513 nmKeyDown.flags = 0;
8514 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8515 if (!IsWindow(hwndSelf))
8518 switch (nVirtualKey)
8521 nItem = infoPtr->nFocusedItem;
8522 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
8523 toggle_checkbox_state(infoPtr, infoPtr->nFocusedItem);
8527 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8529 if (!notify(infoPtr, NM_RETURN)) return 0;
8530 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8535 if (infoPtr->nItemCount > 0)
8540 if (infoPtr->nItemCount > 0)
8541 nItem = infoPtr->nItemCount - 1;
8545 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8549 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8553 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8557 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8561 if (uView == LVS_REPORT)
8563 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8564 if (infoPtr->nFocusedItem == topidx)
8565 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8570 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8571 * LISTVIEW_GetCountPerRow(infoPtr);
8572 if(nItem < 0) nItem = 0;
8576 if (uView == LVS_REPORT)
8578 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8579 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8580 if (infoPtr->nFocusedItem == topidx + cnt - 1)
8581 nItem = infoPtr->nFocusedItem + cnt - 1;
8583 nItem = topidx + cnt - 1;
8586 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8587 * LISTVIEW_GetCountPerRow(infoPtr);
8588 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8592 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8593 LISTVIEW_KeySelection(infoPtr, nItem, nVirtualKey == VK_SPACE);
8603 * [I] infoPtr : valid pointer to the listview structure
8608 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8612 /* if we did not have the focus, there's nothing to do */
8613 if (!infoPtr->bFocus) return 0;
8615 /* send NM_KILLFOCUS notification */
8616 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8618 /* if we have a focus rectagle, get rid of it */
8619 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8621 /* set window focus flag */
8622 infoPtr->bFocus = FALSE;
8624 /* invalidate the selected items before resetting focus flag */
8625 LISTVIEW_InvalidateSelectedItems(infoPtr);
8632 * Processes double click messages (left mouse button).
8635 * [I] infoPtr : valid pointer to the listview structure
8636 * [I] wKey : key flag
8637 * [I] x,y : mouse coordinate
8642 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8644 LVHITTESTINFO htInfo;
8646 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8648 /* Cancel the item edition if any */
8649 if (infoPtr->itemEdit.fEnabled)
8651 KillTimer(infoPtr->hwndSelf, (UINT_PTR)&infoPtr->itemEdit);
8652 infoPtr->itemEdit.fEnabled = FALSE;
8655 /* send NM_RELEASEDCAPTURE notification */
8656 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8661 /* send NM_DBLCLK notification */
8662 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8663 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8665 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8666 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8673 * Processes mouse down messages (left mouse button).
8676 * infoPtr [I ] valid pointer to the listview structure
8677 * wKey [I ] key flag
8678 * x,y [I ] mouse coordinate
8683 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8685 LVHITTESTINFO lvHitTestInfo;
8686 static BOOL bGroupSelect = TRUE;
8687 POINT pt = { x, y };
8690 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8692 /* send NM_RELEASEDCAPTURE notification */
8693 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8695 /* set left button down flag and record the click position */
8696 infoPtr->bLButtonDown = TRUE;
8697 infoPtr->ptClickPos = pt;
8698 infoPtr->bDragging = FALSE;
8700 lvHitTestInfo.pt.x = x;
8701 lvHitTestInfo.pt.y = y;
8703 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8704 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
8705 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8707 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8709 toggle_checkbox_state(infoPtr, nItem);
8713 if (infoPtr->dwStyle & LVS_SINGLESEL)
8715 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8716 infoPtr->nEditLabelItem = nItem;
8718 LISTVIEW_SetSelection(infoPtr, nItem);
8722 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8726 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8727 LISTVIEW_SetItemFocus(infoPtr, nItem);
8728 infoPtr->nSelectionMark = nItem;
8734 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8735 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8737 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8738 infoPtr->nSelectionMark = nItem;
8741 else if (wKey & MK_CONTROL)
8745 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8747 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8748 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8749 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8750 infoPtr->nSelectionMark = nItem;
8752 else if (wKey & MK_SHIFT)
8754 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8758 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8759 infoPtr->nEditLabelItem = nItem;
8761 /* set selection (clears other pre-existing selections) */
8762 LISTVIEW_SetSelection(infoPtr, nItem);
8766 if (infoPtr->dwLvExStyle & LVS_EX_ONECLICKACTIVATE)
8767 if(lvHitTestInfo.iItem != -1) notify_itemactivate(infoPtr,&lvHitTestInfo);
8771 /* remove all selections */
8772 LISTVIEW_DeselectAll(infoPtr);
8781 * Processes mouse up messages (left mouse button).
8784 * infoPtr [I ] valid pointer to the listview structure
8785 * wKey [I ] key flag
8786 * x,y [I ] mouse coordinate
8791 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8793 LVHITTESTINFO lvHitTestInfo;
8795 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8797 if (!infoPtr->bLButtonDown) return 0;
8799 lvHitTestInfo.pt.x = x;
8800 lvHitTestInfo.pt.y = y;
8802 /* send NM_CLICK notification */
8803 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8804 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8806 /* set left button flag */
8807 infoPtr->bLButtonDown = FALSE;
8809 if (infoPtr->bDragging)
8811 infoPtr->bDragging = FALSE;
8815 /* if we clicked on a selected item, edit the label */
8816 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8818 /* we want to make sure the user doesn't want to do a double click. So we will
8819 * delay the edit. WM_LBUTTONDBLCLICK will cancel the timer
8821 infoPtr->itemEdit.fEnabled = TRUE;
8822 infoPtr->itemEdit.iItem = lvHitTestInfo.iItem;
8823 SetTimer(infoPtr->hwndSelf,
8824 (UINT_PTR)&infoPtr->itemEdit,
8825 GetDoubleClickTime(),
8826 LISTVIEW_DelayedEditItem);
8829 if (!infoPtr->bFocus)
8830 SetFocus(infoPtr->hwndSelf);
8837 * Destroys the listview control (called after WM_DESTROY).
8840 * [I] infoPtr : valid pointer to the listview structure
8845 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8849 /* delete all items */
8850 LISTVIEW_DeleteAllItems(infoPtr, TRUE);
8852 /* destroy data structure */
8853 DPA_Destroy(infoPtr->hdpaItems);
8854 DPA_Destroy(infoPtr->hdpaPosX);
8855 DPA_Destroy(infoPtr->hdpaPosY);
8856 DPA_Destroy(infoPtr->hdpaColumns);
8857 ranges_destroy(infoPtr->selectionRanges);
8859 /* destroy image lists */
8860 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8862 if (infoPtr->himlNormal)
8863 ImageList_Destroy(infoPtr->himlNormal);
8864 if (infoPtr->himlSmall)
8865 ImageList_Destroy(infoPtr->himlSmall);
8866 if (infoPtr->himlState)
8867 ImageList_Destroy(infoPtr->himlState);
8870 /* destroy font, bkgnd brush */
8872 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8873 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8875 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8877 /* free listview info pointer*/
8885 * Handles notifications from header.
8888 * [I] infoPtr : valid pointer to the listview structure
8889 * [I] nCtrlId : control identifier
8890 * [I] lpnmh : notification information
8895 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8897 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8898 HWND hwndSelf = infoPtr->hwndSelf;
8900 TRACE("(lpnmh=%p)\n", lpnmh);
8902 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8904 switch (lpnmh->hdr.code)
8909 COLUMN_INFO *lpColumnInfo;
8913 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8916 /* remove the old line (if any) */
8917 LISTVIEW_DrawTrackLine(infoPtr);
8919 /* compute & draw the new line */
8920 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8921 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
8922 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8923 infoPtr->xTrackLine = x + ptOrigin.x;
8924 LISTVIEW_DrawTrackLine(infoPtr);
8930 /* remove the track line (if any) */
8931 LISTVIEW_DrawTrackLine(infoPtr);
8932 infoPtr->xTrackLine = -1;
8936 FIXME("Changing column order not implemented\n");
8939 case HDN_ITEMCHANGINGW:
8940 case HDN_ITEMCHANGINGA:
8941 return notify_forward_header(infoPtr, lpnmh);
8943 case HDN_ITEMCHANGEDW:
8944 case HDN_ITEMCHANGEDA:
8946 COLUMN_INFO *lpColumnInfo;
8949 notify_forward_header(infoPtr, lpnmh);
8950 if (!IsWindow(hwndSelf))
8953 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8957 hdi.mask = HDI_WIDTH;
8958 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, &hdi)) return 0;
8962 cxy = lpnmh->pitem->cxy;
8964 /* determine how much we change since the last know position */
8965 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8966 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8969 lpColumnInfo->rcHeader.right += dx;
8970 if (lpnmh->iItem + 1 < DPA_GetPtrCount(infoPtr->hdpaColumns))
8971 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8974 /* only needs to update the scrolls */
8975 infoPtr->nItemWidth += dx;
8976 LISTVIEW_UpdateScroll(infoPtr);
8978 LISTVIEW_UpdateItemSize(infoPtr);
8979 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8982 RECT rcCol = lpColumnInfo->rcHeader;
8984 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8985 OffsetRect(&rcCol, ptOrigin.x, 0);
8987 rcCol.top = infoPtr->rcList.top;
8988 rcCol.bottom = infoPtr->rcList.bottom;
8990 /* resizing left-aligned columns leaves most of the left side untouched */
8991 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8993 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth;
8996 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
8999 /* when shrinking the last column clear the now unused field */
9000 if (lpnmh->iItem == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1 && dx < 0)
9003 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
9009 case HDN_ITEMCLICKW:
9010 case HDN_ITEMCLICKA:
9012 /* Handle sorting by Header Column */
9015 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
9017 nmlv.iSubItem = lpnmh->iItem;
9018 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
9019 notify_forward_header(infoPtr, lpnmh);
9023 case HDN_DIVIDERDBLCLICKW:
9024 case HDN_DIVIDERDBLCLICKA:
9025 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
9034 * Paint non-client area of control.
9037 * [I] infoPtr : valid pointer to the listview structureof the sender
9038 * [I] region : update region
9041 * TRUE - frame was painted
9042 * FALSE - call default window proc
9044 static BOOL LISTVIEW_NCPaint(const LISTVIEW_INFO *infoPtr, HRGN region)
9046 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
9050 int cxEdge = GetSystemMetrics (SM_CXEDGE),
9051 cyEdge = GetSystemMetrics (SM_CYEDGE);
9053 if (!theme) return FALSE;
9055 GetWindowRect(infoPtr->hwndSelf, &r);
9057 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
9058 r.right - cxEdge, r.bottom - cyEdge);
9059 if (region != (HRGN)1)
9060 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
9061 OffsetRect(&r, -r.left, -r.top);
9063 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
9064 OffsetRect(&r, -r.left, -r.top);
9066 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
9067 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
9068 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
9069 ReleaseDC(infoPtr->hwndSelf, dc);
9071 /* Call default proc to get the scrollbars etc. painted */
9072 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
9079 * Determines the type of structure to use.
9082 * [I] infoPtr : valid pointer to the listview structureof the sender
9083 * [I] hwndFrom : listview window handle
9084 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
9089 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
9091 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
9093 if (nCommand == NF_REQUERY)
9094 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
9096 return infoPtr->notifyFormat;
9101 * Paints/Repaints the listview control.
9104 * [I] infoPtr : valid pointer to the listview structure
9105 * [I] hdc : device context handle
9110 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
9112 TRACE("(hdc=%p)\n", hdc);
9114 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
9116 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9118 infoPtr->bNoItemMetrics = FALSE;
9119 LISTVIEW_UpdateItemSize(infoPtr);
9120 if (uView == LVS_ICON || uView == LVS_SMALLICON)
9121 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9122 LISTVIEW_UpdateScroll(infoPtr);
9125 UpdateWindow(infoPtr->hwndHeader);
9128 LISTVIEW_Refresh(infoPtr, hdc, NULL);
9133 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
9135 LISTVIEW_Refresh(infoPtr, hdc, ps.fErase ? &ps.rcPaint : NULL);
9136 EndPaint(infoPtr->hwndSelf, &ps);
9145 * Paints/Repaints the listview control.
9148 * [I] infoPtr : valid pointer to the listview structure
9149 * [I] hdc : device context handle
9150 * [I] options : drawing options
9155 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
9157 FIXME("Partial Stub: (hdc=%p options=0x%08x)\n", hdc, options);
9159 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
9162 if (options & PRF_ERASEBKGND)
9163 LISTVIEW_EraseBkgnd(infoPtr, hdc);
9165 if (options & PRF_CLIENT)
9166 LISTVIEW_Paint(infoPtr, hdc);
9174 * Processes double click messages (right mouse button).
9177 * [I] infoPtr : valid pointer to the listview structure
9178 * [I] wKey : key flag
9179 * [I] x,y : mouse coordinate
9184 static LRESULT LISTVIEW_RButtonDblClk(const LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9186 LVHITTESTINFO lvHitTestInfo;
9188 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9190 /* send NM_RELEASEDCAPTURE notification */
9191 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9193 /* send NM_RDBLCLK notification */
9194 lvHitTestInfo.pt.x = x;
9195 lvHitTestInfo.pt.y = y;
9196 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9197 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
9204 * Processes mouse down messages (right mouse button).
9207 * [I] infoPtr : valid pointer to the listview structure
9208 * [I] wKey : key flag
9209 * [I] x,y : mouse coordinate
9214 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9216 LVHITTESTINFO lvHitTestInfo;
9219 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9221 /* send NM_RELEASEDCAPTURE notification */
9222 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
9224 /* make sure the listview control window has the focus */
9225 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
9227 /* set right button down flag */
9228 infoPtr->bRButtonDown = TRUE;
9230 /* determine the index of the selected item */
9231 lvHitTestInfo.pt.x = x;
9232 lvHitTestInfo.pt.y = y;
9233 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
9235 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
9237 LISTVIEW_SetItemFocus(infoPtr, nItem);
9238 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
9239 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
9240 LISTVIEW_SetSelection(infoPtr, nItem);
9244 LISTVIEW_DeselectAll(infoPtr);
9252 * Processes mouse up messages (right mouse button).
9255 * [I] infoPtr : valid pointer to the listview structure
9256 * [I] wKey : key flag
9257 * [I] x,y : mouse coordinate
9262 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
9264 LVHITTESTINFO lvHitTestInfo;
9267 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
9269 if (!infoPtr->bRButtonDown) return 0;
9271 /* set button flag */
9272 infoPtr->bRButtonDown = FALSE;
9274 /* Send NM_RClICK notification */
9275 lvHitTestInfo.pt.x = x;
9276 lvHitTestInfo.pt.y = y;
9277 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
9278 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
9280 /* Change to screen coordinate for WM_CONTEXTMENU */
9281 pt = lvHitTestInfo.pt;
9282 ClientToScreen(infoPtr->hwndSelf, &pt);
9284 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
9285 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
9286 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
9297 * [I] infoPtr : valid pointer to the listview structure
9298 * [I] hwnd : window handle of window containing the cursor
9299 * [I] nHittest : hit-test code
9300 * [I] wMouseMsg : ideintifier of the mouse message
9303 * TRUE if cursor is set
9306 static BOOL LISTVIEW_SetCursor(const LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
9308 LVHITTESTINFO lvHitTestInfo;
9310 if(!(LISTVIEW_isHotTracking(infoPtr))) return FALSE;
9312 if(!infoPtr->hHotCursor) return FALSE;
9314 GetCursorPos(&lvHitTestInfo.pt);
9315 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
9317 SetCursor(infoPtr->hHotCursor);
9327 * [I] infoPtr : valid pointer to the listview structure
9328 * [I] hwndLoseFocus : handle of previously focused window
9333 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
9335 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
9337 /* if we have the focus already, there's nothing to do */
9338 if (infoPtr->bFocus) return 0;
9340 /* send NM_SETFOCUS notification */
9341 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
9343 /* set window focus flag */
9344 infoPtr->bFocus = TRUE;
9346 /* put the focus rect back on */
9347 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
9349 /* redraw all visible selected items */
9350 LISTVIEW_InvalidateSelectedItems(infoPtr);
9360 * [I] infoPtr : valid pointer to the listview structure
9361 * [I] fRedraw : font handle
9362 * [I] fRedraw : redraw flag
9367 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
9369 HFONT oldFont = infoPtr->hFont;
9371 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
9373 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
9374 if (infoPtr->hFont == oldFont) return 0;
9376 LISTVIEW_SaveTextMetrics(infoPtr);
9378 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
9380 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
9381 LISTVIEW_UpdateSize(infoPtr);
9382 LISTVIEW_UpdateScroll(infoPtr);
9385 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
9392 * Message handling for WM_SETREDRAW.
9393 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
9396 * [I] infoPtr : valid pointer to the listview structure
9397 * [I] bRedraw: state of redraw flag
9400 * DefWinProc return value
9402 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
9404 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
9406 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
9407 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
9409 infoPtr->bRedraw = bRedraw;
9411 if(!bRedraw) return 0;
9413 if (is_autoarrange(infoPtr))
9414 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9415 LISTVIEW_UpdateScroll(infoPtr);
9417 /* despite what the WM_SETREDRAW docs says, apps expect us
9418 * to invalidate the listview here... stupid! */
9419 LISTVIEW_InvalidateList(infoPtr);
9426 * Resizes the listview control. This function processes WM_SIZE
9427 * messages. At this time, the width and height are not used.
9430 * [I] infoPtr : valid pointer to the listview structure
9431 * [I] Width : new width
9432 * [I] Height : new height
9437 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
9439 RECT rcOld = infoPtr->rcList;
9441 TRACE("(width=%d, height=%d)\n", Width, Height);
9443 LISTVIEW_UpdateSize(infoPtr);
9444 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
9446 /* do not bother with display related stuff if we're not redrawing */
9447 if (!is_redrawing(infoPtr)) return 0;
9449 if (is_autoarrange(infoPtr))
9450 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9452 LISTVIEW_UpdateScroll(infoPtr);
9454 /* refresh all only for lists whose height changed significantly */
9455 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
9456 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
9457 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
9458 LISTVIEW_InvalidateList(infoPtr);
9465 * Sets the size information.
9468 * [I] infoPtr : valid pointer to the listview structure
9473 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
9475 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9477 TRACE("uView=%d, rcList(old)=%s\n", uView, wine_dbgstr_rect(&infoPtr->rcList));
9479 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
9481 if (uView == LVS_LIST)
9483 /* Apparently the "LIST" style is supposed to have the same
9484 * number of items in a column even if there is no scroll bar.
9485 * Since if a scroll bar already exists then the bottom is already
9486 * reduced, only reduce if the scroll bar does not currently exist.
9487 * The "2" is there to mimic the native control. I think it may be
9488 * related to either padding or edges. (GLA 7/2002)
9490 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
9491 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
9492 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
9494 else if (uView == LVS_REPORT)
9499 hl.prc = &infoPtr->rcList;
9501 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9502 TRACE(" wp.flags=0x%08x, wp=%d,%d (%dx%d)\n", wp.flags, wp.x, wp.y, wp.cx, wp.cy);
9503 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy,
9504 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9505 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9506 TRACE(" after SWP wp=%d,%d (%dx%d)\n", wp.x, wp.y, wp.cx, wp.cy);
9508 infoPtr->rcList.top = max(wp.cy, 0);
9509 infoPtr->rcList.top += (infoPtr->dwLvExStyle & LVS_EX_GRIDLINES) ? 2 : 0;
9512 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
9517 * Processes WM_STYLECHANGED messages.
9520 * [I] infoPtr : valid pointer to the listview structure
9521 * [I] wStyleType : window style type (normal or extended)
9522 * [I] lpss : window style information
9527 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9528 const STYLESTRUCT *lpss)
9530 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9531 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9534 TRACE("(styletype=%lx, styleOld=0x%08x, styleNew=0x%08x)\n",
9535 wStyleType, lpss->styleOld, lpss->styleNew);
9537 if (wStyleType != GWL_STYLE) return 0;
9539 /* FIXME: if LVS_NOSORTHEADER changed, update header */
9540 /* what if LVS_OWNERDATA changed? */
9541 /* or LVS_SINGLESEL */
9542 /* or LVS_SORT{AS,DES}CENDING */
9544 infoPtr->dwStyle = lpss->styleNew;
9546 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9547 ((lpss->styleNew & WS_HSCROLL) == 0))
9548 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9550 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9551 ((lpss->styleNew & WS_VSCROLL) == 0))
9552 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9554 if (uNewView != uOldView)
9556 SIZE oldIconSize = infoPtr->iconSize;
9559 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9560 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9562 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9563 SetRectEmpty(&infoPtr->rcFocus);
9565 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9566 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
9568 if (uNewView == LVS_ICON)
9570 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
9572 TRACE("icon old size=(%d,%d), new size=(%d,%d)\n",
9573 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9574 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
9577 else if (uNewView == LVS_REPORT)
9582 LISTVIEW_CreateHeader( infoPtr );
9584 hl.prc = &infoPtr->rcList;
9586 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9587 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy,
9588 wp.flags | ((infoPtr->dwStyle & LVS_NOCOLUMNHEADER)
9589 ? SWP_HIDEWINDOW : SWP_SHOWWINDOW));
9592 LISTVIEW_UpdateItemSize(infoPtr);
9595 if (uNewView == LVS_REPORT)
9597 if ((lpss->styleOld ^ lpss->styleNew) & LVS_NOCOLUMNHEADER)
9599 if (lpss->styleNew & LVS_NOCOLUMNHEADER)
9601 /* Turn off the header control */
9602 style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
9603 TRACE("Hide header control, was 0x%08x\n", style);
9604 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, style | HDS_HIDDEN);
9606 /* Turn on the header control */
9607 if ((style = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE)) & HDS_HIDDEN)
9609 TRACE("Show header control, was 0x%08x\n", style);
9610 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, (style & ~HDS_HIDDEN) | WS_VISIBLE);
9616 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9617 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9618 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9620 /* update the size of the client area */
9621 LISTVIEW_UpdateSize(infoPtr);
9623 /* add scrollbars if needed */
9624 LISTVIEW_UpdateScroll(infoPtr);
9626 /* invalidate client area + erase background */
9627 LISTVIEW_InvalidateList(infoPtr);
9634 * Processes WM_SHOWWINDOW messages.
9637 * [I] infoPtr : valid pointer to the listview structure
9638 * [I] bShown : window is being shown (FALSE when hidden)
9639 * [I] iStatus : window show status
9644 static LRESULT LISTVIEW_ShowWindow(LISTVIEW_INFO *infoPtr, BOOL bShown, INT iStatus)
9646 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9648 /* header delayed creation */
9649 if ((uView == LVS_REPORT) && bShown)
9651 LISTVIEW_CreateHeader(infoPtr);
9653 if (!(LVS_NOCOLUMNHEADER & infoPtr->dwStyle))
9654 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
9662 * Window procedure of the listview control.
9665 static LRESULT WINAPI
9666 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9668 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9670 TRACE("(uMsg=%x wParam=%lx lParam=%lx)\n", uMsg, wParam, lParam);
9672 if (!infoPtr && (uMsg != WM_NCCREATE))
9673 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9677 case LVM_APPROXIMATEVIEWRECT:
9678 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9679 LOWORD(lParam), HIWORD(lParam));
9681 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9683 /* case LVM_CANCELEDITLABEL: */
9685 case LVM_CREATEDRAGIMAGE:
9686 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9688 case LVM_DELETEALLITEMS:
9689 return LISTVIEW_DeleteAllItems(infoPtr, FALSE);
9691 case LVM_DELETECOLUMN:
9692 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9694 case LVM_DELETEITEM:
9695 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9697 case LVM_EDITLABELW:
9698 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9700 case LVM_EDITLABELA:
9701 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9703 /* case LVM_ENABLEGROUPVIEW: */
9705 case LVM_ENSUREVISIBLE:
9706 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9709 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9712 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9714 case LVM_GETBKCOLOR:
9715 return infoPtr->clrBk;
9717 /* case LVM_GETBKIMAGE: */
9719 case LVM_GETCALLBACKMASK:
9720 return infoPtr->uCallbackMask;
9722 case LVM_GETCOLUMNA:
9723 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9725 case LVM_GETCOLUMNW:
9726 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9728 case LVM_GETCOLUMNORDERARRAY:
9729 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9731 case LVM_GETCOLUMNWIDTH:
9732 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9734 case LVM_GETCOUNTPERPAGE:
9735 return LISTVIEW_GetCountPerPage(infoPtr);
9737 case LVM_GETEDITCONTROL:
9738 return (LRESULT)infoPtr->hwndEdit;
9740 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9741 return infoPtr->dwLvExStyle;
9743 /* case LVM_GETGROUPINFO: */
9745 /* case LVM_GETGROUPMETRICS: */
9748 return (LRESULT)infoPtr->hwndHeader;
9750 case LVM_GETHOTCURSOR:
9751 return (LRESULT)infoPtr->hHotCursor;
9753 case LVM_GETHOTITEM:
9754 return infoPtr->nHotItem;
9756 case LVM_GETHOVERTIME:
9757 return infoPtr->dwHoverTime;
9759 case LVM_GETIMAGELIST:
9760 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9762 /* case LVM_GETINSERTMARK: */
9764 /* case LVM_GETINSERTMARKCOLOR: */
9766 /* case LVM_GETINSERTMARKRECT: */
9768 case LVM_GETISEARCHSTRINGA:
9769 case LVM_GETISEARCHSTRINGW:
9770 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9774 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9777 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9779 case LVM_GETITEMCOUNT:
9780 return infoPtr->nItemCount;
9782 case LVM_GETITEMPOSITION:
9783 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9785 case LVM_GETITEMRECT:
9786 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9788 case LVM_GETITEMSPACING:
9789 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9791 case LVM_GETITEMSTATE:
9792 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9794 case LVM_GETITEMTEXTA:
9795 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9797 case LVM_GETITEMTEXTW:
9798 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9800 case LVM_GETNEXTITEM:
9801 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9803 case LVM_GETNUMBEROFWORKAREAS:
9804 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9808 if (!lParam) return FALSE;
9809 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT ||
9810 (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST) return FALSE;
9811 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9814 /* case LVM_GETOUTLINECOLOR: */
9816 /* case LVM_GETSELECTEDCOLUMN: */
9818 case LVM_GETSELECTEDCOUNT:
9819 return LISTVIEW_GetSelectedCount(infoPtr);
9821 case LVM_GETSELECTIONMARK:
9822 return infoPtr->nSelectionMark;
9824 case LVM_GETSTRINGWIDTHA:
9825 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9827 case LVM_GETSTRINGWIDTHW:
9828 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9830 case LVM_GETSUBITEMRECT:
9831 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
9833 case LVM_GETTEXTBKCOLOR:
9834 return infoPtr->clrTextBk;
9836 case LVM_GETTEXTCOLOR:
9837 return infoPtr->clrText;
9839 /* case LVM_GETTILEINFO: */
9841 /* case LVM_GETTILEVIEWINFO: */
9843 case LVM_GETTOOLTIPS:
9844 if( !infoPtr->hwndToolTip )
9845 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9846 return (LRESULT)infoPtr->hwndToolTip;
9848 case LVM_GETTOPINDEX:
9849 return LISTVIEW_GetTopIndex(infoPtr);
9851 case LVM_GETUNICODEFORMAT:
9852 return (infoPtr->notifyFormat == NFR_UNICODE);
9854 /* case LVM_GETVIEW: */
9856 case LVM_GETVIEWRECT:
9857 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9859 case LVM_GETWORKAREAS:
9860 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9863 /* case LVM_HASGROUP: */
9866 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9868 case LVM_INSERTCOLUMNA:
9869 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9871 case LVM_INSERTCOLUMNW:
9872 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9874 /* case LVM_INSERTGROUP: */
9876 /* case LVM_INSERTGROUPSORTED: */
9878 case LVM_INSERTITEMA:
9879 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9881 case LVM_INSERTITEMW:
9882 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9884 /* case LVM_INSERTMARKHITTEST: */
9886 /* case LVM_ISGROUPVIEWENABLED: */
9888 /* case LVM_MAPIDTOINDEX: */
9890 /* case LVM_MAPINDEXTOID: */
9892 /* case LVM_MOVEGROUP: */
9894 /* case LVM_MOVEITEMTOGROUP: */
9896 case LVM_REDRAWITEMS:
9897 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9899 /* case LVM_REMOVEALLGROUPS: */
9901 /* case LVM_REMOVEGROUP: */
9904 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9906 case LVM_SETBKCOLOR:
9907 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9909 /* case LVM_SETBKIMAGE: */
9911 case LVM_SETCALLBACKMASK:
9912 infoPtr->uCallbackMask = (UINT)wParam;
9915 case LVM_SETCOLUMNA:
9916 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9918 case LVM_SETCOLUMNW:
9919 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9921 case LVM_SETCOLUMNORDERARRAY:
9922 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9924 case LVM_SETCOLUMNWIDTH:
9925 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9927 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9928 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9930 /* case LVM_SETGROUPINFO: */
9932 /* case LVM_SETGROUPMETRICS: */
9934 case LVM_SETHOTCURSOR:
9935 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9937 case LVM_SETHOTITEM:
9938 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9940 case LVM_SETHOVERTIME:
9941 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9943 case LVM_SETICONSPACING:
9944 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9946 case LVM_SETIMAGELIST:
9947 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9949 /* case LVM_SETINFOTIP: */
9951 /* case LVM_SETINSERTMARK: */
9953 /* case LVM_SETINSERTMARKCOLOR: */
9956 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9959 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9961 case LVM_SETITEMCOUNT:
9962 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9964 case LVM_SETITEMPOSITION:
9967 pt.x = (short)LOWORD(lParam);
9968 pt.y = (short)HIWORD(lParam);
9969 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9972 case LVM_SETITEMPOSITION32:
9973 if (lParam == 0) return FALSE;
9974 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9976 case LVM_SETITEMSTATE:
9977 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9979 case LVM_SETITEMTEXTA:
9980 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9982 case LVM_SETITEMTEXTW:
9983 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9985 /* case LVM_SETOUTLINECOLOR: */
9987 /* case LVM_SETSELECTEDCOLUMN: */
9989 case LVM_SETSELECTIONMARK:
9990 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9992 case LVM_SETTEXTBKCOLOR:
9993 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9995 case LVM_SETTEXTCOLOR:
9996 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9998 /* case LVM_SETTILEINFO: */
10000 /* case LVM_SETTILEVIEWINFO: */
10002 /* case LVM_SETTILEWIDTH: */
10004 case LVM_SETTOOLTIPS:
10005 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
10007 case LVM_SETUNICODEFORMAT:
10008 return LISTVIEW_SetUnicodeFormat(infoPtr, wParam);
10010 /* case LVM_SETVIEW: */
10012 /* case LVM_SETWORKAREAS: */
10014 /* case LVM_SORTGROUPS: */
10016 case LVM_SORTITEMS:
10017 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
10019 /* LVM_SORTITEMSEX: */
10021 case LVM_SUBITEMHITTEST:
10022 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
10025 return LISTVIEW_Update(infoPtr, (INT)wParam);
10028 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
10031 return LISTVIEW_Command(infoPtr, wParam, lParam);
10034 return LISTVIEW_NCCreate(hwnd, (LPCREATESTRUCTW)lParam);
10037 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
10040 return LISTVIEW_Destroy(infoPtr);
10043 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
10045 case WM_ERASEBKGND:
10046 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
10048 case WM_GETDLGCODE:
10049 return DLGC_WANTCHARS | DLGC_WANTARROWS;
10052 return (LRESULT)infoPtr->hFont;
10055 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10058 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
10061 return LISTVIEW_KillFocus(infoPtr);
10063 case WM_LBUTTONDBLCLK:
10064 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10066 case WM_LBUTTONDOWN:
10067 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10070 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10073 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10075 case WM_MOUSEHOVER:
10076 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10079 return LISTVIEW_NCDestroy(infoPtr);
10082 if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
10087 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
10088 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
10091 case WM_NOTIFYFORMAT:
10092 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
10094 case WM_PRINTCLIENT:
10095 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
10098 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
10100 case WM_RBUTTONDBLCLK:
10101 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10103 case WM_RBUTTONDOWN:
10104 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10107 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
10110 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
10115 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
10118 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
10121 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
10123 case WM_SHOWWINDOW:
10124 LISTVIEW_ShowWindow(infoPtr, (BOOL)wParam, (INT)lParam);
10125 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10128 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
10130 case WM_STYLECHANGED:
10131 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
10133 case WM_SYSCOLORCHANGE:
10134 COMCTL32_RefreshSysColors();
10137 /* case WM_TIMER: */
10138 case WM_THEMECHANGED:
10139 return LISTVIEW_ThemeChanged(infoPtr);
10142 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
10144 case WM_MOUSEWHEEL:
10145 if (wParam & (MK_SHIFT | MK_CONTROL))
10146 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10147 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
10149 case WM_WINDOWPOSCHANGED:
10150 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
10152 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
10153 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
10154 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
10156 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
10158 MEASUREITEMSTRUCT mis;
10159 mis.CtlType = ODT_LISTVIEW;
10160 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
10164 mis.itemHeight= infoPtr->nItemHeight;
10165 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
10166 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
10167 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
10170 LISTVIEW_UpdateSize(infoPtr);
10171 LISTVIEW_UpdateScroll(infoPtr);
10173 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10175 /* case WM_WININICHANGE: */
10178 if ((uMsg >= WM_USER) && (uMsg < WM_APP) && !COMCTL32_IsReflectedMessage(uMsg))
10179 ERR("unknown msg %04x wp=%08lx lp=%08lx\n", uMsg, wParam, lParam);
10182 /* call default window procedure */
10183 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
10190 * Registers the window class.
10198 void LISTVIEW_Register(void)
10200 WNDCLASSW wndClass;
10202 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
10203 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
10204 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
10205 wndClass.cbClsExtra = 0;
10206 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
10207 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
10208 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
10209 wndClass.lpszClassName = WC_LISTVIEWW;
10210 RegisterClassW(&wndClass);
10215 * Unregisters the window class.
10223 void LISTVIEW_Unregister(void)
10225 UnregisterClassW(WC_LISTVIEWW, NULL);
10230 * Handle any WM_COMMAND messages
10233 * [I] infoPtr : valid pointer to the listview structure
10234 * [I] wParam : the first message parameter
10235 * [I] lParam : the second message parameter
10240 static LRESULT LISTVIEW_Command(const LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
10242 switch (HIWORD(wParam))
10247 * Adjust the edit window size
10249 WCHAR buffer[1024];
10250 HDC hdc = GetDC(infoPtr->hwndEdit);
10251 HFONT hFont, hOldFont = 0;
10255 if (!infoPtr->hwndEdit || !hdc) return 0;
10256 GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
10257 GetWindowRect(infoPtr->hwndEdit, &rect);
10259 /* Select font to get the right dimension of the string */
10260 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
10263 hOldFont = SelectObject(hdc, hFont);
10266 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
10268 TEXTMETRICW textMetric;
10270 /* Add Extra spacing for the next character */
10271 GetTextMetricsW(hdc, &textMetric);
10272 sz.cx += (textMetric.tmMaxCharWidth * 2);
10280 rect.bottom - rect.top,
10281 SWP_DRAWFRAME|SWP_NOMOVE);
10284 SelectObject(hdc, hOldFont);
10286 ReleaseDC(infoPtr->hwndEdit, hdc);
10292 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
10301 * Subclassed edit control windproc function
10304 * [I] hwnd : the edit window handle
10305 * [I] uMsg : the message that is to be processed
10306 * [I] wParam : first message parameter
10307 * [I] lParam : second message parameter
10308 * [I] isW : TRUE if input is Unicode
10313 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
10315 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
10316 BOOL cancel = FALSE;
10318 TRACE("(hwnd=%p, uMsg=%x, wParam=%lx, lParam=%lx, isW=%d)\n",
10319 hwnd, uMsg, wParam, lParam, isW);
10323 case WM_GETDLGCODE:
10324 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
10331 WNDPROC editProc = infoPtr->EditWndProc;
10332 infoPtr->EditWndProc = 0;
10333 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
10334 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
10338 if (VK_ESCAPE == (INT)wParam)
10343 else if (VK_RETURN == (INT)wParam)
10347 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
10350 /* kill the edit */
10351 if (infoPtr->hwndEdit)
10353 LPWSTR buffer = NULL;
10355 infoPtr->hwndEdit = 0;
10358 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
10362 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
10364 if (isW) GetWindowTextW(hwnd, buffer, len+1);
10365 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
10369 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
10374 SendMessageW(hwnd, WM_CLOSE, 0, 0);
10380 * Subclassed edit control Unicode windproc function
10383 * [I] hwnd : the edit window handle
10384 * [I] uMsg : the message that is to be processed
10385 * [I] wParam : first message parameter
10386 * [I] lParam : second message parameter
10390 static LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10392 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
10397 * Subclassed edit control ANSI windproc function
10400 * [I] hwnd : the edit window handle
10401 * [I] uMsg : the message that is to be processed
10402 * [I] wParam : first message parameter
10403 * [I] lParam : second message parameter
10407 static LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
10409 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
10414 * Creates a subclassed edit control
10417 * [I] infoPtr : valid pointer to the listview structure
10418 * [I] text : initial text for the edit
10419 * [I] style : the window style
10420 * [I] isW : TRUE if input is Unicode
10424 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
10425 INT x, INT y, INT width, INT height, BOOL isW)
10427 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
10432 TEXTMETRICW textMetric;
10433 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
10435 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
10437 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
10438 hdc = GetDC(infoPtr->hwndSelf);
10440 /* Select the font to get appropriate metric dimensions */
10441 if(infoPtr->hFont != 0)
10442 hOldFont = SelectObject(hdc, infoPtr->hFont);
10444 /*Get String Length in pixels */
10445 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
10447 /*Add Extra spacing for the next character */
10448 GetTextMetricsW(hdc, &textMetric);
10449 sz.cx += (textMetric.tmMaxCharWidth * 2);
10451 if(infoPtr->hFont != 0)
10452 SelectObject(hdc, hOldFont);
10454 ReleaseDC(infoPtr->hwndSelf, hdc);
10456 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10458 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
10460 if (!hedit) return 0;
10462 infoPtr->EditWndProc = (WNDPROC)
10463 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
10464 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
10466 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);