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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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
95 * -- LVS_EX_HEADERDRAGDROP
98 * -- LVS_EX_MULTIWORKAREAS
99 * -- LVS_EX_ONECLICKACTIVATE
101 * -- LVS_EX_SIMPLESELECT
102 * -- LVS_EX_TRACKSELECT
103 * -- LVS_EX_TWOCLICKACTIVATE
104 * -- LVS_EX_UNDERLINECOLD
105 * -- LVS_EX_UNDERLINEHOT
108 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
111 * -- LVN_MARQUEEBEGIN
118 * -- LVM_CANCELEDITLABEL
119 * -- LVM_ENABLEGROUPVIEW
120 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
121 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
122 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
123 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
124 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
125 * -- LVM_GETINSERTMARKRECT
126 * -- LVM_GETNUMBEROFWORKAREAS
127 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
128 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
129 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
130 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
131 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
132 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
133 * -- LVM_GETVIEW, LVM_SETVIEW
134 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
135 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
136 * -- LVM_INSERTGROUPSORTED
137 * -- LVM_INSERTMARKHITTEST
138 * -- LVM_ISGROUPVIEWENABLED
139 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
141 * -- LVM_MOVEITEMTOGROUP
143 * -- LVM_SETTILEWIDTH
148 * -- ListView_GetCheckSate, ListView_SetCheckState
149 * -- ListView_GetHoverTime, ListView_SetHoverTime
150 * -- ListView_GetISearchString
151 * -- ListView_GetNumberOfWorkAreas
152 * -- ListView_GetOrigin
153 * -- ListView_GetTextBkColor
154 * -- ListView_GetUnicodeFormat, ListView_SetUnicodeFormat
155 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
156 * -- ListView_SortItemsEx
161 * Known differences in message stream from native control (not known if
162 * these differences cause problems):
163 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
164 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
165 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
166 * processing for "USEDOUBLECLICKTIME".
170 #include "wine/port.h"
185 #include "commctrl.h"
186 #include "comctl32.h"
189 #include "wine/debug.h"
190 #include "wine/unicode.h"
192 WINE_DEFAULT_DEBUG_CHANNEL(listview);
194 /* make sure you set this to 0 for production use! */
195 #define DEBUG_RANGES 1
197 typedef struct tagCOLUMN_INFO
199 RECT rcHeader; /* tracks the header's rectangle */
200 int fmt; /* same as LVCOLUMN.fmt */
203 typedef struct tagITEMHDR
207 } ITEMHDR, *LPITEMHDR;
209 typedef struct tagSUBITEM_INFO
215 typedef struct tagITEM_INFO
223 typedef struct tagRANGE
229 typedef struct tagRANGES
234 typedef struct tagITERATOR
243 typedef struct tagLISTVIEW_INFO
250 COLORREF clrTextBkDefault;
251 HIMAGELIST himlNormal;
252 HIMAGELIST himlSmall;
253 HIMAGELIST himlState;
256 POINT ptClickPos; /* point where the user clicked */
257 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
260 RANGES selectionRanges;
265 RECT rcList; /* This rectangle is really the window
266 * client rectangle possibly reduced by the
267 * horizontal scroll bar and/or header - see
268 * LISTVIEW_UpdateSize. This rectangle offset
269 * by the LISTVIEW_GetOrigin value is in
270 * client coordinates */
279 INT ntmHeight; /* Some cached metrics of the font used */
280 INT ntmMaxCharWidth; /* by the listview to draw items */
282 BOOL bRedraw; /* Turns on/off repaints & invalidations */
283 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
285 BOOL bDoChangeNotify; /* send change notification messages? */
288 DWORD dwStyle; /* the cached window GWL_STYLE */
289 DWORD dwLvExStyle; /* extended listview style */
290 INT nItemCount; /* the number of items in the list */
291 HDPA hdpaItems; /* array ITEM_INFO pointers */
292 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
293 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
294 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
295 POINT currIconPos; /* this is the position next icon will be placed */
296 PFNLVCOMPARE pfnCompare;
304 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
306 DWORD lastKeyPressTimestamp;
308 INT nSearchParamLength;
309 WCHAR szSearchParam[ MAX_PATH ];
311 INT nMeasureItemHeight;
312 INT xTrackLine; /* The x coefficient of the track line or -1 if none */
318 /* How many we debug buffer to allocate */
319 #define DEBUG_BUFFERS 20
320 /* The size of a single debug bbuffer */
321 #define DEBUG_BUFFER_SIZE 256
323 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
324 #define SB_INTERNAL -1
326 /* maximum size of a label */
327 #define DISP_TEXT_SIZE 512
329 /* padding for items in list and small icon display modes */
330 #define WIDTH_PADDING 12
332 /* padding for items in list, report and small icon display modes */
333 #define HEIGHT_PADDING 1
335 /* offset of items in report display mode */
336 #define REPORT_MARGINX 2
338 /* padding for icon in large icon display mode
339 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
340 * that HITTEST will see.
341 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
342 * ICON_TOP_PADDING - sum of the two above.
343 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
344 * LABEL_HOR_PADDING - between text and sides of box
345 * LABEL_VERT_PADDING - between bottom of text and end of box
347 * ICON_LR_PADDING - additional width above icon size.
348 * ICON_LR_HALF - half of the above value
350 #define ICON_TOP_PADDING_NOTHITABLE 2
351 #define ICON_TOP_PADDING_HITABLE 2
352 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
353 #define ICON_BOTTOM_PADDING 4
354 #define LABEL_HOR_PADDING 5
355 #define LABEL_VERT_PADDING 7
356 #define ICON_LR_PADDING 16
357 #define ICON_LR_HALF (ICON_LR_PADDING/2)
359 /* default label width for items in list and small icon display modes */
360 #define DEFAULT_LABEL_WIDTH 40
362 /* default column width for items in list display mode */
363 #define DEFAULT_COLUMN_WIDTH 128
365 /* Size of "line" scroll for V & H scrolls */
366 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
368 /* Padding betwen image and label */
369 #define IMAGE_PADDING 2
371 /* Padding behind the label */
372 #define TRAILING_LABEL_PADDING 12
373 #define TRAILING_HEADER_PADDING 11
375 /* Border for the icon caption */
376 #define CAPTION_BORDER 2
378 /* Standard DrawText flags */
379 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
380 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
381 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
383 /* Image index from state */
384 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
386 /* The time in milliseconds to reset the search in the list */
387 #define KEY_DELAY 450
389 /* Dump the LISTVIEW_INFO structure to the debug channel */
390 #define LISTVIEW_DUMP(iP) do { \
391 TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
392 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
393 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
394 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
395 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
396 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
397 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
398 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
399 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
400 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
403 static const WCHAR themeClass[] = {'L','i','s','t','V','i','e','w',0};
406 * forward declarations
408 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
409 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
410 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
411 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
412 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
413 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
414 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
415 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
416 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
417 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, const LVITEMW *, BOOL);
418 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
419 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
420 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
421 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
422 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
423 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
424 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
425 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
426 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
427 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
428 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
429 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
430 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
431 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
432 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
433 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
434 static INT LISTVIEW_HitTest(LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
436 /******** Text handling functions *************************************/
438 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
439 * text string. The string may be ANSI or Unicode, in which case
440 * the boolean isW tells us the type of the string.
442 * The name of the function tell what type of strings it expects:
443 * W: Unicode, T: ANSI/Unicode - function of isW
446 static inline BOOL is_textW(LPCWSTR text)
448 return text != NULL && text != LPSTR_TEXTCALLBACKW;
451 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
453 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
454 return is_textW(text);
457 static inline int textlenT(LPCWSTR text, BOOL isW)
459 return !is_textT(text, isW) ? 0 :
460 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
463 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
466 if (isSrcW) lstrcpynW(dest, src, max);
467 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
469 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
470 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
473 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
475 LPWSTR wstr = (LPWSTR)text;
477 if (!isW && is_textT(text, isW))
479 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
480 wstr = Alloc(len * sizeof(WCHAR));
481 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
483 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
487 static inline void textfreeT(LPWSTR wstr, BOOL isW)
489 if (!isW && is_textT(wstr, isW)) Free (wstr);
493 * dest is a pointer to a Unicode string
494 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
496 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
500 if (src == LPSTR_TEXTCALLBACKW)
502 if (is_textW(*dest)) Free(*dest);
503 *dest = LPSTR_TEXTCALLBACKW;
507 LPWSTR pszText = textdupTtoW(src, isW);
508 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
509 bResult = Str_SetPtrW(dest, pszText);
510 textfreeT(pszText, isW);
516 * compares a Unicode to a Unicode/ANSI text string
518 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
520 if (!aw) return bt ? -1 : 0;
521 if (!bt) return aw ? 1 : 0;
522 if (aw == LPSTR_TEXTCALLBACKW)
523 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
524 if (bt != LPSTR_TEXTCALLBACKW)
526 LPWSTR bw = textdupTtoW(bt, isW);
527 int r = bw ? lstrcmpW(aw, bw) : 1;
535 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
539 n = min(min(n, strlenW(s1)), strlenW(s2));
540 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
541 return res ? res - sizeof(WCHAR) : res;
544 /******** Debugging functions *****************************************/
546 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
548 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
549 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
552 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
554 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
555 n = min(textlenT(text, isW), n);
556 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
559 static char* debug_getbuf(void)
561 static int index = 0;
562 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
563 return buffers[index++ % DEBUG_BUFFERS];
566 static inline const char* debugrange(const RANGE *lprng)
568 if (!lprng) return "(null)";
569 return wine_dbg_sprintf("[%d, %d)", lprng->lower, lprng->upper);
572 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
574 char* buf = debug_getbuf(), *text = buf;
575 int len, size = DEBUG_BUFFER_SIZE;
577 if (pScrollInfo == NULL) return "(null)";
578 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
579 if (len == -1) goto end; buf += len; size -= len;
580 if (pScrollInfo->fMask & SIF_RANGE)
581 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
583 if (len == -1) goto end; buf += len; size -= len;
584 if (pScrollInfo->fMask & SIF_PAGE)
585 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
587 if (len == -1) goto end; buf += len; size -= len;
588 if (pScrollInfo->fMask & SIF_POS)
589 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
591 if (len == -1) goto end; buf += len; size -= len;
592 if (pScrollInfo->fMask & SIF_TRACKPOS)
593 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
595 if (len == -1) goto end; buf += len; size -= len;
598 buf = text + strlen(text);
600 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
604 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
606 if (!plvnm) return "(null)";
607 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
608 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
609 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
610 plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam);
613 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
615 char* buf = debug_getbuf(), *text = buf;
616 int len, size = DEBUG_BUFFER_SIZE;
618 if (lpLVItem == NULL) return "(null)";
619 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
620 if (len == -1) goto end; buf += len; size -= len;
621 if (lpLVItem->mask & LVIF_STATE)
622 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
624 if (len == -1) goto end; buf += len; size -= len;
625 if (lpLVItem->mask & LVIF_TEXT)
626 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
628 if (len == -1) goto end; buf += len; size -= len;
629 if (lpLVItem->mask & LVIF_IMAGE)
630 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
632 if (len == -1) goto end; buf += len; size -= len;
633 if (lpLVItem->mask & LVIF_PARAM)
634 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
636 if (len == -1) goto end; buf += len; size -= len;
637 if (lpLVItem->mask & LVIF_INDENT)
638 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
640 if (len == -1) goto end; buf += len; size -= len;
643 buf = text + strlen(text);
645 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
649 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
651 char* buf = debug_getbuf(), *text = buf;
652 int len, size = DEBUG_BUFFER_SIZE;
654 if (lpColumn == NULL) return "(null)";
655 len = snprintf(buf, size, "{");
656 if (len == -1) goto end; buf += len; size -= len;
657 if (lpColumn->mask & LVCF_SUBITEM)
658 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
660 if (len == -1) goto end; buf += len; size -= len;
661 if (lpColumn->mask & LVCF_FMT)
662 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
664 if (len == -1) goto end; buf += len; size -= len;
665 if (lpColumn->mask & LVCF_WIDTH)
666 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
668 if (len == -1) goto end; buf += len; size -= len;
669 if (lpColumn->mask & LVCF_TEXT)
670 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
672 if (len == -1) goto end; buf += len; size -= len;
673 if (lpColumn->mask & LVCF_IMAGE)
674 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
676 if (len == -1) goto end; buf += len; size -= len;
677 if (lpColumn->mask & LVCF_ORDER)
678 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
680 if (len == -1) goto end; buf += len; size -= len;
683 buf = text + strlen(text);
685 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
689 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
691 if (!lpht) return "(null)";
693 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
694 wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
697 /* Return the corresponding text for a given scroll value */
698 static inline LPCSTR debugscrollcode(int nScrollCode)
702 case SB_LINELEFT: return "SB_LINELEFT";
703 case SB_LINERIGHT: return "SB_LINERIGHT";
704 case SB_PAGELEFT: return "SB_PAGELEFT";
705 case SB_PAGERIGHT: return "SB_PAGERIGHT";
706 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
707 case SB_THUMBTRACK: return "SB_THUMBTRACK";
708 case SB_ENDSCROLL: return "SB_ENDSCROLL";
709 case SB_INTERNAL: return "SB_INTERNAL";
710 default: return "unknown";
715 /******** Notification functions i************************************/
717 static LRESULT notify_forward_header(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
719 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
720 (WPARAM)lpnmh->hdr.idFrom, (LPARAM)lpnmh);
723 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
727 TRACE("(code=%d)\n", code);
729 pnmh->hwndFrom = infoPtr->hwndSelf;
730 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
732 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
733 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
735 TRACE(" <= %ld\n", result);
740 static inline BOOL notify(LISTVIEW_INFO *infoPtr, INT code)
743 HWND hwnd = infoPtr->hwndSelf;
744 notify_hdr(infoPtr, code, &nmh);
745 return IsWindow(hwnd);
748 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr, LVHITTESTINFO *htInfo)
759 item.mask = LVIF_PARAM|LVIF_STATE;
760 item.iItem = htInfo->iItem;
762 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
763 nmia.lParam = item.lParam;
764 nmia.uOldState = item.state;
765 nmia.uNewState = item.state | LVIS_ACTIVATING;
766 nmia.uChanged = LVIF_STATE;
769 nmia.iItem = htInfo->iItem;
770 nmia.iSubItem = htInfo->iSubItem;
771 nmia.ptAction = htInfo->pt;
773 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
774 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
775 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
777 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
780 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
782 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
783 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
786 static BOOL notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
790 HWND hwnd = infoPtr->hwndSelf;
792 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
793 ZeroMemory(&nmlv, sizeof(nmlv));
794 nmlv.iItem = lvht->iItem;
795 nmlv.iSubItem = lvht->iSubItem;
796 nmlv.ptAction = lvht->pt;
797 item.mask = LVIF_PARAM;
798 item.iItem = lvht->iItem;
800 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
801 notify_listview(infoPtr, code, &nmlv);
802 return IsWindow(hwnd);
805 static BOOL notify_deleteitem(LISTVIEW_INFO *infoPtr, INT nItem)
809 HWND hwnd = infoPtr->hwndSelf;
811 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
813 item.mask = LVIF_PARAM;
816 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
817 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
818 return IsWindow(hwnd);
821 static int get_ansi_notification(INT unicodeNotificationCode)
823 switch (unicodeNotificationCode)
825 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
826 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
827 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
828 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
829 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
830 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
832 ERR("unknown notification %x\n", unicodeNotificationCode);
838 Send notification. depends on dispinfoW having same
839 structure as dispinfoA.
840 infoPtr : listview struct
841 notificationCode : *Unicode* notification code
842 pdi : dispinfo structure (can be unicode or ansi)
843 isW : TRUE if dispinfo is Unicode
845 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
847 BOOL bResult = FALSE;
848 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
849 INT cchTempBufMax = 0, savCchTextMax = 0, realNotifCode;
850 LPWSTR pszTempBuf = NULL, savPszText = NULL;
852 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
854 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
855 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
858 if (convertToAnsi || convertToUnicode)
860 if (notificationCode != LVN_GETDISPINFOW)
862 cchTempBufMax = convertToUnicode ?
863 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
864 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
868 cchTempBufMax = pdi->item.cchTextMax;
869 *pdi->item.pszText = 0; /* make sure we don't process garbage */
872 pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
873 if (!pszTempBuf) return FALSE;
875 if (convertToUnicode)
876 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
877 pszTempBuf, cchTempBufMax);
879 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
880 cchTempBufMax, NULL, NULL);
882 savCchTextMax = pdi->item.cchTextMax;
883 savPszText = pdi->item.pszText;
884 pdi->item.pszText = pszTempBuf;
885 pdi->item.cchTextMax = cchTempBufMax;
888 if (infoPtr->notifyFormat == NFR_ANSI)
889 realNotifCode = get_ansi_notification(notificationCode);
891 realNotifCode = notificationCode;
892 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
893 bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
895 if (convertToUnicode || convertToAnsi)
897 if (convertToUnicode) /* note : pointer can be changed by app ! */
898 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
899 savCchTextMax, NULL, NULL);
901 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
902 savPszText, savCchTextMax);
903 pdi->item.pszText = savPszText; /* restores our buffer */
904 pdi->item.cchTextMax = savCchTextMax;
910 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc,
911 const RECT *rcBounds, const LVITEMW *lplvItem)
913 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
914 lpnmlvcd->nmcd.hdc = hdc;
915 lpnmlvcd->nmcd.rc = *rcBounds;
916 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
917 lpnmlvcd->clrText = infoPtr->clrText;
918 if (!lplvItem) return;
919 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
920 lpnmlvcd->iSubItem = lplvItem->iSubItem;
921 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
922 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
923 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
924 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
927 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
929 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
932 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
933 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
934 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
935 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
936 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
937 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
941 static void prepaint_setup (LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd)
943 /* apprently, for selected items, we have to override the returned values */
944 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
948 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
949 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
951 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
953 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
954 lpnmlvcd->clrText = comctl32_color.clrBtnText;
958 /* Set the text attributes */
959 if (lpnmlvcd->clrTextBk != CLR_NONE)
961 SetBkMode(hdc, OPAQUE);
962 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
963 SetBkColor(hdc, infoPtr->clrTextBkDefault);
965 SetBkColor(hdc,lpnmlvcd->clrTextBk);
968 SetBkMode(hdc, TRANSPARENT);
969 SetTextColor(hdc, lpnmlvcd->clrText);
972 static inline DWORD notify_postpaint (LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
974 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
977 /******** Item iterator functions **********************************/
979 static RANGES ranges_create(int count);
980 static void ranges_destroy(RANGES ranges);
981 static BOOL ranges_add(RANGES ranges, RANGE range);
982 static BOOL ranges_del(RANGES ranges, RANGE range);
983 static void ranges_dump(RANGES ranges);
985 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
987 RANGE range = { nItem, nItem + 1 };
989 return ranges_add(ranges, range);
992 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
994 RANGE range = { nItem, nItem + 1 };
996 return ranges_del(ranges, range);
1000 * ITERATOR DOCUMENTATION
1002 * The iterator functions allow for easy, and convenient iteration
1003 * over items of iterest in the list. Typically, you create a
1004 * iterator, use it, and destroy it, as such:
1007 * iterator_xxxitems(&i, ...);
1008 * while (iterator_{prev,next}(&i)
1010 * //code which uses i.nItem
1012 * iterator_destroy(&i);
1014 * where xxx is either: framed, or visible.
1015 * Note that it is important that the code destroys the iterator
1016 * after it's done with it, as the creation of the iterator may
1017 * allocate memory, which thus needs to be freed.
1019 * You can iterate both forwards, and backwards through the list,
1020 * by using iterator_next or iterator_prev respectively.
1022 * Lower numbered items are draw on top of higher number items in
1023 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1024 * items may overlap). So, to test items, you should use
1026 * which lists the items top to bottom (in Z-order).
1027 * For drawing items, you should use
1029 * which lists the items bottom to top (in Z-order).
1030 * If you keep iterating over the items after the end-of-items
1031 * marker (-1) is returned, the iterator will start from the
1032 * beginning. Typically, you don't need to test for -1,
1033 * because iterator_{next,prev} will return TRUE if more items
1034 * are to be iterated over, or FALSE otherwise.
1036 * Note: the iterator is defined to be bidirectional. That is,
1037 * any number of prev followed by any number of next, or
1038 * five versa, should leave the iterator at the same item:
1039 * prev * n, next * n = next * n, prev * n
1041 * The iterator has a notion of an out-of-order, special item,
1042 * which sits at the start of the list. This is used in
1043 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1044 * which needs to be first, as it may overlap other items.
1046 * The code is a bit messy because we have:
1047 * - a special item to deal with
1048 * - simple range, or composite range
1050 * If you find bugs, or want to add features, please make sure you
1051 * always check/modify *both* iterator_prev, and iterator_next.
1055 * This function iterates through the items in increasing order,
1056 * but prefixed by the special item, then -1. That is:
1057 * special, 1, 2, 3, ..., n, -1.
1058 * Each item is listed only once.
1060 static inline BOOL iterator_next(ITERATOR* i)
1064 i->nItem = i->nSpecial;
1065 if (i->nItem != -1) return TRUE;
1067 if (i->nItem == i->nSpecial)
1069 if (i->ranges) i->index = 0;
1075 if (i->nItem == i->nSpecial) i->nItem++;
1076 if (i->nItem < i->range.upper) return TRUE;
1081 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1082 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1085 else if (i->nItem >= i->range.upper) goto end;
1087 i->nItem = i->range.lower;
1088 if (i->nItem >= 0) goto testitem;
1095 * This function iterates through the items in decreasing order,
1096 * followed by the special item, then -1. That is:
1097 * n, n-1, ..., 3, 2, 1, special, -1.
1098 * Each item is listed only once.
1100 static inline BOOL iterator_prev(ITERATOR* i)
1107 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1110 if (i->nItem == i->nSpecial)
1118 if (i->nItem == i->nSpecial) i->nItem--;
1119 if (i->nItem >= i->range.lower) return TRUE;
1125 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1128 else if (!start && i->nItem < i->range.lower) goto end;
1130 i->nItem = i->range.upper;
1131 if (i->nItem > 0) goto testitem;
1133 return (i->nItem = i->nSpecial) != -1;
1136 static RANGE iterator_range(ITERATOR* i)
1140 if (!i->ranges) return i->range;
1142 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1144 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1145 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1147 else range.lower = range.upper = 0;
1153 * Releases resources associated with this ierator.
1155 static inline void iterator_destroy(ITERATOR* i)
1157 ranges_destroy(i->ranges);
1161 * Create an empty iterator.
1163 static inline BOOL iterator_empty(ITERATOR* i)
1165 ZeroMemory(i, sizeof(*i));
1166 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1171 * Create an iterator over a range.
1173 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1181 * Create an iterator over a bunch of ranges.
1182 * Please note that the iterator will take ownership of the ranges,
1183 * and will free them upon destruction.
1185 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1193 * Creates an iterator over the items which intersect lprc.
1195 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT *lprc)
1197 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1198 RECT frame = *lprc, rcItem, rcTemp;
1201 /* in case we fail, we want to return an empty iterator */
1202 if (!iterator_empty(i)) return FALSE;
1204 LISTVIEW_GetOrigin(infoPtr, &Origin);
1206 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc));
1207 OffsetRect(&frame, -Origin.x, -Origin.y);
1209 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1213 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1215 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1216 if (IntersectRect(&rcTemp, &rcItem, lprc))
1217 i->nSpecial = infoPtr->nFocusedItem;
1219 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1220 /* to do better here, we need to have PosX, and PosY sorted */
1221 TRACE("building icon ranges:\n");
1222 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1224 rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1225 rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1226 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1227 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1228 if (IntersectRect(&rcTemp, &rcItem, &frame))
1229 ranges_additem(i->ranges, nItem);
1233 else if (uView == LVS_REPORT)
1237 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1238 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1240 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1241 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1242 if (range.upper <= range.lower) return TRUE;
1243 if (!iterator_rangeitems(i, range)) return FALSE;
1244 TRACE(" report=%s\n", debugrange(&i->range));
1248 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1249 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1250 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1251 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1252 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1253 INT lower = nFirstCol * nPerCol + nFirstRow;
1257 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1258 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1260 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1262 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1263 TRACE("building list ranges:\n");
1264 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1266 item_range.lower = nCol * nPerCol + nFirstRow;
1267 if(item_range.lower >= infoPtr->nItemCount) break;
1268 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1269 TRACE(" list=%s\n", debugrange(&item_range));
1270 ranges_add(i->ranges, item_range);
1278 * Creates an iterator over the items which intersect the visible region of hdc.
1280 static BOOL iterator_visibleitems(ITERATOR *i, LISTVIEW_INFO *infoPtr, HDC hdc)
1282 POINT Origin, Position;
1283 RECT rcItem, rcClip;
1286 rgntype = GetClipBox(hdc, &rcClip);
1287 if (rgntype == NULLREGION) return iterator_empty(i);
1288 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1289 if (rgntype == SIMPLEREGION) return TRUE;
1291 /* first deal with the special item */
1292 if (i->nSpecial != -1)
1294 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1295 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1298 /* if we can't deal with the region, we'll just go with the simple range */
1299 LISTVIEW_GetOrigin(infoPtr, &Origin);
1300 TRACE("building visible range:\n");
1301 if (!i->ranges && i->range.lower < i->range.upper)
1303 if (!(i->ranges = ranges_create(50))) return TRUE;
1304 if (!ranges_add(i->ranges, i->range))
1306 ranges_destroy(i->ranges);
1312 /* now delete the invisible items from the list */
1313 while(iterator_next(i))
1315 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1316 rcItem.left = Position.x + Origin.x;
1317 rcItem.top = Position.y + Origin.y;
1318 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1319 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1320 if (!RectVisible(hdc, &rcItem))
1321 ranges_delitem(i->ranges, i->nItem);
1323 /* the iterator should restart on the next iterator_next */
1329 /******** Misc helper functions ************************************/
1331 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1332 WPARAM wParam, LPARAM lParam, BOOL isW)
1334 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1335 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1338 static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr)
1340 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1342 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1343 (uView == LVS_ICON || uView == LVS_SMALLICON);
1346 /******** Internal API functions ************************************/
1348 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1350 static COLUMN_INFO mainItem;
1352 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1353 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1354 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1357 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1359 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1362 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1364 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1367 /* Listview invalidation functions: use _only_ these functions to invalidate */
1369 static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr)
1371 return infoPtr->bRedraw;
1374 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT* rect)
1376 if(!is_redrawing(infoPtr)) return;
1377 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect));
1378 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1381 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem)
1385 if(!is_redrawing(infoPtr)) return;
1386 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1387 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1390 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1392 POINT Origin, Position;
1395 if(!is_redrawing(infoPtr)) return;
1396 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1397 LISTVIEW_GetOrigin(infoPtr, &Origin);
1398 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1399 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1401 rcBox.bottom = infoPtr->nItemHeight;
1402 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1403 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1406 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr)
1408 LISTVIEW_InvalidateRect(infoPtr, NULL);
1411 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1415 if(!is_redrawing(infoPtr)) return;
1416 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1417 rcCol.top = infoPtr->rcList.top;
1418 rcCol.bottom = infoPtr->rcList.bottom;
1419 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1424 * Retrieves the number of items that can fit vertically in the client area.
1427 * [I] infoPtr : valid pointer to the listview structure
1430 * Number of items per row.
1432 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1434 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1436 return max(nListWidth/infoPtr->nItemWidth, 1);
1441 * Retrieves the number of items that can fit horizontally in the client
1445 * [I] infoPtr : valid pointer to the listview structure
1448 * Number of items per column.
1450 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1452 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1454 return max(nListHeight / infoPtr->nItemHeight, 1);
1458 /*************************************************************************
1459 * LISTVIEW_ProcessLetterKeys
1461 * Processes keyboard messages generated by pressing the letter keys
1463 * What this does is perform a case insensitive search from the
1464 * current position with the following quirks:
1465 * - If two chars or more are pressed in quick succession we search
1466 * for the corresponding string (e.g. 'abc').
1467 * - If there is a delay we wipe away the current search string and
1468 * restart with just that char.
1469 * - If the user keeps pressing the same character, whether slowly or
1470 * fast, so that the search string is entirely composed of this
1471 * character ('aaaaa' for instance), then we search for first item
1472 * that starting with that character.
1473 * - If the user types the above character in quick succession, then
1474 * we must also search for the corresponding string ('aaaaa'), and
1475 * go to that string if there is a match.
1478 * [I] hwnd : handle to the window
1479 * [I] charCode : the character code, the actual character
1480 * [I] keyData : key data
1488 * - The current implementation has a list of characters it will
1489 * accept and it ignores averything else. In particular it will
1490 * ignore accentuated characters which seems to match what
1491 * Windows does. But I'm not sure it makes sense to follow
1493 * - We don't sound a beep when the search fails.
1497 * TREEVIEW_ProcessLetterKeys
1499 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1504 WCHAR buffer[MAX_PATH];
1505 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1507 /* simple parameter checking */
1508 if (!charCode || !keyData) return 0;
1510 /* only allow the valid WM_CHARs through */
1511 if (!isalnum(charCode) &&
1512 charCode != '.' && charCode != '`' && charCode != '!' &&
1513 charCode != '@' && charCode != '#' && charCode != '$' &&
1514 charCode != '%' && charCode != '^' && charCode != '&' &&
1515 charCode != '*' && charCode != '(' && charCode != ')' &&
1516 charCode != '-' && charCode != '_' && charCode != '+' &&
1517 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1518 charCode != '}' && charCode != '[' && charCode != '{' &&
1519 charCode != '/' && charCode != '?' && charCode != '>' &&
1520 charCode != '<' && charCode != ',' && charCode != '~')
1523 /* if there's one item or less, there is no where to go */
1524 if (infoPtr->nItemCount <= 1) return 0;
1526 /* update the search parameters */
1527 infoPtr->lastKeyPressTimestamp = GetTickCount();
1528 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1529 if (infoPtr->nSearchParamLength < MAX_PATH)
1530 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1531 if (infoPtr->charCode != charCode)
1532 infoPtr->charCode = charCode = 0;
1534 infoPtr->charCode=charCode;
1535 infoPtr->szSearchParam[0]=charCode;
1536 infoPtr->nSearchParamLength=1;
1537 /* Redundant with the 1 char string */
1541 /* and search from the current position */
1543 if (infoPtr->nFocusedItem >= 0) {
1544 endidx=infoPtr->nFocusedItem;
1546 /* if looking for single character match,
1547 * then we must always move forward
1549 if (infoPtr->nSearchParamLength == 1)
1552 endidx=infoPtr->nItemCount;
1556 if (idx == infoPtr->nItemCount) {
1557 if (endidx == infoPtr->nItemCount || endidx == 0)
1563 item.mask = LVIF_TEXT;
1566 item.pszText = buffer;
1567 item.cchTextMax = MAX_PATH;
1568 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1570 /* check for a match */
1571 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1574 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1575 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1576 /* This would work but we must keep looking for a longer match */
1580 } while (idx != endidx);
1583 LISTVIEW_KeySelection(infoPtr, nItem);
1588 /*************************************************************************
1589 * LISTVIEW_UpdateHeaderSize [Internal]
1591 * Function to resize the header control
1594 * [I] hwnd : handle to a window
1595 * [I] nNewScrollPos : scroll pos to set
1600 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1605 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1607 GetWindowRect(infoPtr->hwndHeader, &winRect);
1608 point[0].x = winRect.left;
1609 point[0].y = winRect.top;
1610 point[1].x = winRect.right;
1611 point[1].y = winRect.bottom;
1613 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1614 point[0].x = -nNewScrollPos;
1615 point[1].x += nNewScrollPos;
1617 SetWindowPos(infoPtr->hwndHeader,0,
1618 point[0].x,point[0].y,point[1].x,point[1].y,
1619 SWP_NOZORDER | SWP_NOACTIVATE);
1624 * Update the scrollbars. This functions should be called whenever
1625 * the content, size or view changes.
1628 * [I] infoPtr : valid pointer to the listview structure
1633 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1635 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1636 SCROLLINFO horzInfo, vertInfo;
1639 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1641 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1642 horzInfo.cbSize = sizeof(SCROLLINFO);
1643 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1645 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1646 if (uView == LVS_LIST)
1648 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1649 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1651 /* scroll by at least one column per page */
1652 if(horzInfo.nPage < infoPtr->nItemWidth)
1653 horzInfo.nPage = infoPtr->nItemWidth;
1655 horzInfo.nPage /= infoPtr->nItemWidth;
1657 else if (uView == LVS_REPORT)
1659 horzInfo.nMax = infoPtr->nItemWidth;
1661 else /* LVS_ICON, or LVS_SMALLICON */
1665 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1668 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1669 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1670 dx = GetScrollPos(infoPtr->hwndSelf, SB_HORZ);
1671 dx -= SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1672 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1674 /* Setting the horizontal scroll can change the listview size
1675 * (and potentially everything else) so we need to recompute
1676 * everything again for the vertical scroll
1679 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1680 vertInfo.cbSize = sizeof(SCROLLINFO);
1681 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1683 if (uView == LVS_REPORT)
1685 vertInfo.nMax = infoPtr->nItemCount;
1687 /* scroll by at least one page */
1688 if(vertInfo.nPage < infoPtr->nItemHeight)
1689 vertInfo.nPage = infoPtr->nItemHeight;
1691 vertInfo.nPage /= infoPtr->nItemHeight;
1693 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1697 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1700 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1701 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1702 dy = GetScrollPos(infoPtr->hwndSelf, SB_VERT);
1703 dy -= SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1704 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1706 /* Change of the range may have changed the scroll pos. If so move the content */
1707 if (dx != 0 || dy != 0)
1710 listRect = infoPtr->rcList;
1711 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &listRect, &listRect, 0, 0,
1712 SW_ERASE | SW_INVALIDATE);
1715 /* Update the Header Control */
1716 if (uView == LVS_REPORT)
1718 horzInfo.fMask = SIF_POS;
1719 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1720 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1727 * Shows/hides the focus rectangle.
1730 * [I] infoPtr : valid pointer to the listview structure
1731 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1736 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1738 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1741 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1743 if (infoPtr->nFocusedItem < 0) return;
1745 /* we need some gymnastics in ICON mode to handle large items */
1746 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1750 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1751 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1753 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1758 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1760 /* for some reason, owner draw should work only in report mode */
1761 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1766 item.iItem = infoPtr->nFocusedItem;
1768 item.mask = LVIF_PARAM;
1769 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1771 ZeroMemory(&dis, sizeof(dis));
1772 dis.CtlType = ODT_LISTVIEW;
1773 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1774 dis.itemID = item.iItem;
1775 dis.itemAction = ODA_FOCUS;
1776 if (fShow) dis.itemState |= ODS_FOCUS;
1777 dis.hwndItem = infoPtr->hwndSelf;
1779 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1780 dis.itemData = item.lParam;
1782 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1786 DrawFocusRect(hdc, &infoPtr->rcFocus);
1789 ReleaseDC(infoPtr->hwndSelf, hdc);
1793 * Invalidates all visible selected items.
1795 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1799 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1800 while(iterator_next(&i))
1802 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1803 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1805 iterator_destroy(&i);
1810 * DESCRIPTION: [INTERNAL]
1811 * Computes an item's (left,top) corner, relative to rcView.
1812 * That is, the position has NOT been made relative to the Origin.
1813 * This is deliberate, to avoid computing the Origin over, and
1814 * over again, when this function is call in a loop. Instead,
1815 * one ca factor the computation of the Origin before the loop,
1816 * and offset the value retured by this function, on every iteration.
1819 * [I] infoPtr : valid pointer to the listview structure
1820 * [I] nItem : item number
1821 * [O] lpptOrig : item top, left corner
1826 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1828 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1830 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1832 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1834 lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1835 lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1837 else if (uView == LVS_LIST)
1839 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1840 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1841 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1843 else /* LVS_REPORT */
1845 lpptPosition->x = 0;
1846 lpptPosition->y = nItem * infoPtr->nItemHeight;
1851 * DESCRIPTION: [INTERNAL]
1852 * Compute the rectangles of an item. This is to localize all
1853 * the computations in one place. If you are not interested in some
1854 * of these values, simply pass in a NULL -- the fucntion is smart
1855 * enough to compute only what's necessary. The function computes
1856 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1857 * one, the BOX rectangle. This rectangle is very cheap to compute,
1858 * and is guaranteed to contain all the other rectangles. Computing
1859 * the ICON rect is also cheap, but all the others are potentaily
1860 * expensive. This gives an easy and effective optimization when
1861 * searching (like point inclusion, or rectangle intersection):
1862 * first test against the BOX, and if TRUE, test agains the desired
1864 * If the function does not have all the necessary information
1865 * to computed the requested rectangles, will crash with a
1866 * failed assertion. This is done so we catch all programming
1867 * errors, given that the function is called only from our code.
1869 * We have the following 'special' meanings for a few fields:
1870 * * If LVIS_FOCUSED is set, we assume the item has the focus
1871 * This is important in ICON mode, where it might get a larger
1872 * then usual rectange
1874 * Please note that subitem support works only in REPORT mode.
1877 * [I] infoPtr : valid pointer to the listview structure
1878 * [I] lpLVItem : item to compute the measures for
1879 * [O] lprcBox : ptr to Box rectangle
1880 * The internal LVIR_BOX rectangle
1881 * [0] lprcState : ptr to State icon rectangle
1882 * The internal LVIR_STATE rectangle
1883 * [O] lprcIcon : ptr to Icon rectangle
1884 * Same as LVM_GETITEMRECT with LVIR_ICON
1885 * [O] lprcLabel : ptr to Label rectangle
1886 * Same as LVM_GETITEMRECT with LVIR_LABEL
1891 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1892 LPRECT lprcBox, LPRECT lprcState,
1893 LPRECT lprcIcon, LPRECT lprcLabel)
1895 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1896 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1897 RECT Box, State, Icon, Label;
1898 COLUMN_INFO *lpColumnInfo = NULL;
1900 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1902 /* Be smart and try to figure out the minimum we have to do */
1903 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1904 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1906 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1907 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1909 if (lprcLabel) doLabel = TRUE;
1910 if (doLabel || lprcIcon) doIcon = TRUE;
1911 if (doIcon || lprcState) doState = TRUE;
1913 /************************************************************/
1914 /* compute the box rectangle (it should be cheap to do) */
1915 /************************************************************/
1916 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1917 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1919 if (lpLVItem->iSubItem)
1921 Box = lpColumnInfo->rcHeader;
1926 Box.right = infoPtr->nItemWidth;
1929 Box.bottom = infoPtr->nItemHeight;
1931 /************************************************************/
1932 /* compute STATEICON bounding box */
1933 /************************************************************/
1936 if (uView == LVS_ICON)
1938 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1939 if (infoPtr->himlNormal)
1940 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1941 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1945 /* we need the ident in report mode, if we don't have it, we fail */
1946 State.left = Box.left;
1947 if (uView == LVS_REPORT)
1949 if (lpLVItem->iSubItem == 0)
1951 State.left += REPORT_MARGINX;
1952 assert(lpLVItem->mask & LVIF_INDENT);
1953 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1956 State.top = Box.top;
1958 State.right = State.left;
1959 State.bottom = State.top;
1960 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1962 State.right += infoPtr->iconStateSize.cx;
1963 State.bottom += infoPtr->iconStateSize.cy;
1965 if (lprcState) *lprcState = State;
1966 TRACE(" - state=%s\n", wine_dbgstr_rect(&State));
1968 else State.right = 0;
1970 /************************************************************/
1971 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1972 /************************************************************/
1975 if (uView == LVS_ICON)
1977 Icon.left = Box.left;
1978 if (infoPtr->himlNormal)
1979 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1980 Icon.top = Box.top + ICON_TOP_PADDING;
1981 Icon.right = Icon.left;
1982 Icon.bottom = Icon.top;
1983 if (infoPtr->himlNormal)
1985 Icon.right += infoPtr->iconSize.cx;
1986 Icon.bottom += infoPtr->iconSize.cy;
1989 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1991 Icon.left = State.right;
1993 Icon.right = Icon.left;
1994 if (infoPtr->himlSmall &&
1995 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
1996 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
1997 Icon.right += infoPtr->iconSize.cx;
1998 Icon.bottom = Icon.top + infoPtr->nItemHeight;
2000 if(lprcIcon) *lprcIcon = Icon;
2001 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon));
2003 else Icon.right = 0;
2005 /************************************************************/
2006 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
2007 /************************************************************/
2010 SIZE labelSize = { 0, 0 };
2012 /* calculate how far to the right can the label strech */
2013 Label.right = Box.right;
2014 if (uView == LVS_REPORT)
2016 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
2019 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
2021 labelSize.cx = infoPtr->nItemWidth;
2022 labelSize.cy = infoPtr->nItemHeight;
2026 /* we need the text in non owner draw mode */
2027 assert(lpLVItem->mask & LVIF_TEXT);
2028 if (is_textT(lpLVItem->pszText, TRUE))
2030 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2031 HDC hdc = GetDC(infoPtr->hwndSelf);
2032 HFONT hOldFont = SelectObject(hdc, hFont);
2036 /* compute rough rectangle where the label will go */
2037 SetRectEmpty(&rcText);
2038 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2039 rcText.bottom = infoPtr->nItemHeight;
2040 if (uView == LVS_ICON)
2041 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2043 /* now figure out the flags */
2044 if (uView == LVS_ICON)
2045 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2047 uFormat = LV_SL_DT_FLAGS;
2049 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2051 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2052 labelSize.cy = rcText.bottom - rcText.top;
2054 SelectObject(hdc, hOldFont);
2055 ReleaseDC(infoPtr->hwndSelf, hdc);
2059 if (uView == LVS_ICON)
2061 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2062 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2063 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2064 Label.right = Label.left + labelSize.cx;
2065 Label.bottom = Label.top + infoPtr->nItemHeight;
2066 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2068 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2069 labelSize.cy /= infoPtr->ntmHeight;
2070 labelSize.cy = max(labelSize.cy, 1);
2071 labelSize.cy *= infoPtr->ntmHeight;
2073 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2075 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2077 Label.left = Icon.right;
2078 Label.top = Box.top;
2079 Label.right = min(Label.left + labelSize.cx, Label.right);
2080 Label.bottom = Label.top + infoPtr->nItemHeight;
2083 if (lprcLabel) *lprcLabel = Label;
2084 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label));
2087 /* Fix the Box if necessary */
2090 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2091 else *lprcBox = Box;
2093 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box));
2097 * DESCRIPTION: [INTERNAL]
2100 * [I] infoPtr : valid pointer to the listview structure
2101 * [I] nItem : item number
2102 * [O] lprcBox : ptr to Box rectangle
2107 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2109 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2110 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2111 POINT Position, Origin;
2114 LISTVIEW_GetOrigin(infoPtr, &Origin);
2115 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2117 /* Be smart and try to figure out the minimum we have to do */
2119 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2120 lvItem.mask |= LVIF_TEXT;
2121 lvItem.iItem = nItem;
2122 lvItem.iSubItem = 0;
2123 lvItem.pszText = szDispText;
2124 lvItem.cchTextMax = DISP_TEXT_SIZE;
2125 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2126 if (uView == LVS_ICON)
2128 lvItem.mask |= LVIF_STATE;
2129 lvItem.stateMask = LVIS_FOCUSED;
2130 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2132 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
2134 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2140 * Returns the current icon position, and advances it along the top.
2141 * The returned position is not offset by Origin.
2144 * [I] infoPtr : valid pointer to the listview structure
2145 * [O] lpPos : will get the current icon position
2150 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2152 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2154 *lpPos = infoPtr->currIconPos;
2156 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2157 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2159 infoPtr->currIconPos.x = 0;
2160 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2166 * Returns the current icon position, and advances it down the left edge.
2167 * The returned position is not offset by Origin.
2170 * [I] infoPtr : valid pointer to the listview structure
2171 * [O] lpPos : will get the current icon position
2176 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2178 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2180 *lpPos = infoPtr->currIconPos;
2182 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2183 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2185 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2186 infoPtr->currIconPos.y = 0;
2192 * Moves an icon to the specified position.
2193 * It takes care of invalidating the item, etc.
2196 * [I] infoPtr : valid pointer to the listview structure
2197 * [I] nItem : the item to move
2198 * [I] lpPos : the new icon position
2199 * [I] isNew : flags the item as being new
2205 static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2211 old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2212 old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2214 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2215 LISTVIEW_InvalidateItem(infoPtr, nItem);
2218 /* Allocating a POINTER for every item is too resource intensive,
2219 * so we'll keep the (x,y) in different arrays */
2220 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2221 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2223 LISTVIEW_InvalidateItem(infoPtr, nItem);
2230 * Arranges listview items in icon display mode.
2233 * [I] infoPtr : valid pointer to the listview structure
2234 * [I] nAlignCode : alignment code
2240 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2242 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2243 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2247 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2249 TRACE("nAlignCode=%d\n", nAlignCode);
2251 if (nAlignCode == LVA_DEFAULT)
2253 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2254 else nAlignCode = LVA_ALIGNTOP;
2259 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2260 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2261 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2262 default: return FALSE;
2265 infoPtr->bAutoarrange = TRUE;
2266 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2267 for (i = 0; i < infoPtr->nItemCount; i++)
2269 next_pos(infoPtr, &pos);
2270 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2278 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2281 * [I] infoPtr : valid pointer to the listview structure
2282 * [O] lprcView : bounding rectangle
2288 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2292 SetRectEmpty(lprcView);
2294 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2298 for (i = 0; i < infoPtr->nItemCount; i++)
2300 x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2301 y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2302 lprcView->right = max(lprcView->right, x);
2303 lprcView->bottom = max(lprcView->bottom, y);
2305 if (infoPtr->nItemCount > 0)
2307 lprcView->right += infoPtr->nItemWidth;
2308 lprcView->bottom += infoPtr->nItemHeight;
2313 y = LISTVIEW_GetCountPerColumn(infoPtr);
2314 x = infoPtr->nItemCount / y;
2315 if (infoPtr->nItemCount % y) x++;
2316 lprcView->right = x * infoPtr->nItemWidth;
2317 lprcView->bottom = y * infoPtr->nItemHeight;
2321 lprcView->right = infoPtr->nItemWidth;
2322 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2329 * Retrieves the bounding rectangle of all the items.
2332 * [I] infoPtr : valid pointer to the listview structure
2333 * [O] lprcView : bounding rectangle
2339 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2343 TRACE("(lprcView=%p)\n", lprcView);
2345 if (!lprcView) return FALSE;
2347 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2348 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2349 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2351 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView));
2358 * Retrieves the subitem pointer associated with the subitem index.
2361 * [I] hdpaSubItems : DPA handle for a specific item
2362 * [I] nSubItem : index of subitem
2365 * SUCCESS : subitem pointer
2368 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2370 SUBITEM_INFO *lpSubItem;
2373 /* we should binary search here if need be */
2374 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2376 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2377 if (lpSubItem->iSubItem == nSubItem)
2387 * Caclulates the desired item width.
2390 * [I] infoPtr : valid pointer to the listview structure
2393 * The desired item width.
2395 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr)
2397 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2400 TRACE("uView=%d\n", uView);
2402 if (uView == LVS_ICON)
2403 nItemWidth = infoPtr->iconSpacing.cx;
2404 else if (uView == LVS_REPORT)
2408 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2410 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2411 nItemWidth = rcHeader.right;
2414 else /* LVS_SMALLICON, or LVS_LIST */
2418 for (i = 0; i < infoPtr->nItemCount; i++)
2419 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2421 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2422 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2424 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2427 return max(nItemWidth, 1);
2432 * Caclulates the desired item height.
2435 * [I] infoPtr : valid pointer to the listview structure
2438 * The desired item height.
2440 static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr)
2442 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2445 TRACE("uView=%d\n", uView);
2447 if (uView == LVS_ICON)
2448 nItemHeight = infoPtr->iconSpacing.cy;
2451 nItemHeight = infoPtr->ntmHeight;
2452 if (infoPtr->himlState)
2453 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2454 if (infoPtr->himlSmall)
2455 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2456 if (infoPtr->himlState || infoPtr->himlSmall)
2457 nItemHeight += HEIGHT_PADDING;
2458 if (infoPtr->nMeasureItemHeight > 0)
2459 nItemHeight = infoPtr->nMeasureItemHeight;
2462 return max(nItemHeight, 1);
2467 * Updates the width, and height of an item.
2470 * [I] infoPtr : valid pointer to the listview structure
2475 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2477 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2478 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2484 * Retrieves and saves important text metrics info for the current
2488 * [I] infoPtr : valid pointer to the listview structure
2491 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2493 HDC hdc = GetDC(infoPtr->hwndSelf);
2494 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2495 HFONT hOldFont = SelectObject(hdc, hFont);
2499 if (GetTextMetricsW(hdc, &tm))
2501 infoPtr->ntmHeight = tm.tmHeight;
2502 infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
2505 if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
2506 infoPtr->nEllipsisWidth = sz.cx;
2508 SelectObject(hdc, hOldFont);
2509 ReleaseDC(infoPtr->hwndSelf, hdc);
2511 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2516 * A compare function for ranges
2519 * [I] range1 : pointer to range 1;
2520 * [I] range2 : pointer to range 2;
2524 * > 0 : if range 1 > range 2
2525 * < 0 : if range 2 > range 1
2526 * = 0 : if range intersects range 2
2528 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2532 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2534 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2539 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2545 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2547 #define ranges_check(ranges, desc) do { } while(0)
2550 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2555 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2557 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2558 ranges_dump(ranges);
2559 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2560 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2561 assert (prev->lower >= 0 && prev->lower < prev->upper);
2562 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2564 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2565 assert (prev->upper <= curr->lower);
2566 assert (curr->lower < curr->upper);
2569 TRACE("--- Done checking---\n");
2572 static RANGES ranges_create(int count)
2574 RANGES ranges = (RANGES)Alloc(sizeof(struct tagRANGES));
2575 if (!ranges) return NULL;
2576 ranges->hdpa = DPA_Create(count);
2577 if (ranges->hdpa) return ranges;
2582 static void ranges_clear(RANGES ranges)
2586 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2587 Free(DPA_GetPtr(ranges->hdpa, i));
2588 DPA_DeleteAllPtrs(ranges->hdpa);
2592 static void ranges_destroy(RANGES ranges)
2594 if (!ranges) return;
2595 ranges_clear(ranges);
2596 DPA_Destroy(ranges->hdpa);
2600 static RANGES ranges_clone(RANGES ranges)
2605 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2607 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2609 RANGE *newrng = (RANGE *)Alloc(sizeof(RANGE));
2610 if (!newrng) goto fail;
2611 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2612 DPA_SetPtr(clone->hdpa, i, newrng);
2617 TRACE ("clone failed\n");
2618 ranges_destroy(clone);
2622 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2626 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2627 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2632 static void ranges_dump(RANGES ranges)
2636 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2637 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2640 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2642 RANGE srchrng = { nItem, nItem + 1 };
2644 TRACE("(nItem=%d)\n", nItem);
2645 ranges_check(ranges, "before contain");
2646 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2649 static INT ranges_itemcount(RANGES ranges)
2653 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2655 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2656 count += sel->upper - sel->lower;
2662 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2664 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2667 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2668 if (index == -1) return TRUE;
2670 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2672 chkrng = DPA_GetPtr(ranges->hdpa, index);
2673 if (chkrng->lower >= nItem)
2674 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2675 if (chkrng->upper > nItem)
2676 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2681 static BOOL ranges_add(RANGES ranges, RANGE range)
2686 TRACE("(%s)\n", debugrange(&range));
2687 ranges_check(ranges, "before add");
2689 /* try find overlapping regions first */
2690 srchrgn.lower = range.lower - 1;
2691 srchrgn.upper = range.upper + 1;
2692 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2698 TRACE("Adding new range\n");
2700 /* create the brand new range to insert */
2701 newrgn = (RANGE *)Alloc(sizeof(RANGE));
2702 if(!newrgn) goto fail;
2705 /* figure out where to insert it */
2706 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2707 TRACE("index=%d\n", index);
2708 if (index == -1) index = 0;
2710 /* and get it over with */
2711 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2719 RANGE *chkrgn, *mrgrgn;
2720 INT fromindex, mergeindex;
2722 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2723 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2725 chkrgn->lower = min(range.lower, chkrgn->lower);
2726 chkrgn->upper = max(range.upper, chkrgn->upper);
2728 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2730 /* merge now common anges */
2732 srchrgn.lower = chkrgn->lower - 1;
2733 srchrgn.upper = chkrgn->upper + 1;
2737 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2738 if (mergeindex == -1) break;
2739 if (mergeindex == index)
2741 fromindex = index + 1;
2745 TRACE("Merge with index %i\n", mergeindex);
2747 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2748 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2749 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2751 DPA_DeletePtr(ranges->hdpa, mergeindex);
2752 if (mergeindex < index) index --;
2756 ranges_check(ranges, "after add");
2760 ranges_check(ranges, "failed add");
2764 static BOOL ranges_del(RANGES ranges, RANGE range)
2769 TRACE("(%s)\n", debugrange(&range));
2770 ranges_check(ranges, "before del");
2772 /* we don't use DPAS_SORTED here, since we need *
2773 * to find the first overlapping range */
2774 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2777 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2779 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2781 /* case 1: Same range */
2782 if ( (chkrgn->upper == range.upper) &&
2783 (chkrgn->lower == range.lower) )
2785 DPA_DeletePtr(ranges->hdpa, index);
2788 /* case 2: engulf */
2789 else if ( (chkrgn->upper <= range.upper) &&
2790 (chkrgn->lower >= range.lower) )
2792 DPA_DeletePtr(ranges->hdpa, index);
2794 /* case 3: overlap upper */
2795 else if ( (chkrgn->upper <= range.upper) &&
2796 (chkrgn->lower < range.lower) )
2798 chkrgn->upper = range.lower;
2800 /* case 4: overlap lower */
2801 else if ( (chkrgn->upper > range.upper) &&
2802 (chkrgn->lower >= range.lower) )
2804 chkrgn->lower = range.upper;
2807 /* case 5: fully internal */
2810 RANGE tmprgn = *chkrgn, *newrgn;
2812 if (!(newrgn = (RANGE *)Alloc(sizeof(RANGE)))) goto fail;
2813 newrgn->lower = chkrgn->lower;
2814 newrgn->upper = range.lower;
2815 chkrgn->lower = range.upper;
2816 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2825 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2828 ranges_check(ranges, "after del");
2832 ranges_check(ranges, "failed del");
2838 * Removes all selection ranges
2841 * [I] infoPtr : valid pointer to the listview structure
2842 * [I] toSkip : item range to skip removing the selection
2848 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2857 lvItem.stateMask = LVIS_SELECTED;
2859 /* need to clone the DPA because callbacks can change it */
2860 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2861 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2862 while(iterator_next(&i))
2863 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2864 /* note that the iterator destructor will free the cloned range */
2865 iterator_destroy(&i);
2870 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2874 if (!(toSkip = ranges_create(1))) return FALSE;
2875 if (nItem != -1) ranges_additem(toSkip, nItem);
2876 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2877 ranges_destroy(toSkip);
2881 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2883 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2888 * Retrieves the number of items that are marked as selected.
2891 * [I] infoPtr : valid pointer to the listview structure
2894 * Number of items selected.
2896 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2898 INT nSelectedCount = 0;
2900 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2903 for (i = 0; i < infoPtr->nItemCount; i++)
2905 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2910 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2912 TRACE("nSelectedCount=%d\n", nSelectedCount);
2913 return nSelectedCount;
2918 * Manages the item focus.
2921 * [I] infoPtr : valid pointer to the listview structure
2922 * [I] nItem : item index
2925 * TRUE : focused item changed
2926 * FALSE : focused item has NOT changed
2928 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2930 INT oldFocus = infoPtr->nFocusedItem;
2933 if (nItem == infoPtr->nFocusedItem) return FALSE;
2935 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2936 lvItem.stateMask = LVIS_FOCUSED;
2937 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2939 return oldFocus != infoPtr->nFocusedItem;
2942 /* Helper function for LISTVIEW_ShiftIndices *only* */
2943 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2945 if (nShiftItem < nItem) return nShiftItem;
2947 if (nShiftItem > nItem) return nShiftItem + direction;
2949 if (direction > 0) return nShiftItem + direction;
2951 return min(nShiftItem, infoPtr->nItemCount - 1);
2956 * Updates the various indices after an item has been inserted or deleted.
2959 * [I] infoPtr : valid pointer to the listview structure
2960 * [I] nItem : item index
2961 * [I] direction : Direction of shift, +1 or -1.
2966 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2971 /* temporarily disable change notification while shifting items */
2972 bOldChange = infoPtr->bDoChangeNotify;
2973 infoPtr->bDoChangeNotify = FALSE;
2975 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2977 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2979 assert(abs(direction) == 1);
2981 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2983 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2984 if (nNewFocus != infoPtr->nFocusedItem)
2985 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2987 /* But we are not supposed to modify nHotItem! */
2989 infoPtr->bDoChangeNotify = bOldChange;
2995 * Adds a block of selections.
2998 * [I] infoPtr : valid pointer to the listview structure
2999 * [I] nItem : item index
3002 * Whether the window is still valid.
3004 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3006 INT nFirst = min(infoPtr->nSelectionMark, nItem);
3007 INT nLast = max(infoPtr->nSelectionMark, nItem);
3008 HWND hwndSelf = infoPtr->hwndSelf;
3009 NMLVODSTATECHANGE nmlv;
3014 /* Temporarily disable change notification
3015 * If the control is LVS_OWNERDATA, we need to send
3016 * only one LVN_ODSTATECHANGED notification.
3017 * See MSDN documentation for LVN_ITEMCHANGED.
3019 bOldChange = infoPtr->bDoChangeNotify;
3020 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3022 if (nFirst == -1) nFirst = nItem;
3024 item.state = LVIS_SELECTED;
3025 item.stateMask = LVIS_SELECTED;
3027 for (i = nFirst; i <= nLast; i++)
3028 LISTVIEW_SetItemState(infoPtr,i,&item);
3030 ZeroMemory(&nmlv, sizeof(nmlv));
3031 nmlv.iFrom = nFirst;
3034 nmlv.uOldState = item.state;
3036 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3037 if (!IsWindow(hwndSelf))
3039 infoPtr->bDoChangeNotify = bOldChange;
3046 * Sets a single group selection.
3049 * [I] infoPtr : valid pointer to the listview structure
3050 * [I] nItem : item index
3055 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3057 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3063 if (!(selection = ranges_create(100))) return;
3065 item.state = LVIS_SELECTED;
3066 item.stateMask = LVIS_SELECTED;
3068 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3070 if (infoPtr->nSelectionMark == -1)
3072 infoPtr->nSelectionMark = nItem;
3073 ranges_additem(selection, nItem);
3079 sel.lower = min(infoPtr->nSelectionMark, nItem);
3080 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3081 ranges_add(selection, sel);
3086 RECT rcItem, rcSel, rcSelMark;
3089 rcItem.left = LVIR_BOUNDS;
3090 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3091 rcSelMark.left = LVIR_BOUNDS;
3092 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3093 UnionRect(&rcSel, &rcItem, &rcSelMark);
3094 iterator_frameditems(&i, infoPtr, &rcSel);
3095 while(iterator_next(&i))
3097 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3098 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3100 iterator_destroy(&i);
3103 bOldChange = infoPtr->bDoChangeNotify;
3104 infoPtr->bDoChangeNotify = FALSE;
3106 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3109 iterator_rangesitems(&i, selection);
3110 while(iterator_next(&i))
3111 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3112 /* this will also destroy the selection */
3113 iterator_destroy(&i);
3115 infoPtr->bDoChangeNotify = bOldChange;
3117 LISTVIEW_SetItemFocus(infoPtr, nItem);
3122 * Sets a single selection.
3125 * [I] infoPtr : valid pointer to the listview structure
3126 * [I] nItem : item index
3131 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3135 TRACE("nItem=%d\n", nItem);
3137 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3139 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3140 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3141 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3143 infoPtr->nSelectionMark = nItem;
3148 * Set selection(s) with keyboard.
3151 * [I] infoPtr : valid pointer to the listview structure
3152 * [I] nItem : item index
3155 * SUCCESS : TRUE (needs to be repainted)
3156 * FAILURE : FALSE (nothing has changed)
3158 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3160 /* FIXME: pass in the state */
3161 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3162 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3163 BOOL bResult = FALSE;
3165 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3166 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3168 if (infoPtr->dwStyle & LVS_SINGLESEL)
3171 LISTVIEW_SetSelection(infoPtr, nItem);
3178 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3183 lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3184 lvItem.stateMask = LVIS_SELECTED;
3185 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3187 if (lvItem.state & LVIS_SELECTED)
3188 infoPtr->nSelectionMark = nItem;
3190 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3195 LISTVIEW_SetSelection(infoPtr, nItem);
3198 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3201 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3205 static BOOL LISTVIEW_GetItemAtPt(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3207 LVHITTESTINFO lvHitTestInfo;
3209 ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3210 lvHitTestInfo.pt.x = pt.x;
3211 lvHitTestInfo.pt.y = pt.y;
3213 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3215 lpLVItem->mask = LVIF_PARAM;
3216 lpLVItem->iItem = lvHitTestInfo.iItem;
3217 lpLVItem->iSubItem = 0;
3219 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3224 * Called when the mouse is being actively tracked and has hovered for a specified
3228 * [I] infoPtr : valid pointer to the listview structure
3229 * [I] fwKeys : key indicator
3230 * [I] x,y : mouse position
3233 * 0 if the message was processed, non-zero if there was an error
3236 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3237 * over the item for a certain period of time.
3240 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, INT x, INT y)
3242 if (infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3250 if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3251 LISTVIEW_SetSelection(infoPtr, item.iItem);
3259 * Called whenever WM_MOUSEMOVE is received.
3262 * [I] infoPtr : valid pointer to the listview structure
3263 * [I] fwKeys : key indicator
3264 * [I] x,y : mouse position
3267 * 0 if the message is processed, non-zero if there was an error
3269 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3271 TRACKMOUSEEVENT trackinfo;
3273 if (!(fwKeys & MK_LBUTTON))
3274 infoPtr->bLButtonDown = FALSE;
3276 if (infoPtr->bLButtonDown && DragDetect(infoPtr->hwndSelf, infoPtr->ptClickPos))
3278 LVHITTESTINFO lvHitTestInfo;
3281 lvHitTestInfo.pt = infoPtr->ptClickPos;
3282 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3284 ZeroMemory(&nmlv, sizeof(nmlv));
3285 nmlv.iItem = lvHitTestInfo.iItem;
3286 nmlv.ptAction = infoPtr->ptClickPos;
3288 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3293 infoPtr->bLButtonDown = FALSE;
3295 /* see if we are supposed to be tracking mouse hovering */
3296 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3297 /* fill in the trackinfo struct */
3298 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3299 trackinfo.dwFlags = TME_QUERY;
3300 trackinfo.hwndTrack = infoPtr->hwndSelf;
3301 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3303 /* see if we are already tracking this hwnd */
3304 _TrackMouseEvent(&trackinfo);
3306 if(!(trackinfo.dwFlags & TME_HOVER)) {
3307 trackinfo.dwFlags = TME_HOVER;
3309 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3310 _TrackMouseEvent(&trackinfo);
3319 * Tests wheather the item is assignable to a list with style lStyle
3321 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3323 if ( (lpLVItem->mask & LVIF_TEXT) &&
3324 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3325 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3333 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3336 * [I] infoPtr : valid pointer to the listview structure
3337 * [I] lpLVItem : valid pointer to new item atttributes
3338 * [I] isNew : the item being set is being inserted
3339 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3340 * [O] bChanged : will be set to TRUE if the item really changed
3346 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3348 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3356 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3358 if (lpLVItem->mask == 0) return TRUE;
3360 if (infoPtr->dwStyle & LVS_OWNERDATA)
3362 /* a virtual listview we stores only selection and focus */
3363 if (lpLVItem->mask & ~LVIF_STATE)
3369 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3370 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3374 /* we need to get the lParam and state of the item */
3375 item.iItem = lpLVItem->iItem;
3376 item.iSubItem = lpLVItem->iSubItem;
3377 item.mask = LVIF_STATE | LVIF_PARAM;
3378 item.stateMask = ~0;
3381 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3383 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3384 /* determine what fields will change */
3385 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3386 uChanged |= LVIF_STATE;
3388 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3389 uChanged |= LVIF_IMAGE;
3391 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3392 uChanged |= LVIF_PARAM;
3394 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3395 uChanged |= LVIF_INDENT;
3397 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3398 uChanged |= LVIF_TEXT;
3400 TRACE("uChanged=0x%x\n", uChanged);
3401 if (!uChanged) return TRUE;
3404 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3405 nmlv.iItem = lpLVItem->iItem;
3406 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3407 nmlv.uOldState = item.state;
3408 nmlv.uChanged = uChanged;
3409 nmlv.lParam = item.lParam;
3411 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3412 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3414 if(lpItem && !isNew && infoPtr->bDoChangeNotify)
3416 HWND hwndSelf = infoPtr->hwndSelf;
3418 if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3420 if (!IsWindow(hwndSelf))
3424 /* copy information */
3425 if (lpLVItem->mask & LVIF_TEXT)
3426 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3428 if (lpLVItem->mask & LVIF_IMAGE)
3429 lpItem->hdr.iImage = lpLVItem->iImage;
3431 if (lpLVItem->mask & LVIF_PARAM)
3432 lpItem->lParam = lpLVItem->lParam;
3434 if (lpLVItem->mask & LVIF_INDENT)
3435 lpItem->iIndent = lpLVItem->iIndent;
3437 if (uChanged & LVIF_STATE)
3439 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3441 lpItem->state &= ~lpLVItem->stateMask;
3442 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3444 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3446 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3447 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3449 else if (lpLVItem->stateMask & LVIS_SELECTED)
3450 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3452 /* if we are asked to change focus, and we manage it, do it */
3453 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3455 if (lpLVItem->state & LVIS_FOCUSED)
3457 LISTVIEW_SetItemFocus(infoPtr, -1);
3458 infoPtr->nFocusedItem = lpLVItem->iItem;
3459 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3461 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3462 infoPtr->nFocusedItem = -1;
3466 /* if we're inserting the item, we're done */
3467 if (isNew) return TRUE;
3469 /* send LVN_ITEMCHANGED notification */
3470 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3471 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3478 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3481 * [I] infoPtr : valid pointer to the listview structure
3482 * [I] lpLVItem : valid pointer to new subitem atttributes
3483 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3484 * [O] bChanged : will be set to TRUE if the item really changed
3490 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3493 SUBITEM_INFO *lpSubItem;
3495 /* we do not support subitems for virtual listviews */
3496 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3498 /* set subitem only if column is present */
3499 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3501 /* First do some sanity checks */
3502 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3503 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3505 /* get the subitem structure, and create it if not there */
3506 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3507 assert (hdpaSubItems);
3509 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3512 SUBITEM_INFO *tmpSubItem;
3515 lpSubItem = (SUBITEM_INFO *)Alloc(sizeof(SUBITEM_INFO));
3516 if (!lpSubItem) return FALSE;
3517 /* we could binary search here, if need be...*/
3518 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3520 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3521 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3523 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3528 lpSubItem->iSubItem = lpLVItem->iSubItem;
3529 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3533 if (lpLVItem->mask & LVIF_IMAGE)
3534 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3536 lpSubItem->hdr.iImage = lpLVItem->iImage;
3540 if (lpLVItem->mask & LVIF_TEXT)
3541 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3543 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3552 * Sets item attributes.
3555 * [I] infoPtr : valid pointer to the listview structure
3556 * [I] lpLVItem : new item atttributes
3557 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3563 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
3565 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3566 HWND hwndSelf = infoPtr->hwndSelf;
3567 LPWSTR pszText = NULL;
3568 BOOL bResult, bChanged = FALSE;
3570 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3572 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3575 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3576 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3578 pszText = lpLVItem->pszText;
3579 ((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW);
3582 /* actually set the fields */
3583 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3585 if (lpLVItem->iSubItem)
3586 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3588 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3589 if (!IsWindow(hwndSelf))
3592 /* redraw item, if necessary */
3593 if (bChanged && !infoPtr->bIsDrawing)
3595 /* this little optimization eliminates some nasty flicker */
3596 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3597 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3598 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3600 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3605 textfreeT(lpLVItem->pszText, isW);
3606 ((LVITEMW *)lpLVItem)->pszText = pszText;
3614 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3617 * [I] infoPtr : valid pointer to the listview structure
3622 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3624 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3626 SCROLLINFO scrollInfo;
3628 scrollInfo.cbSize = sizeof(SCROLLINFO);
3629 scrollInfo.fMask = SIF_POS;
3631 if (uView == LVS_LIST)
3633 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3634 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3636 else if (uView == LVS_REPORT)
3638 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3639 nItem = scrollInfo.nPos;
3643 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3644 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3647 TRACE("nItem=%d\n", nItem);
3655 * Erases the background of the given rectangle
3658 * [I] infoPtr : valid pointer to the listview structure
3659 * [I] hdc : device context handle
3660 * [I] lprcBox : clipping rectangle
3666 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3668 if (!infoPtr->hBkBrush) return FALSE;
3670 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
3672 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3680 * [I] infoPtr : valid pointer to the listview structure
3681 * [I] hdc : device context handle
3682 * [I] nItem : item index
3683 * [I] nSubItem : subitem index
3684 * [I] pos : item position in client coordinates
3685 * [I] cdmode : custom draw mode
3691 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3693 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3694 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3695 static const WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3696 DWORD cdsubitemmode = CDRF_DODEFAULT;
3697 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3698 NMLVCUSTOMDRAW nmlvcd;
3703 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
3705 /* get information needed for drawing the item */
3706 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3707 if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3708 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3709 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3710 lvItem.iItem = nItem;
3711 lvItem.iSubItem = nSubItem;
3714 lvItem.cchTextMax = DISP_TEXT_SIZE;
3715 lvItem.pszText = szDispText;
3716 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3717 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3718 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3719 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = (LPWSTR)szCallback;
3720 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3722 /* now check if we need to update the focus rectangle */
3723 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3725 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3726 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3727 OffsetRect(&rcBox, pos.x, pos.y);
3728 OffsetRect(&rcState, pos.x, pos.y);
3729 OffsetRect(&rcIcon, pos.x, pos.y);
3730 OffsetRect(&rcLabel, pos.x, pos.y);
3731 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3732 wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcState),
3733 wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
3735 /* fill in the custom draw structure */
3736 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3738 hOldFont = GetCurrentObject(hdc, OBJ_FONT);
3739 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3740 if (cdmode & CDRF_NOTIFYITEMDRAW)
3741 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3742 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3743 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3744 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3745 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3747 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3748 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3750 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3751 prepaint_setup(infoPtr, hdc, &nmlvcd);
3753 /* in full row select, subitems, will just use main item's colors */
3754 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3755 nmlvcd.clrTextBk = CLR_NONE;
3758 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3760 UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
3763 TRACE("uStateImage=%d\n", uStateImage);
3764 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3769 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3770 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3772 TRACE("iImage=%d\n", lvItem.iImage);
3773 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3774 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3777 /* Don't bother painting item being edited */
3778 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3780 /* draw the selection background, if we're drawing the main item */
3784 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3785 rcSelect.right = rcBox.right;
3787 if (nmlvcd.clrTextBk != CLR_NONE)
3788 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3789 if(lprcFocus) *lprcFocus = rcSelect;
3792 /* figure out the text drawing flags */
3793 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3794 if (uView == LVS_ICON)
3795 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3798 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3800 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3801 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3802 default: uFormat |= DT_LEFT;
3805 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3807 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3808 else rcLabel.left += LABEL_HOR_PADDING;
3810 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3811 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3814 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3815 notify_postpaint(infoPtr, &nmlvcd);
3816 if (cdsubitemmode & CDRF_NEWFONT)
3817 SelectObject(hdc, hOldFont);
3823 * Draws listview items when in owner draw mode.
3826 * [I] infoPtr : valid pointer to the listview structure
3827 * [I] hdc : device context handle
3832 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3834 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3835 DWORD cditemmode = CDRF_DODEFAULT;
3836 NMLVCUSTOMDRAW nmlvcd;
3837 POINT Origin, Position;
3843 ZeroMemory(&dis, sizeof(dis));
3845 /* Get scroll info once before loop */
3846 LISTVIEW_GetOrigin(infoPtr, &Origin);
3848 /* iterate through the invalidated rows */
3849 while(iterator_next(i))
3851 item.iItem = i->nItem;
3853 item.mask = LVIF_PARAM | LVIF_STATE;
3854 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3855 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3857 dis.CtlType = ODT_LISTVIEW;
3859 dis.itemID = item.iItem;
3860 dis.itemAction = ODA_DRAWENTIRE;
3862 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3863 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3864 dis.hwndItem = infoPtr->hwndSelf;
3866 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3867 dis.rcItem.left = Position.x + Origin.x;
3868 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3869 dis.rcItem.top = Position.y + Origin.y;
3870 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3871 dis.itemData = item.lParam;
3873 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
3876 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3877 * structure for the rest. of the paint cycle
3879 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3880 if (cdmode & CDRF_NOTIFYITEMDRAW)
3881 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3883 if (!(cditemmode & CDRF_SKIPDEFAULT))
3885 prepaint_setup (infoPtr, hdc, &nmlvcd);
3886 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3889 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3890 notify_postpaint(infoPtr, &nmlvcd);
3896 * Draws listview items when in report display mode.
3899 * [I] infoPtr : valid pointer to the listview structure
3900 * [I] hdc : device context handle
3901 * [I] cdmode : custom draw mode
3906 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3909 RECT rcClip, rcItem;
3910 POINT Origin, Position;
3916 /* figure out what to draw */
3917 rgntype = GetClipBox(hdc, &rcClip);
3918 if (rgntype == NULLREGION) return;
3920 /* Get scroll info once before loop */
3921 LISTVIEW_GetOrigin(infoPtr, &Origin);
3923 /* narrow down the columns we need to paint */
3924 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3926 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3927 if (rcItem.right + Origin.x >= rcClip.left) break;
3929 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3931 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3932 if (rcItem.left + Origin.x < rcClip.right) break;
3934 iterator_rangeitems(&j, colRange);
3936 /* in full row select, we _have_ to draw the main item */
3937 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3940 /* iterate through the invalidated rows */
3941 while(iterator_next(i))
3943 /* iterate through the invalidated columns */
3944 while(iterator_next(&j))
3946 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3947 Position.x += Origin.x;
3948 Position.y += Origin.y;
3950 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3952 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3954 rcItem.bottom = infoPtr->nItemHeight;
3955 OffsetRect(&rcItem, Position.x, Position.y);
3956 if (!RectVisible(hdc, &rcItem)) continue;
3959 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3962 iterator_destroy(&j);
3967 * Draws listview items when in list display mode.
3970 * [I] infoPtr : valid pointer to the listview structure
3971 * [I] hdc : device context handle
3972 * [I] cdmode : custom draw mode
3977 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3979 POINT Origin, Position;
3981 /* Get scroll info once before loop */
3982 LISTVIEW_GetOrigin(infoPtr, &Origin);
3984 while(iterator_prev(i))
3986 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3987 Position.x += Origin.x;
3988 Position.y += Origin.y;
3990 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3997 * Draws listview items.
4000 * [I] infoPtr : valid pointer to the listview structure
4001 * [I] hdc : device context handle
4006 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
4008 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4009 COLORREF oldTextColor, oldClrTextBk, oldClrText;
4010 NMLVCUSTOMDRAW nmlvcd;
4017 LISTVIEW_DUMP(infoPtr);
4019 infoPtr->bIsDrawing = TRUE;
4021 /* save dc values we're gonna trash while drawing */
4022 hOldFont = SelectObject(hdc, infoPtr->hFont);
4023 oldBkMode = GetBkMode(hdc);
4024 infoPtr->clrTextBkDefault = GetBkColor(hdc);
4025 oldTextColor = GetTextColor(hdc);
4027 oldClrTextBk = infoPtr->clrTextBk;
4028 oldClrText = infoPtr->clrText;
4030 infoPtr->cditemmode = CDRF_DODEFAULT;
4032 GetClientRect(infoPtr->hwndSelf, &rcClient);
4033 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4034 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4035 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4036 prepaint_setup(infoPtr, hdc, &nmlvcd);
4038 /* Use these colors to draw the items */
4039 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4040 infoPtr->clrText = nmlvcd.clrText;
4042 /* nothing to draw */
4043 if(infoPtr->nItemCount == 0) goto enddraw;
4045 /* figure out what we need to draw */
4046 iterator_visibleitems(&i, infoPtr, hdc);
4048 /* send cache hint notification */
4049 if (infoPtr->dwStyle & LVS_OWNERDATA)
4051 RANGE range = iterator_range(&i);
4054 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4055 nmlv.iFrom = range.lower;
4056 nmlv.iTo = range.upper - 1;
4057 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4060 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4061 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4064 if (uView == LVS_REPORT)
4065 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4066 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4067 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4069 /* if we have a focus rect, draw it */
4070 if (infoPtr->bFocus)
4071 DrawFocusRect(hdc, &infoPtr->rcFocus);
4073 iterator_destroy(&i);
4076 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4077 notify_postpaint(infoPtr, &nmlvcd);
4079 infoPtr->clrTextBk = oldClrTextBk;
4080 infoPtr->clrText = oldClrText;
4082 SelectObject(hdc, hOldFont);
4083 SetBkMode(hdc, oldBkMode);
4084 SetBkColor(hdc, infoPtr->clrTextBkDefault);
4085 SetTextColor(hdc, oldTextColor);
4086 infoPtr->bIsDrawing = FALSE;
4092 * Calculates the approximate width and height of a given number of items.
4095 * [I] infoPtr : valid pointer to the listview structure
4096 * [I] nItemCount : number of items
4097 * [I] wWidth : width
4098 * [I] wHeight : height
4101 * Returns a DWORD. The width in the low word and the height in high word.
4103 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
4104 WORD wWidth, WORD wHeight)
4106 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4107 INT nItemCountPerColumn = 1;
4108 INT nColumnCount = 0;
4109 DWORD dwViewRect = 0;
4111 if (nItemCount == -1)
4112 nItemCount = infoPtr->nItemCount;
4114 if (uView == LVS_LIST)
4116 if (wHeight == 0xFFFF)
4118 /* use current height */
4119 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4122 if (wHeight < infoPtr->nItemHeight)
4123 wHeight = infoPtr->nItemHeight;
4127 if (infoPtr->nItemHeight > 0)
4129 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4130 if (nItemCountPerColumn == 0)
4131 nItemCountPerColumn = 1;
4133 if (nItemCount % nItemCountPerColumn != 0)
4134 nColumnCount = nItemCount / nItemCountPerColumn;
4136 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4140 /* Microsoft padding magic */
4141 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4142 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4144 dwViewRect = MAKELONG(wWidth, wHeight);
4146 else if (uView == LVS_REPORT)
4150 if (infoPtr->nItemCount > 0)
4152 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4153 wWidth = rcBox.right - rcBox.left;
4154 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4158 /* use current height and width */
4159 if (wHeight == 0xffff)
4160 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4161 if (wWidth == 0xffff)
4162 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4165 dwViewRect = MAKELONG(wWidth, wHeight);
4167 else if (uView == LVS_SMALLICON)
4168 FIXME("uView == LVS_SMALLICON: not implemented\n");
4169 else if (uView == LVS_ICON)
4170 FIXME("uView == LVS_ICON: not implemented\n");
4178 * Create a drag image list for the specified item.
4181 * [I] infoPtr : valid pointer to the listview structure
4182 * [I] iItem : index of item
4183 * [O] lppt : Upperr-left corner of the image
4186 * Returns a handle to the image list if successful, NULL otherwise.
4188 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4194 HBITMAP hbmp, hOldbmp;
4195 HIMAGELIST dragList = 0;
4196 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
4198 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4201 rcItem.left = LVIR_BOUNDS;
4202 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4205 lppt->x = rcItem.left;
4206 lppt->y = rcItem.top;
4208 size.cx = rcItem.right - rcItem.left;
4209 size.cy = rcItem.bottom - rcItem.top;
4211 hdcOrig = GetDC(infoPtr->hwndSelf);
4212 hdc = CreateCompatibleDC(hdcOrig);
4213 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4214 hOldbmp = SelectObject(hdc, hbmp);
4216 rcItem.left = rcItem.top = 0;
4217 rcItem.right = size.cx;
4218 rcItem.bottom = size.cy;
4219 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4222 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4224 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4225 SelectObject(hdc, hOldbmp);
4226 ImageList_Add(dragList, hbmp, 0);
4229 SelectObject(hdc, hOldbmp);
4233 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4235 TRACE("ret=%p\n", dragList);
4243 * Removes all listview items and subitems.
4246 * [I] infoPtr : valid pointer to the listview structure
4252 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4255 HDPA hdpaSubItems = NULL;
4262 /* we do it directly, to avoid notifications */
4263 ranges_clear(infoPtr->selectionRanges);
4264 infoPtr->nSelectionMark = -1;
4265 infoPtr->nFocusedItem = -1;
4266 SetRectEmpty(&infoPtr->rcFocus);
4267 /* But we are supposed to leave nHotItem as is! */
4270 /* send LVN_DELETEALLITEMS notification */
4271 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4273 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4275 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4277 /* send LVN_DELETEITEM notification, if not suppressed */
4278 if (!bSuppress) notify_deleteitem(infoPtr, i);
4279 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4281 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4282 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4284 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4285 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4288 DPA_Destroy(hdpaSubItems);
4289 DPA_DeletePtr(infoPtr->hdpaItems, i);
4291 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4292 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4293 infoPtr->nItemCount --;
4296 LISTVIEW_UpdateScroll(infoPtr);
4298 LISTVIEW_InvalidateList(infoPtr);
4305 * Scrolls, and updates the columns, when a column is changing width.
4308 * [I] infoPtr : valid pointer to the listview structure
4309 * [I] nColumn : column to scroll
4310 * [I] dx : amount of scroll, in pixels
4315 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4317 COLUMN_INFO *lpColumnInfo;
4322 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4323 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4324 rcCol = lpColumnInfo->rcHeader;
4325 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4326 rcCol.left = rcCol.right;
4328 /* ajust the other columns */
4329 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4331 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4332 lpColumnInfo->rcHeader.left += dx;
4333 lpColumnInfo->rcHeader.right += dx;
4336 /* do not update screen if not in report mode */
4337 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4339 /* if we have a focus, must first erase the focus rect */
4340 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4342 /* Need to reset the item width when inserting a new column */
4343 infoPtr->nItemWidth += dx;
4345 LISTVIEW_UpdateScroll(infoPtr);
4346 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4348 /* scroll to cover the deleted column, and invalidate for redraw */
4349 rcOld = infoPtr->rcList;
4350 rcOld.left = ptOrigin.x + rcCol.left + dx;
4351 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4353 /* we can restore focus now */
4354 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4359 * Removes a column from the listview control.
4362 * [I] infoPtr : valid pointer to the listview structure
4363 * [I] nColumn : column index
4369 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4373 TRACE("nColumn=%d\n", nColumn);
4375 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4376 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4378 /* While the MSDN specifically says that column zero should not be deleted,
4379 what actually happens is that the column itself is deleted but no items or subitems
4383 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4385 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4388 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4389 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4391 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4393 SUBITEM_INFO *lpSubItem, *lpDelItem;
4395 INT nItem, nSubItem, i;
4397 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4399 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4402 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4404 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4405 if (lpSubItem->iSubItem == nColumn)
4408 lpDelItem = lpSubItem;
4410 else if (lpSubItem->iSubItem > nColumn)
4412 lpSubItem->iSubItem--;
4416 /* if we found our subitem, zapp it */
4420 if (is_textW(lpDelItem->hdr.pszText))
4421 Free(lpDelItem->hdr.pszText);
4426 /* free dpa memory */
4427 DPA_DeletePtr(hdpaSubItems, nSubItem);
4432 /* update the other column info */
4433 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4434 LISTVIEW_InvalidateList(infoPtr);
4436 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4443 * Invalidates the listview after an item's insertion or deletion.
4446 * [I] infoPtr : valid pointer to the listview structure
4447 * [I] nItem : item index
4448 * [I] dir : -1 if deleting, 1 if inserting
4453 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4455 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4456 INT nPerCol, nItemCol, nItemRow;
4460 /* if we don't refresh, what's the point of scrolling? */
4461 if (!is_redrawing(infoPtr)) return;
4463 assert (abs(dir) == 1);
4465 /* arrange icons if autoarrange is on */
4466 if (is_autoarrange(infoPtr))
4468 BOOL arrange = TRUE;
4469 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4470 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4471 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4474 /* scrollbars need updating */
4475 LISTVIEW_UpdateScroll(infoPtr);
4477 /* figure out the item's position */
4478 if (uView == LVS_REPORT)
4479 nPerCol = infoPtr->nItemCount + 1;
4480 else if (uView == LVS_LIST)
4481 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4482 else /* LVS_ICON, or LVS_SMALLICON */
4485 nItemCol = nItem / nPerCol;
4486 nItemRow = nItem % nPerCol;
4487 LISTVIEW_GetOrigin(infoPtr, &Origin);
4489 /* move the items below up a slot */
4490 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4491 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4492 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4493 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4494 OffsetRect(&rcScroll, Origin.x, Origin.y);
4495 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
4496 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4498 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
4499 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4500 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4503 /* report has only that column, so we're done */
4504 if (uView == LVS_REPORT) return;
4506 /* now for LISTs, we have to deal with the columns to the right */
4507 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4509 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4510 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4511 OffsetRect(&rcScroll, Origin.x, Origin.y);
4512 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4513 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4514 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4519 * Removes an item from the listview control.
4522 * [I] infoPtr : valid pointer to the listview structure
4523 * [I] nItem : item index
4529 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4531 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4534 TRACE("(nItem=%d)\n", nItem);
4536 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4538 /* remove selection, and focus */
4540 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4541 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4543 /* send LVN_DELETEITEM notification. */
4544 if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
4546 /* we need to do this here, because we'll be deleting stuff */
4547 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4548 LISTVIEW_InvalidateItem(infoPtr, nItem);
4550 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4556 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4557 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4559 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4560 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4563 DPA_Destroy(hdpaSubItems);
4566 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4568 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4569 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4572 infoPtr->nItemCount--;
4573 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4575 /* now is the invalidation fun */
4576 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4583 * Callback implementation for editlabel control
4586 * [I] infoPtr : valid pointer to the listview structure
4587 * [I] pszText : modified text
4588 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4594 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4596 HWND hwndSelf = infoPtr->hwndSelf;
4597 NMLVDISPINFOW dispInfo;
4599 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4601 ZeroMemory(&dispInfo, sizeof(dispInfo));
4602 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4603 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4604 dispInfo.item.iSubItem = 0;
4605 dispInfo.item.stateMask = ~0;
4606 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4607 /* add the text from the edit in */
4608 dispInfo.item.mask |= LVIF_TEXT;
4609 dispInfo.item.pszText = pszText;
4610 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4612 /* Do we need to update the Item Text */
4613 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4614 if (!IsWindow(hwndSelf))
4616 if (!pszText) return TRUE;
4618 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4620 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nEditLabelItem);
4621 ITEM_INFO* lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
4622 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4624 LISTVIEW_InvalidateItem(infoPtr, infoPtr->nEditLabelItem);
4629 ZeroMemory(&dispInfo, sizeof(dispInfo));
4630 dispInfo.item.mask = LVIF_TEXT;
4631 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4632 dispInfo.item.iSubItem = 0;
4633 dispInfo.item.pszText = pszText;
4634 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4635 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4640 * Begin in place editing of specified list view item
4643 * [I] infoPtr : valid pointer to the listview structure
4644 * [I] nItem : item index
4645 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4651 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4653 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4654 NMLVDISPINFOW dispInfo;
4656 HWND hwndSelf = infoPtr->hwndSelf;
4658 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4660 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4661 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4663 infoPtr->nEditLabelItem = nItem;
4665 /* Is the EditBox still there, if so remove it */
4666 if(infoPtr->hwndEdit != 0)
4668 SetFocus(infoPtr->hwndSelf);
4669 infoPtr->hwndEdit = 0;
4672 LISTVIEW_SetSelection(infoPtr, nItem);
4673 LISTVIEW_SetItemFocus(infoPtr, nItem);
4674 LISTVIEW_InvalidateItem(infoPtr, nItem);
4676 rect.left = LVIR_LABEL;
4677 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4679 ZeroMemory(&dispInfo, sizeof(dispInfo));
4680 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4681 dispInfo.item.iItem = nItem;
4682 dispInfo.item.iSubItem = 0;
4683 dispInfo.item.stateMask = ~0;
4684 dispInfo.item.pszText = szDispText;
4685 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4686 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4688 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4689 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4690 if (!infoPtr->hwndEdit) return 0;
4692 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4694 if (!IsWindow(hwndSelf))
4696 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4697 infoPtr->hwndEdit = 0;
4701 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4702 SetFocus(infoPtr->hwndEdit);
4703 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4704 return infoPtr->hwndEdit;
4710 * Ensures the specified item is visible, scrolling into view if necessary.
4713 * [I] infoPtr : valid pointer to the listview structure
4714 * [I] nItem : item index
4715 * [I] bPartial : partially or entirely visible
4721 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4723 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4724 INT nScrollPosHeight = 0;
4725 INT nScrollPosWidth = 0;
4726 INT nHorzAdjust = 0;
4727 INT nVertAdjust = 0;
4730 RECT rcItem, rcTemp;
4732 rcItem.left = LVIR_BOUNDS;
4733 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4735 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4737 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4739 /* scroll left/right, but in LVS_REPORT mode */
4740 if (uView == LVS_LIST)
4741 nScrollPosWidth = infoPtr->nItemWidth;
4742 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4743 nScrollPosWidth = 1;
4745 if (rcItem.left < infoPtr->rcList.left)
4748 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4753 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4757 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4759 /* scroll up/down, but not in LVS_LIST mode */
4760 if (uView == LVS_REPORT)
4761 nScrollPosHeight = infoPtr->nItemHeight;
4762 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4763 nScrollPosHeight = 1;
4765 if (rcItem.top < infoPtr->rcList.top)
4768 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4773 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4777 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4779 if (nScrollPosWidth)
4781 INT diff = nHorzDiff / nScrollPosWidth;
4782 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4783 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4786 if (nScrollPosHeight)
4788 INT diff = nVertDiff / nScrollPosHeight;
4789 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4790 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4798 * Searches for an item with specific characteristics.
4801 * [I] hwnd : window handle
4802 * [I] nStart : base item index
4803 * [I] lpFindInfo : item information to look for
4806 * SUCCESS : index of item
4809 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4810 const LVFINDINFOW *lpFindInfo)
4812 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4813 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4814 BOOL bWrap = FALSE, bNearest = FALSE;
4815 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4816 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4817 POINT Position, Destination;
4820 if (!lpFindInfo || nItem < 0) return -1;
4823 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4825 lvItem.mask |= LVIF_TEXT;
4826 lvItem.pszText = szDispText;
4827 lvItem.cchTextMax = DISP_TEXT_SIZE;
4830 if (lpFindInfo->flags & LVFI_WRAP)
4833 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4834 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4839 LISTVIEW_GetOrigin(infoPtr, &Origin);
4840 Destination.x = lpFindInfo->pt.x - Origin.x;
4841 Destination.y = lpFindInfo->pt.y - Origin.y;
4842 switch(lpFindInfo->vkDirection)
4844 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4845 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4846 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4847 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4848 case VK_HOME: Destination.x = Destination.y = 0; break;
4849 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4850 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4852 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4853 Destination.x = rcArea.right;
4854 Destination.y = rcArea.bottom;
4856 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4860 else Destination.x = Destination.y = 0;
4862 /* if LVFI_PARAM is specified, all other flags are ignored */
4863 if (lpFindInfo->flags & LVFI_PARAM)
4865 lvItem.mask |= LVIF_PARAM;
4867 lvItem.mask &= ~LVIF_TEXT;
4871 for (; nItem < nLast; nItem++)
4873 lvItem.iItem = nItem;
4874 lvItem.iSubItem = 0;
4875 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4877 if (lvItem.mask & LVIF_PARAM)
4879 if (lpFindInfo->lParam == lvItem.lParam)
4885 if (lvItem.mask & LVIF_TEXT)
4887 if (lpFindInfo->flags & LVFI_PARTIAL)
4889 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4893 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4897 if (!bNearest) return nItem;
4899 /* This is very inefficient. To do a good job here,
4900 * we need a sorted array of (x,y) item positions */
4901 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4903 /* compute the distance^2 to the destination */
4904 xdist = Destination.x - Position.x;
4905 ydist = Destination.y - Position.y;
4906 dist = xdist * xdist + ydist * ydist;
4908 /* remember the distance, and item if it's closer */
4912 nNearestItem = nItem;
4919 nLast = min(nStart + 1, infoPtr->nItemCount);
4924 return nNearestItem;
4929 * Searches for an item with specific characteristics.
4932 * [I] hwnd : window handle
4933 * [I] nStart : base item index
4934 * [I] lpFindInfo : item information to look for
4937 * SUCCESS : index of item
4940 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4941 const LVFINDINFOA *lpFindInfo)
4943 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4947 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4948 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4949 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4950 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4956 * Retrieves the background image of the listview control.
4959 * [I] infoPtr : valid pointer to the listview structure
4960 * [O] lpBkImage : background image attributes
4966 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4968 /* FIXME (listview, "empty stub!\n"); */
4974 * Retrieves column attributes.
4977 * [I] infoPtr : valid pointer to the listview structure
4978 * [I] nColumn : column index
4979 * [IO] lpColumn : column information
4980 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4981 * otherwise it is in fact a LPLVCOLUMNA
4987 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4989 COLUMN_INFO *lpColumnInfo;
4992 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4993 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4995 /* initialize memory */
4996 ZeroMemory(&hdi, sizeof(hdi));
4998 if (lpColumn->mask & LVCF_TEXT)
5000 hdi.mask |= HDI_TEXT;
5001 hdi.pszText = lpColumn->pszText;
5002 hdi.cchTextMax = lpColumn->cchTextMax;
5005 if (lpColumn->mask & LVCF_IMAGE)
5006 hdi.mask |= HDI_IMAGE;
5008 if (lpColumn->mask & LVCF_ORDER)
5009 hdi.mask |= HDI_ORDER;
5011 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
5013 if (lpColumn->mask & LVCF_FMT)
5014 lpColumn->fmt = lpColumnInfo->fmt;
5016 if (lpColumn->mask & LVCF_WIDTH)
5017 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
5019 if (lpColumn->mask & LVCF_IMAGE)
5020 lpColumn->iImage = hdi.iImage;
5022 if (lpColumn->mask & LVCF_ORDER)
5023 lpColumn->iOrder = hdi.iOrder;
5029 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5036 /* FIXME: little hack */
5037 for (i = 0; i < iCount; i++)
5045 * Retrieves the column width.
5048 * [I] infoPtr : valid pointer to the listview structure
5049 * [I] int : column index
5052 * SUCCESS : column width
5055 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
5057 INT nColumnWidth = 0;
5060 TRACE("nColumn=%d\n", nColumn);
5062 /* we have a 'column' in LIST and REPORT mode only */
5063 switch(infoPtr->dwStyle & LVS_TYPEMASK)
5066 nColumnWidth = infoPtr->nItemWidth;
5069 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
5070 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
5071 nColumnWidth = rcHeader.right - rcHeader.left;
5075 TRACE("nColumnWidth=%d\n", nColumnWidth);
5076 return nColumnWidth;
5081 * In list or report display mode, retrieves the number of items that can fit
5082 * vertically in the visible area. In icon or small icon display mode,
5083 * retrieves the total number of visible items.
5086 * [I] infoPtr : valid pointer to the listview structure
5089 * Number of fully visible items.
5091 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
5093 switch (infoPtr->dwStyle & LVS_TYPEMASK)
5097 return infoPtr->nItemCount;
5099 return LISTVIEW_GetCountPerColumn(infoPtr);
5101 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5109 * Retrieves an image list handle.
5112 * [I] infoPtr : valid pointer to the listview structure
5113 * [I] nImageList : image list identifier
5116 * SUCCESS : image list handle
5119 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
5123 case LVSIL_NORMAL: return infoPtr->himlNormal;
5124 case LVSIL_SMALL: return infoPtr->himlSmall;
5125 case LVSIL_STATE: return infoPtr->himlState;
5130 /* LISTVIEW_GetISearchString */
5134 * Retrieves item attributes.
5137 * [I] hwnd : window handle
5138 * [IO] lpLVItem : item info
5139 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5140 * if FALSE, the lpLVItem is a LPLVITEMA.
5143 * This is the internal 'GetItem' interface -- it tries to
5144 * be smart and avoid text copies, if possible, by modifying
5145 * lpLVItem->pszText to point to the text string. Please note
5146 * that this is not always possible (e.g. OWNERDATA), so on
5147 * entry you *must* supply valid values for pszText, and cchTextMax.
5148 * The only difference to the documented interface is that upon
5149 * return, you should use *only* the lpLVItem->pszText, rather than
5150 * the buffer pointer you provided on input. Most code already does
5151 * that, so it's not a problem.
5152 * For the two cases when the text must be copied (that is,
5153 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5159 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5161 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5162 NMLVDISPINFOW dispInfo;
5168 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5170 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5173 if (lpLVItem->mask == 0) return TRUE;
5175 /* make a local copy */
5176 isubitem = lpLVItem->iSubItem;
5178 /* a quick optimization if all we're asked is the focus state
5179 * these queries are worth optimising since they are common,
5180 * and can be answered in constant time, without the heavy accesses */
5181 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5182 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5184 lpLVItem->state = 0;
5185 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5186 lpLVItem->state |= LVIS_FOCUSED;
5190 ZeroMemory(&dispInfo, sizeof(dispInfo));
5192 /* if the app stores all the data, handle it separately */
5193 if (infoPtr->dwStyle & LVS_OWNERDATA)
5195 dispInfo.item.state = 0;
5197 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
5198 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5200 /* NOTE: copy only fields which we _know_ are initialized, some apps
5201 * depend on the uninitialized fields being 0 */
5202 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5203 dispInfo.item.iItem = lpLVItem->iItem;
5204 dispInfo.item.iSubItem = isubitem;
5205 if (lpLVItem->mask & LVIF_TEXT)
5207 dispInfo.item.pszText = lpLVItem->pszText;
5208 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5210 if (lpLVItem->mask & LVIF_STATE)
5211 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5212 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5213 dispInfo.item.stateMask = lpLVItem->stateMask;
5214 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5216 /* full size structure expected - _WIN32IE >= 0x560 */
5217 *lpLVItem = dispInfo.item;
5219 else if (lpLVItem->mask & LVIF_INDENT)
5221 /* indent member expected - _WIN32IE >= 0x300 */
5222 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5226 /* minimal structure expected */
5227 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5229 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5232 /* make sure lParam is zeroed out */
5233 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5235 /* we store only a little state, so if we're not asked, we're done */
5236 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5238 /* if focus is handled by us, report it */
5239 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5241 lpLVItem->state &= ~LVIS_FOCUSED;
5242 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5243 lpLVItem->state |= LVIS_FOCUSED;
5246 /* and do the same for selection, if we handle it */
5247 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5249 lpLVItem->state &= ~LVIS_SELECTED;
5250 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5251 lpLVItem->state |= LVIS_SELECTED;
5257 /* find the item and subitem structures before we proceed */
5258 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5259 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5264 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5265 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5268 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5273 pItemHdr = &lpItem->hdr;
5275 /* Do we need to query the state from the app? */
5276 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5278 dispInfo.item.mask |= LVIF_STATE;
5279 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5282 /* Do we need to enquire about the image? */
5283 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5284 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5286 dispInfo.item.mask |= LVIF_IMAGE;
5287 dispInfo.item.iImage = I_IMAGECALLBACK;
5290 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5291 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5293 dispInfo.item.mask |= LVIF_TEXT;
5294 dispInfo.item.pszText = lpLVItem->pszText;
5295 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5296 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5297 *dispInfo.item.pszText = '\0';
5300 /* If we don't have all the requested info, query the application */
5301 if (dispInfo.item.mask != 0)
5303 dispInfo.item.iItem = lpLVItem->iItem;
5304 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5305 dispInfo.item.lParam = lpItem->lParam;
5306 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5307 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5310 /* we should not store values for subitems */
5311 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5313 /* Now, handle the iImage field */
5314 if (dispInfo.item.mask & LVIF_IMAGE)
5316 lpLVItem->iImage = dispInfo.item.iImage;
5317 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5318 pItemHdr->iImage = dispInfo.item.iImage;
5320 else if (lpLVItem->mask & LVIF_IMAGE)
5322 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5323 lpLVItem->iImage = pItemHdr->iImage;
5325 lpLVItem->iImage = 0;
5328 /* The pszText field */
5329 if (dispInfo.item.mask & LVIF_TEXT)
5331 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5332 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5334 lpLVItem->pszText = dispInfo.item.pszText;
5336 else if (lpLVItem->mask & LVIF_TEXT)
5338 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5339 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5342 /* if this is a subitem, we're done */
5343 if (isubitem) return TRUE;
5345 /* Next is the lParam field */
5346 if (dispInfo.item.mask & LVIF_PARAM)
5348 lpLVItem->lParam = dispInfo.item.lParam;
5349 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5350 lpItem->lParam = dispInfo.item.lParam;
5352 else if (lpLVItem->mask & LVIF_PARAM)
5353 lpLVItem->lParam = lpItem->lParam;
5355 /* ... the state field (this one is different due to uCallbackmask) */
5356 if (lpLVItem->mask & LVIF_STATE)
5358 lpLVItem->state = lpItem->state;
5359 if (dispInfo.item.mask & LVIF_STATE)
5361 lpLVItem->state &= ~dispInfo.item.stateMask;
5362 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5364 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5366 lpLVItem->state &= ~LVIS_FOCUSED;
5367 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5368 lpLVItem->state |= LVIS_FOCUSED;
5370 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5372 lpLVItem->state &= ~LVIS_SELECTED;
5373 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5374 lpLVItem->state |= LVIS_SELECTED;
5378 /* and last, but not least, the indent field */
5379 if (lpLVItem->mask & LVIF_INDENT)
5380 lpLVItem->iIndent = lpItem->iIndent;
5387 * Retrieves item attributes.
5390 * [I] hwnd : window handle
5391 * [IO] lpLVItem : item info
5392 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5393 * if FALSE, the lpLVItem is a LPLVITEMA.
5396 * This is the external 'GetItem' interface -- it properly copies
5397 * the text in the provided buffer.
5403 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5408 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5411 pszText = lpLVItem->pszText;
5412 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5413 if (bResult && lpLVItem->pszText != pszText)
5414 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5415 lpLVItem->pszText = pszText;
5423 * Retrieves the position (upper-left) of the listview control item.
5424 * Note that for LVS_ICON style, the upper-left is that of the icon
5425 * and not the bounding box.
5428 * [I] infoPtr : valid pointer to the listview structure
5429 * [I] nItem : item index
5430 * [O] lpptPosition : coordinate information
5436 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5438 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5441 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5443 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5445 LISTVIEW_GetOrigin(infoPtr, &Origin);
5446 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5448 if (uView == LVS_ICON)
5450 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5451 lpptPosition->y += ICON_TOP_PADDING;
5453 lpptPosition->x += Origin.x;
5454 lpptPosition->y += Origin.y;
5456 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
5463 * Retrieves the bounding rectangle for a listview control item.
5466 * [I] infoPtr : valid pointer to the listview structure
5467 * [I] nItem : item index
5468 * [IO] lprc : bounding rectangle coordinates
5469 * lprc->left specifies the portion of the item for which the bounding
5470 * rectangle will be retrieved.
5472 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5473 * including the icon and label.
5476 * * Experiment shows that native control returns:
5477 * * width = min (48, length of text line)
5478 * * .left = position.x - (width - iconsize.cx)/2
5479 * * .right = .left + width
5480 * * height = #lines of text * ntmHeight + icon height + 8
5481 * * .top = position.y - 2
5482 * * .bottom = .top + height
5483 * * separation between items .y = itemSpacing.cy - height
5484 * * .x = itemSpacing.cx - width
5485 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5488 * * Experiment shows that native control returns:
5489 * * width = iconSize.cx + 16
5490 * * .left = position.x - (width - iconsize.cx)/2
5491 * * .right = .left + width
5492 * * height = iconSize.cy + 4
5493 * * .top = position.y - 2
5494 * * .bottom = .top + height
5495 * * separation between items .y = itemSpacing.cy - height
5496 * * .x = itemSpacing.cx - width
5497 * LVIR_LABEL Returns the bounding rectangle of the item text.
5500 * * Experiment shows that native control returns:
5501 * * width = text length
5502 * * .left = position.x - width/2
5503 * * .right = .left + width
5504 * * height = ntmH * linecount + 2
5505 * * .top = position.y + iconSize.cy + 6
5506 * * .bottom = .top + height
5507 * * separation between items .y = itemSpacing.cy - height
5508 * * .x = itemSpacing.cx - width
5509 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5510 * rectangles, but excludes columns in report view.
5517 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5518 * upon whether the window has the focus currently and on whether the item
5519 * is the one with the focus. Ensure that the control's record of which
5520 * item has the focus agrees with the items' records.
5522 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5524 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5525 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5526 BOOL doLabel = TRUE, oversizedBox = FALSE;
5527 POINT Position, Origin;
5531 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5533 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5535 LISTVIEW_GetOrigin(infoPtr, &Origin);
5536 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5538 /* Be smart and try to figure out the minimum we have to do */
5539 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5540 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5541 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5542 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5543 oversizedBox = TRUE;
5545 /* get what we need from the item before hand, so we make
5546 * only one request. This can speed up things, if data
5547 * is stored on the app side */
5549 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5550 if (doLabel) lvItem.mask |= LVIF_TEXT;
5551 lvItem.iItem = nItem;
5552 lvItem.iSubItem = 0;
5553 lvItem.pszText = szDispText;
5554 lvItem.cchTextMax = DISP_TEXT_SIZE;
5555 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5556 /* we got the state already up, simulate it here, to avoid a reget */
5557 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5559 lvItem.mask |= LVIF_STATE;
5560 lvItem.stateMask = LVIS_FOCUSED;
5561 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5564 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5565 lprc->left = LVIR_BOUNDS;
5569 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5573 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5577 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5580 case LVIR_SELECTBOUNDS:
5581 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5582 UnionRect(lprc, lprc, &label_rect);
5586 WARN("Unknown value: %ld\n", lprc->left);
5590 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5592 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
5599 * Retrieves the spacing between listview control items.
5602 * [I] infoPtr : valid pointer to the listview structure
5603 * [IO] lprc : rectangle to receive the output
5604 * on input, lprc->top = nSubItem
5605 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5607 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5608 * not only those of the first column.
5609 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5615 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5619 INT nColumn = lprc->top;
5621 if (!lprc) return FALSE;
5623 TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top);
5624 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5626 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5628 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5630 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5632 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5635 lvItem.iItem = nItem;
5636 lvItem.iSubItem = nColumn;
5638 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5642 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5647 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5651 ERR("Unknown bounds=%ld\n", lprc->left);
5655 OffsetRect(lprc, Position.x, Position.y);
5662 * Retrieves the width of a label.
5665 * [I] infoPtr : valid pointer to the listview structure
5668 * SUCCESS : string width (in pixels)
5671 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5673 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5676 TRACE("(nItem=%d)\n", nItem);
5678 lvItem.mask = LVIF_TEXT;
5679 lvItem.iItem = nItem;
5680 lvItem.iSubItem = 0;
5681 lvItem.pszText = szDispText;
5682 lvItem.cchTextMax = DISP_TEXT_SIZE;
5683 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5685 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5690 * Retrieves the spacing between listview control items.
5693 * [I] infoPtr : valid pointer to the listview structure
5694 * [I] bSmall : flag for small or large icon
5697 * Horizontal + vertical spacing
5699 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5705 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5709 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5710 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5712 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5719 * Retrieves the state of a listview control item.
5722 * [I] infoPtr : valid pointer to the listview structure
5723 * [I] nItem : item index
5724 * [I] uMask : state mask
5727 * State specified by the mask.
5729 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5733 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5735 lvItem.iItem = nItem;
5736 lvItem.iSubItem = 0;
5737 lvItem.mask = LVIF_STATE;
5738 lvItem.stateMask = uMask;
5739 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5741 return lvItem.state & uMask;
5746 * Retrieves the text of a listview control item or subitem.
5749 * [I] hwnd : window handle
5750 * [I] nItem : item index
5751 * [IO] lpLVItem : item information
5752 * [I] isW : TRUE if lpLVItem is Unicode
5755 * SUCCESS : string length
5758 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5760 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5762 lpLVItem->mask = LVIF_TEXT;
5763 lpLVItem->iItem = nItem;
5764 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5766 return textlenT(lpLVItem->pszText, isW);
5771 * Searches for an item based on properties + relationships.
5774 * [I] infoPtr : valid pointer to the listview structure
5775 * [I] nItem : item index
5776 * [I] uFlags : relationship flag
5779 * SUCCESS : item index
5782 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5784 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5786 LVFINDINFOW lvFindInfo;
5787 INT nCountPerColumn;
5791 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5792 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5794 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5796 if (uFlags & LVNI_CUT)
5799 if (uFlags & LVNI_DROPHILITED)
5800 uMask |= LVIS_DROPHILITED;
5802 if (uFlags & LVNI_FOCUSED)
5803 uMask |= LVIS_FOCUSED;
5805 if (uFlags & LVNI_SELECTED)
5806 uMask |= LVIS_SELECTED;
5808 /* if we're asked for the focused item, that's only one,
5809 * so it's worth optimizing */
5810 if (uFlags & LVNI_FOCUSED)
5812 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
5813 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5816 if (uFlags & LVNI_ABOVE)
5818 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5823 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5829 /* Special case for autoarrange - move 'til the top of a list */
5830 if (is_autoarrange(infoPtr))
5832 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5833 while (nItem - nCountPerRow >= 0)
5835 nItem -= nCountPerRow;
5836 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5841 lvFindInfo.flags = LVFI_NEARESTXY;
5842 lvFindInfo.vkDirection = VK_UP;
5843 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5844 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5846 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5851 else if (uFlags & LVNI_BELOW)
5853 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5855 while (nItem < infoPtr->nItemCount)
5858 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5864 /* Special case for autoarrange - move 'til the bottom of a list */
5865 if (is_autoarrange(infoPtr))
5867 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5868 while (nItem + nCountPerRow < infoPtr->nItemCount )
5870 nItem += nCountPerRow;
5871 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5876 lvFindInfo.flags = LVFI_NEARESTXY;
5877 lvFindInfo.vkDirection = VK_DOWN;
5878 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5879 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5881 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5886 else if (uFlags & LVNI_TOLEFT)
5888 if (uView == LVS_LIST)
5890 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5891 while (nItem - nCountPerColumn >= 0)
5893 nItem -= nCountPerColumn;
5894 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5898 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5900 /* Special case for autoarrange - move 'ti the beginning of a row */
5901 if (is_autoarrange(infoPtr))
5903 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5904 while (nItem % nCountPerRow > 0)
5907 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5912 lvFindInfo.flags = LVFI_NEARESTXY;
5913 lvFindInfo.vkDirection = VK_LEFT;
5914 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5915 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5917 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5922 else if (uFlags & LVNI_TORIGHT)
5924 if (uView == LVS_LIST)
5926 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5927 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5929 nItem += nCountPerColumn;
5930 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5934 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5936 /* Special case for autoarrange - move 'til the end of a row */
5937 if (is_autoarrange(infoPtr))
5939 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5940 while (nItem % nCountPerRow < nCountPerRow - 1 )
5943 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5948 lvFindInfo.flags = LVFI_NEARESTXY;
5949 lvFindInfo.vkDirection = VK_RIGHT;
5950 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5951 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5953 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5962 /* search by index */
5963 for (i = nItem; i < infoPtr->nItemCount; i++)
5965 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5973 /* LISTVIEW_GetNumberOfWorkAreas */
5977 * Retrieves the origin coordinates when in icon or small icon display mode.
5980 * [I] infoPtr : valid pointer to the listview structure
5981 * [O] lpptOrigin : coordinate information
5986 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5988 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5989 INT nHorzPos = 0, nVertPos = 0;
5990 SCROLLINFO scrollInfo;
5992 scrollInfo.cbSize = sizeof(SCROLLINFO);
5993 scrollInfo.fMask = SIF_POS;
5995 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5996 nHorzPos = scrollInfo.nPos;
5997 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5998 nVertPos = scrollInfo.nPos;
6000 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6002 lpptOrigin->x = infoPtr->rcList.left;
6003 lpptOrigin->y = infoPtr->rcList.top;
6004 if (uView == LVS_LIST)
6005 nHorzPos *= infoPtr->nItemWidth;
6006 else if (uView == LVS_REPORT)
6007 nVertPos *= infoPtr->nItemHeight;
6009 lpptOrigin->x -= nHorzPos;
6010 lpptOrigin->y -= nVertPos;
6012 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
6017 * Retrieves the width of a string.
6020 * [I] hwnd : window handle
6021 * [I] lpszText : text string to process
6022 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6025 * SUCCESS : string width (in pixels)
6028 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6033 if (is_textT(lpszText, isW))
6035 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6036 HDC hdc = GetDC(infoPtr->hwndSelf);
6037 HFONT hOldFont = SelectObject(hdc, hFont);
6040 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6042 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6043 SelectObject(hdc, hOldFont);
6044 ReleaseDC(infoPtr->hwndSelf, hdc);
6046 return stringSize.cx;
6051 * Determines which listview item is located at the specified position.
6054 * [I] infoPtr : valid pointer to the listview structure
6055 * [IO] lpht : hit test information
6056 * [I] subitem : fill out iSubItem.
6057 * [I] select : return the index only if the hit selects the item
6060 * (mm 20001022): We must not allow iSubItem to be touched, for
6061 * an app might pass only a structure with space up to iItem!
6062 * (MS Office 97 does that for instance in the file open dialog)
6065 * SUCCESS : item index
6068 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6070 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6071 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6072 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6073 POINT Origin, Position, opt;
6078 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6082 if (subitem) lpht->iSubItem = 0;
6084 if (infoPtr->rcList.left > lpht->pt.x)
6085 lpht->flags |= LVHT_TOLEFT;
6086 else if (infoPtr->rcList.right < lpht->pt.x)
6087 lpht->flags |= LVHT_TORIGHT;
6089 if (infoPtr->rcList.top > lpht->pt.y)
6090 lpht->flags |= LVHT_ABOVE;
6091 else if (infoPtr->rcList.bottom < lpht->pt.y)
6092 lpht->flags |= LVHT_BELOW;
6094 TRACE("lpht->flags=0x%x\n", lpht->flags);
6095 if (lpht->flags) return -1;
6097 lpht->flags |= LVHT_NOWHERE;
6099 LISTVIEW_GetOrigin(infoPtr, &Origin);
6101 /* first deal with the large items */
6102 rcSearch.left = lpht->pt.x;
6103 rcSearch.top = lpht->pt.y;
6104 rcSearch.right = rcSearch.left + 1;
6105 rcSearch.bottom = rcSearch.top + 1;
6107 iterator_frameditems(&i, infoPtr, &rcSearch);
6108 iterator_next(&i); /* go to first item in the sequence */
6110 iterator_destroy(&i);
6112 TRACE("lpht->iItem=%d\n", iItem);
6113 if (iItem == -1) return -1;
6115 lvItem.mask = LVIF_STATE | LVIF_TEXT;
6116 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6117 lvItem.stateMask = LVIS_STATEIMAGEMASK;
6118 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6119 lvItem.iItem = iItem;
6120 lvItem.iSubItem = 0;
6121 lvItem.pszText = szDispText;
6122 lvItem.cchTextMax = DISP_TEXT_SIZE;
6123 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6124 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6126 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
6127 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6128 opt.x = lpht->pt.x - Position.x - Origin.x;
6129 opt.y = lpht->pt.y - Position.y - Origin.y;
6131 if (uView == LVS_REPORT)
6134 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6135 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6136 if (!PtInRect(&rcBounds, opt)) return -1;
6138 if (PtInRect(&rcIcon, opt))
6139 lpht->flags |= LVHT_ONITEMICON;
6140 else if (PtInRect(&rcLabel, opt))
6141 lpht->flags |= LVHT_ONITEMLABEL;
6142 else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6143 lpht->flags |= LVHT_ONITEMSTATEICON;
6144 if (lpht->flags & LVHT_ONITEM)
6145 lpht->flags &= ~LVHT_NOWHERE;
6147 TRACE("lpht->flags=0x%x\n", lpht->flags);
6148 if (uView == LVS_REPORT && subitem)
6152 rcBounds.right = rcBounds.left;
6153 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6155 rcBounds.left = rcBounds.right;
6156 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6157 if (PtInRect(&rcBounds, opt))
6165 if (select && !(uView == LVS_REPORT &&
6166 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6167 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6169 if (uView == LVS_REPORT)
6171 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6172 UnionRect(&rcBounds, &rcBounds, &rcState);
6174 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6176 return lpht->iItem = iItem;
6180 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6181 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6182 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6183 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6184 their own sort proc. when sending LVM_SORTITEMS.
6187 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6189 LVS_SORTXXX must be specified,
6190 LVS_OWNERDRAW is not set,
6191 <item>.pszText is not LPSTR_TEXTCALLBACK.
6193 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6194 are sorted based on item text..."
6196 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6198 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6199 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6200 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6202 /* if we're sorting descending, negate the return value */
6203 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6208 * Inserts a new item in the listview control.
6211 * [I] infoPtr : valid pointer to the listview structure
6212 * [I] lpLVItem : item information
6213 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6216 * SUCCESS : new item index
6219 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6221 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6226 BOOL is_sorted, has_changed;
6228 HWND hwndSelf = infoPtr->hwndSelf;
6230 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6232 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6234 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6235 if (!lpLVItem || lpLVItem->iSubItem) return -1;
6237 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6239 if (!(lpItem = (ITEM_INFO *)Alloc(sizeof(ITEM_INFO)))) return -1;
6241 /* insert item in listview control data structure */
6242 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6243 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6245 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6246 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6248 if (lpLVItem->iItem < 0 && !is_sorted) return -1;
6250 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6251 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6252 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6253 if (nItem == -1) goto fail;
6254 infoPtr->nItemCount++;
6256 /* shift indices first so they don't get tangled */
6257 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6259 /* set the item attributes */
6260 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6262 /* full size structure expected - _WIN32IE >= 0x560 */
6265 else if (lpLVItem->mask & LVIF_INDENT)
6267 /* indent member expected - _WIN32IE >= 0x300 */
6268 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6272 /* minimal structure expected */
6273 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6276 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) item.state &= ~LVIS_STATEIMAGEMASK;
6277 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6279 /* if we're sorted, sort the list, and update the index */
6282 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6283 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6284 assert(nItem != -1);
6287 /* make room for the position, if we are in the right mode */
6288 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6290 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6292 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6294 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6299 /* send LVN_INSERTITEM notification */
6300 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6302 nmlv.lParam = lpItem->lParam;
6303 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6304 if (!IsWindow(hwndSelf))
6307 /* align items (set position of each item) */
6308 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6312 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6313 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6315 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6317 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6320 /* now is the invalidation fun */
6321 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6325 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6326 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6327 infoPtr->nItemCount--;
6329 DPA_DeletePtr(hdpaSubItems, 0);
6330 DPA_Destroy (hdpaSubItems);
6337 * Redraws a range of items.
6340 * [I] infoPtr : valid pointer to the listview structure
6341 * [I] nFirst : first item
6342 * [I] nLast : last item
6348 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6352 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6353 max(nFirst, nLast) >= infoPtr->nItemCount)
6356 for (i = nFirst; i <= nLast; i++)
6357 LISTVIEW_InvalidateItem(infoPtr, i);
6364 * Scroll the content of a listview.
6367 * [I] infoPtr : valid pointer to the listview structure
6368 * [I] dx : horizontal scroll amount in pixels
6369 * [I] dy : vertical scroll amount in pixels
6376 * If the control is in report mode (LVS_REPORT) the control can
6377 * be scrolled only in line increments. "dy" will be rounded to the
6378 * nearest number of pixels that are a whole line. Ex: if line height
6379 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6380 * is passed the the scroll will be 0. (per MSDN 7/2002)
6382 * For: (per experimentaion with native control and CSpy ListView)
6383 * LVS_ICON dy=1 = 1 pixel (vertical only)
6385 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6387 * LVS_LIST dx=1 = 1 column (horizontal only)
6388 * but will only scroll 1 column per message
6389 * no matter what the value.
6390 * dy must be 0 or FALSE returned.
6391 * LVS_REPORT dx=1 = 1 pixel
6395 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6397 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6399 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6400 dy /= infoPtr->nItemHeight;
6403 if (dy != 0) return FALSE;
6410 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6411 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6418 * Sets the background color.
6421 * [I] infoPtr : valid pointer to the listview structure
6422 * [I] clrBk : background color
6428 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6430 TRACE("(clrBk=%lx)\n", clrBk);
6432 if(infoPtr->clrBk != clrBk) {
6433 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6434 infoPtr->clrBk = clrBk;
6435 if (clrBk == CLR_NONE)
6436 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6438 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6439 LISTVIEW_InvalidateList(infoPtr);
6445 /* LISTVIEW_SetBkImage */
6447 /*** Helper for {Insert,Set}ColumnT *only* */
6448 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6450 if (lpColumn->mask & LVCF_FMT)
6452 /* format member is valid */
6453 lphdi->mask |= HDI_FORMAT;
6455 /* set text alignment (leftmost column must be left-aligned) */
6456 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6457 lphdi->fmt |= HDF_LEFT;
6458 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6459 lphdi->fmt |= HDF_RIGHT;
6460 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6461 lphdi->fmt |= HDF_CENTER;
6463 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6464 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6466 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6468 lphdi->fmt |= HDF_IMAGE;
6469 lphdi->iImage = I_IMAGECALLBACK;
6473 if (lpColumn->mask & LVCF_WIDTH)
6475 lphdi->mask |= HDI_WIDTH;
6476 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6478 /* make it fill the remainder of the controls width */
6482 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6484 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6485 lphdi->cxy += rcHeader.right - rcHeader.left;
6488 /* retrieve the layout of the header */
6489 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6490 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
6492 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6495 lphdi->cxy = lpColumn->cx;
6498 if (lpColumn->mask & LVCF_TEXT)
6500 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6501 lphdi->fmt |= HDF_STRING;
6502 lphdi->pszText = lpColumn->pszText;
6503 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6506 if (lpColumn->mask & LVCF_IMAGE)
6508 lphdi->mask |= HDI_IMAGE;
6509 lphdi->iImage = lpColumn->iImage;
6512 if (lpColumn->mask & LVCF_ORDER)
6514 lphdi->mask |= HDI_ORDER;
6515 lphdi->iOrder = lpColumn->iOrder;
6522 * Inserts a new column.
6525 * [I] infoPtr : valid pointer to the listview structure
6526 * [I] nColumn : column index
6527 * [I] lpColumn : column information
6528 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6531 * SUCCESS : new column index
6534 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6535 const LVCOLUMNW *lpColumn, BOOL isW)
6537 COLUMN_INFO *lpColumnInfo;
6541 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6543 if (!lpColumn || nColumn < 0) return -1;
6544 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6546 ZeroMemory(&hdi, sizeof(HDITEMW));
6547 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6549 /* insert item in header control */
6550 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6551 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6552 (WPARAM)nColumn, (LPARAM)&hdi);
6553 if (nNewColumn == -1) return -1;
6554 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6556 /* create our own column info */
6557 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6558 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6560 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6561 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6563 /* now we have to actually adjust the data */
6564 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6566 SUBITEM_INFO *lpSubItem;
6570 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6572 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6573 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6575 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6576 if (lpSubItem->iSubItem >= nNewColumn)
6577 lpSubItem->iSubItem++;
6582 /* make space for the new column */
6583 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6588 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6591 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6599 * Sets the attributes of a header item.
6602 * [I] infoPtr : valid pointer to the listview structure
6603 * [I] nColumn : column index
6604 * [I] lpColumn : column attributes
6605 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6611 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6612 const LVCOLUMNW *lpColumn, BOOL isW)
6614 HDITEMW hdi, hdiget;
6617 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6619 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6621 ZeroMemory(&hdi, sizeof(HDITEMW));
6622 if (lpColumn->mask & LVCF_FMT)
6624 hdi.mask |= HDI_FORMAT;
6625 hdiget.mask = HDI_FORMAT;
6626 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6627 hdi.fmt = hdiget.fmt & HDF_STRING;
6629 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6631 /* set header item attributes */
6632 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6633 if (!bResult) return FALSE;
6635 if (lpColumn->mask & LVCF_FMT)
6637 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6638 int oldFmt = lpColumnInfo->fmt;
6640 lpColumnInfo->fmt = lpColumn->fmt;
6641 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6643 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6644 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6653 * Sets the column order array
6656 * [I] infoPtr : valid pointer to the listview structure
6657 * [I] iCount : number of elements in column order array
6658 * [I] lpiArray : pointer to column order array
6664 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6666 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6677 * Sets the width of a column
6680 * [I] infoPtr : valid pointer to the listview structure
6681 * [I] nColumn : column index
6682 * [I] cx : column width
6688 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6690 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6691 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6695 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6697 /* set column width only if in report or list mode */
6698 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6700 /* take care of invalid cx values */
6701 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6702 else if (uView == LVS_LIST && cx < 1) return FALSE;
6704 /* resize all columns if in LVS_LIST mode */
6705 if(uView == LVS_LIST)
6707 infoPtr->nItemWidth = cx;
6708 LISTVIEW_InvalidateList(infoPtr);
6712 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6714 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6719 lvItem.mask = LVIF_TEXT;
6721 lvItem.iSubItem = nColumn;
6722 lvItem.pszText = szDispText;
6723 lvItem.cchTextMax = DISP_TEXT_SIZE;
6724 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6726 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6727 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6728 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6730 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6731 max_cx += infoPtr->iconSize.cx;
6732 max_cx += TRAILING_LABEL_PADDING;
6735 /* autosize based on listview items width */
6736 if(cx == LVSCW_AUTOSIZE)
6738 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6740 /* if iCol is the last column make it fill the remainder of the controls width */
6741 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
6746 LISTVIEW_GetOrigin(infoPtr, &Origin);
6747 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6749 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6753 /* Despite what the MS docs say, if this is not the last
6754 column, then MS resizes the column to the width of the
6755 largest text string in the column, including headers
6756 and items. This is different from LVSCW_AUTOSIZE in that
6757 LVSCW_AUTOSIZE ignores the header string length. */
6760 /* retrieve header text */
6761 hdi.mask = HDI_TEXT;
6762 hdi.cchTextMax = DISP_TEXT_SIZE;
6763 hdi.pszText = szDispText;
6764 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
6766 HDC hdc = GetDC(infoPtr->hwndSelf);
6767 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6770 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6771 cx = size.cx + TRAILING_HEADER_PADDING;
6772 /* FIXME: Take into account the header image, if one is present */
6773 SelectObject(hdc, old_font);
6774 ReleaseDC(infoPtr->hwndSelf, hdc);
6776 cx = max (cx, max_cx);
6780 if (cx < 0) return FALSE;
6782 /* call header to update the column change */
6783 hdi.mask = HDI_WIDTH;
6785 TRACE("hdi.cxy=%d\n", hdi.cxy);
6786 return Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6790 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
6793 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO *infoPtr)
6796 HBITMAP hbm_im, hbm_mask, hbm_orig;
6798 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6799 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6802 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6803 ILC_COLOR | ILC_MASK, 2, 2);
6804 hdc_wnd = GetDC(infoPtr->hwndSelf);
6805 hdc = CreateCompatibleDC(hdc_wnd);
6806 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6807 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6808 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6810 rc.left = rc.top = 0;
6811 rc.right = GetSystemMetrics(SM_CXSMICON);
6812 rc.bottom = GetSystemMetrics(SM_CYSMICON);
6814 hbm_orig = SelectObject(hdc, hbm_mask);
6815 FillRect(hdc, &rc, hbr_white);
6816 InflateRect(&rc, -3, -3);
6817 FillRect(hdc, &rc, hbr_black);
6819 SelectObject(hdc, hbm_im);
6820 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6821 SelectObject(hdc, hbm_orig);
6822 ImageList_Add(himl, hbm_im, hbm_mask);
6824 SelectObject(hdc, hbm_im);
6825 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6826 SelectObject(hdc, hbm_orig);
6827 ImageList_Add(himl, hbm_im, hbm_mask);
6829 DeleteObject(hbm_mask);
6830 DeleteObject(hbm_im);
6838 * Sets the extended listview style.
6841 * [I] infoPtr : valid pointer to the listview structure
6843 * [I] dwStyle : style
6846 * SUCCESS : previous style
6849 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6851 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6855 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6857 infoPtr->dwLvExStyle = dwStyle;
6859 if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES)
6861 HIMAGELIST himl = 0;
6862 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6863 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
6864 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
6872 * Sets the new hot cursor used during hot tracking and hover selection.
6875 * [I] infoPtr : valid pointer to the listview structure
6876 * [I] hCursor : the new hot cursor handle
6879 * Returns the previous hot cursor
6881 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6883 HCURSOR oldCursor = infoPtr->hHotCursor;
6885 infoPtr->hHotCursor = hCursor;
6893 * Sets the hot item index.
6896 * [I] infoPtr : valid pointer to the listview structure
6897 * [I] iIndex : index
6900 * SUCCESS : previous hot item index
6901 * FAILURE : -1 (no hot item)
6903 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6905 INT iOldIndex = infoPtr->nHotItem;
6907 infoPtr->nHotItem = iIndex;
6915 * Sets the amount of time the cursor must hover over an item before it is selected.
6918 * [I] infoPtr : valid pointer to the listview structure
6919 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6922 * Returns the previous hover time
6924 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6926 DWORD oldHoverTime = infoPtr->dwHoverTime;
6928 infoPtr->dwHoverTime = dwHoverTime;
6930 return oldHoverTime;
6935 * Sets spacing for icons of LVS_ICON style.
6938 * [I] infoPtr : valid pointer to the listview structure
6939 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6940 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6943 * MAKELONG(oldcx, oldcy)
6945 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6947 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6948 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6950 TRACE("requested=(%d,%d)\n", cx, cy);
6952 /* this is supported only for LVS_ICON style */
6953 if (uView != LVS_ICON) return oldspacing;
6955 /* set to defaults, if instructed to */
6956 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6957 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6959 /* if 0 then compute width
6960 * FIXME: Should scan each item and determine max width of
6961 * icon or label, then make that the width */
6963 cx = infoPtr->iconSpacing.cx;
6965 /* if 0 then compute height */
6967 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6968 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6971 infoPtr->iconSpacing.cx = cx;
6972 infoPtr->iconSpacing.cy = cy;
6974 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6975 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6976 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6977 infoPtr->ntmHeight);
6979 /* these depend on the iconSpacing */
6980 LISTVIEW_UpdateItemSize(infoPtr);
6985 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6989 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6996 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6997 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7006 * [I] infoPtr : valid pointer to the listview structure
7007 * [I] nType : image list type
7008 * [I] himl : image list handle
7011 * SUCCESS : old image list
7014 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7016 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7017 INT oldHeight = infoPtr->nItemHeight;
7018 HIMAGELIST himlOld = 0;
7020 TRACE("(nType=%d, himl=%p\n", nType, himl);
7025 himlOld = infoPtr->himlNormal;
7026 infoPtr->himlNormal = himl;
7027 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7028 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7032 himlOld = infoPtr->himlSmall;
7033 infoPtr->himlSmall = himl;
7034 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7038 himlOld = infoPtr->himlState;
7039 infoPtr->himlState = himl;
7040 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7041 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7045 ERR("Unknown icon type=%d\n", nType);
7049 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7050 if (infoPtr->nItemHeight != oldHeight)
7051 LISTVIEW_UpdateScroll(infoPtr);
7058 * Preallocates memory (does *not* set the actual count of items !)
7061 * [I] infoPtr : valid pointer to the listview structure
7062 * [I] nItems : item count (projected number of items to allocate)
7063 * [I] dwFlags : update flags
7069 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7071 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
7073 if (infoPtr->dwStyle & LVS_OWNERDATA)
7075 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7076 INT nOldCount = infoPtr->nItemCount;
7078 if (nItems < nOldCount)
7080 RANGE range = { nItems, nOldCount };
7081 ranges_del(infoPtr->selectionRanges, range);
7082 if (infoPtr->nFocusedItem >= nItems)
7084 infoPtr->nFocusedItem = -1;
7085 SetRectEmpty(&infoPtr->rcFocus);
7089 infoPtr->nItemCount = nItems;
7090 LISTVIEW_UpdateScroll(infoPtr);
7092 /* the flags are valid only in ownerdata report and list modes */
7093 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7095 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7096 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7098 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7099 LISTVIEW_InvalidateList(infoPtr);
7106 LISTVIEW_GetOrigin(infoPtr, &Origin);
7107 nFrom = min(nOldCount, nItems);
7108 nTo = max(nOldCount, nItems);
7110 if (uView == LVS_REPORT)
7113 rcErase.top = nFrom * infoPtr->nItemHeight;
7114 rcErase.right = infoPtr->nItemWidth;
7115 rcErase.bottom = nTo * infoPtr->nItemHeight;
7116 OffsetRect(&rcErase, Origin.x, Origin.y);
7117 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7118 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7122 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7124 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7125 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7126 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7127 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7128 OffsetRect(&rcErase, Origin.x, Origin.y);
7129 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7130 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7132 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7134 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7135 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7136 OffsetRect(&rcErase, Origin.x, Origin.y);
7137 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7138 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7144 /* According to MSDN for non-LVS_OWNERDATA this is just
7145 * a performance issue. The control allocates its internal
7146 * data structures for the number of items specified. It
7147 * cuts down on the number of memory allocations. Therefore
7148 * we will just issue a WARN here
7150 WARN("for non-ownerdata performance option not implemented.\n");
7158 * Sets the position of an item.
7161 * [I] infoPtr : valid pointer to the listview structure
7162 * [I] nItem : item index
7163 * [I] pt : coordinate
7169 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7171 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7174 TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7176 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7177 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7179 LISTVIEW_GetOrigin(infoPtr, &Origin);
7181 /* This point value seems to be an undocumented feature.
7182 * The best guess is that it means either at the origin,
7183 * or at true beginning of the list. I will assume the origin. */
7184 if ((pt.x == -1) && (pt.y == -1))
7187 if (uView == LVS_ICON)
7189 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7190 pt.y -= ICON_TOP_PADDING;
7195 infoPtr->bAutoarrange = FALSE;
7197 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7202 * Sets the state of one or many items.
7205 * [I] infoPtr : valid pointer to the listview structure
7206 * [I] nItem : item index
7207 * [I] lpLVItem : item or subitem info
7213 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7215 BOOL bResult = TRUE;
7218 lvItem.iItem = nItem;
7219 lvItem.iSubItem = 0;
7220 lvItem.mask = LVIF_STATE;
7221 lvItem.state = lpLVItem->state;
7222 lvItem.stateMask = lpLVItem->stateMask;
7223 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7227 /* apply to all items */
7228 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7229 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7232 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7235 * Update selection mark
7237 * Investigation on windows 2k showed that selection mark was updated
7238 * whenever a new selection was made, but if the selected item was
7239 * unselected it was not updated.
7241 * we are probably still not 100% accurate, but this at least sets the
7242 * proper selection mark when it is needed
7245 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7246 ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark)))
7249 infoPtr->nSelectionMark = -1;
7250 for (i = 0; i < infoPtr->nItemCount; i++)
7252 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7254 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7256 infoPtr->nSelectionMark = i;
7260 else if (ranges_contain(infoPtr->selectionRanges, i))
7262 infoPtr->nSelectionMark = i;
7273 * Sets the text of an item or subitem.
7276 * [I] hwnd : window handle
7277 * [I] nItem : item index
7278 * [I] lpLVItem : item or subitem info
7279 * [I] isW : TRUE if input is Unicode
7285 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7289 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7291 lvItem.iItem = nItem;
7292 lvItem.iSubItem = lpLVItem->iSubItem;
7293 lvItem.mask = LVIF_TEXT;
7294 lvItem.pszText = lpLVItem->pszText;
7295 lvItem.cchTextMax = lpLVItem->cchTextMax;
7297 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7299 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7304 * Set item index that marks the start of a multiple selection.
7307 * [I] infoPtr : valid pointer to the listview structure
7308 * [I] nIndex : index
7311 * Index number or -1 if there is no selection mark.
7313 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7315 INT nOldIndex = infoPtr->nSelectionMark;
7317 TRACE("(nIndex=%d)\n", nIndex);
7319 infoPtr->nSelectionMark = nIndex;
7326 * Sets the text background color.
7329 * [I] infoPtr : valid pointer to the listview structure
7330 * [I] clrTextBk : text background color
7336 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7338 TRACE("(clrTextBk=%lx)\n", clrTextBk);
7340 if (infoPtr->clrTextBk != clrTextBk)
7342 infoPtr->clrTextBk = clrTextBk;
7343 LISTVIEW_InvalidateList(infoPtr);
7351 * Sets the text foreground color.
7354 * [I] infoPtr : valid pointer to the listview structure
7355 * [I] clrText : text color
7361 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7363 TRACE("(clrText=%lx)\n", clrText);
7365 if (infoPtr->clrText != clrText)
7367 infoPtr->clrText = clrText;
7368 LISTVIEW_InvalidateList(infoPtr);
7376 * Determines which listview item is located at the specified position.
7379 * [I] infoPtr : valid pointer to the listview structure
7380 * [I] hwndNewToolTip : handle to new ToolTip
7385 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7387 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7388 infoPtr->hwndToolTip = hwndNewToolTip;
7389 return hwndOldToolTip;
7392 /* LISTVIEW_SetUnicodeFormat */
7393 /* LISTVIEW_SetWorkAreas */
7397 * Callback internally used by LISTVIEW_SortItems()
7400 * [I] first : pointer to first ITEM_INFO to compare
7401 * [I] second : pointer to second ITEM_INFO to compare
7402 * [I] lParam : HWND of control
7405 * if first comes before second : negative
7406 * if first comes after second : positive
7407 * if first and second are equivalent : zero
7409 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7411 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7412 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7413 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7415 /* Forward the call to the client defined callback */
7416 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7421 * Sorts the listview items.
7424 * [I] infoPtr : valid pointer to the listview structure
7425 * [I] pfnCompare : application-defined value
7426 * [I] lParamSort : pointer to comparision callback
7432 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7434 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7437 LPVOID selectionMarkItem;
7441 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7443 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7445 if (!pfnCompare) return FALSE;
7446 if (!infoPtr->hdpaItems) return FALSE;
7448 /* if there are 0 or 1 items, there is no need to sort */
7449 if (infoPtr->nItemCount < 2) return TRUE;
7451 if (infoPtr->nFocusedItem >= 0)
7453 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7454 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7455 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7457 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7458 /* clear the lpItem->state for non-selected ones */
7459 /* remove the selection ranges */
7461 infoPtr->pfnCompare = pfnCompare;
7462 infoPtr->lParamSort = lParamSort;
7463 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7465 /* Adjust selections and indices so that they are the way they should
7466 * be after the sort (otherwise, the list items move around, but
7467 * whatever is at the item's previous original position will be
7470 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7471 for (i=0; i < infoPtr->nItemCount; i++)
7473 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7474 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7476 if (lpItem->state & LVIS_SELECTED)
7478 item.state = LVIS_SELECTED;
7479 item.stateMask = LVIS_SELECTED;
7480 LISTVIEW_SetItemState(infoPtr, i, &item);
7482 if (lpItem->state & LVIS_FOCUSED)
7484 infoPtr->nFocusedItem = i;
7485 lpItem->state &= ~LVIS_FOCUSED;
7488 if (selectionMarkItem != NULL)
7489 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7490 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7492 /* refresh the display */
7493 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7494 LISTVIEW_InvalidateList(infoPtr);
7501 * Update theme handle after a theme change.
7504 * [I] infoPtr : valid pointer to the listview structure
7508 * FAILURE : something else
7510 static LRESULT LISTVIEW_ThemeChanged(LISTVIEW_INFO *infoPtr)
7512 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7513 CloseThemeData(theme);
7514 OpenThemeData(infoPtr->hwndSelf, themeClass);
7520 * Updates an items or rearranges the listview control.
7523 * [I] infoPtr : valid pointer to the listview structure
7524 * [I] nItem : item index
7530 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7532 TRACE("(nItem=%d)\n", nItem);
7534 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7536 /* rearrange with default alignment style */
7537 if (is_autoarrange(infoPtr))
7538 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7540 LISTVIEW_InvalidateItem(infoPtr, nItem);
7547 * Draw the track line at the place defined in the infoPtr structure.
7548 * The line is drawn with a XOR pen so drawing the line for the second time
7549 * in the same place erases the line.
7552 * [I] infoPtr : valid pointer to the listview structure
7558 static BOOL LISTVIEW_DrawTrackLine(LISTVIEW_INFO *infoPtr)
7564 if (infoPtr->xTrackLine == -1)
7567 if (!(hdc = GetDC(infoPtr->hwndSelf)))
7569 hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
7570 oldROP = SetROP2(hdc, R2_XORPEN);
7571 MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
7572 LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
7573 SetROP2(hdc, oldROP);
7574 SelectObject(hdc, hOldPen);
7575 ReleaseDC(infoPtr->hwndSelf, hdc);
7582 * Creates the listview control.
7585 * [I] hwnd : window handle
7586 * [I] lpcs : the create parameters
7592 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7594 LISTVIEW_INFO *infoPtr;
7595 UINT uView = lpcs->style & LVS_TYPEMASK;
7598 TRACE("(lpcs=%p)\n", lpcs);
7600 /* initialize info pointer */
7601 infoPtr = (LISTVIEW_INFO *)Alloc(sizeof(LISTVIEW_INFO));
7602 if (!infoPtr) return -1;
7604 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
7606 infoPtr->hwndSelf = hwnd;
7607 infoPtr->dwStyle = lpcs->style;
7608 /* determine the type of structures to use */
7609 infoPtr->hwndNotify = lpcs->hwndParent;
7610 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7611 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7613 /* initialize color information */
7614 infoPtr->clrBk = CLR_NONE;
7615 infoPtr->clrText = comctl32_color.clrWindowText;
7616 infoPtr->clrTextBk = CLR_DEFAULT;
7617 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7619 /* set default values */
7620 infoPtr->nFocusedItem = -1;
7621 infoPtr->nSelectionMark = -1;
7622 infoPtr->nHotItem = -1;
7623 infoPtr->bRedraw = TRUE;
7624 infoPtr->bNoItemMetrics = TRUE;
7625 infoPtr->bDoChangeNotify = TRUE;
7626 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7627 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7628 infoPtr->nEditLabelItem = -1;
7629 infoPtr->dwHoverTime = -1; /* default system hover time */
7630 infoPtr->nMeasureItemHeight = 0;
7631 infoPtr->xTrackLine = -1; /* no track line */
7633 /* get default font (icon title) */
7634 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7635 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7636 infoPtr->hFont = infoPtr->hDefaultFont;
7637 LISTVIEW_SaveTextMetrics(infoPtr);
7640 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7641 WS_CHILD | HDS_HORZ | HDS_FULLDRAG | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7642 0, 0, 0, 0, hwnd, NULL,
7643 lpcs->hInstance, NULL);
7644 if (!infoPtr->hwndHeader) goto fail;
7646 /* set header unicode format */
7647 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7649 /* set header font */
7650 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7652 /* allocate memory for the data structure */
7653 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7654 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7655 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7656 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7657 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7659 /* initialize the icon sizes */
7660 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7661 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7663 /* init item size to avoid division by 0 */
7664 LISTVIEW_UpdateItemSize (infoPtr);
7666 if (uView == LVS_REPORT)
7668 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7670 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7674 /* set HDS_HIDDEN flag to hide the header bar */
7675 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7676 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7680 OpenThemeData(hwnd, themeClass);
7685 DestroyWindow(infoPtr->hwndHeader);
7686 ranges_destroy(infoPtr->selectionRanges);
7687 DPA_Destroy(infoPtr->hdpaItems);
7688 DPA_Destroy(infoPtr->hdpaPosX);
7689 DPA_Destroy(infoPtr->hdpaPosY);
7690 DPA_Destroy(infoPtr->hdpaColumns);
7697 * Destroys the listview control.
7700 * [I] infoPtr : valid pointer to the listview structure
7706 static LRESULT LISTVIEW_Destroy(LISTVIEW_INFO *infoPtr)
7708 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7709 CloseThemeData(theme);
7715 * Enables the listview control.
7718 * [I] infoPtr : valid pointer to the listview structure
7719 * [I] bEnable : specifies whether to enable or disable the window
7725 static BOOL LISTVIEW_Enable(LISTVIEW_INFO *infoPtr, BOOL bEnable)
7727 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
7728 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7734 * Erases the background of the listview control.
7737 * [I] infoPtr : valid pointer to the listview structure
7738 * [I] hdc : device context handle
7744 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7748 TRACE("(hdc=%p)\n", hdc);
7750 if (!GetClipBox(hdc, &rc)) return FALSE;
7752 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7758 * Helper function for LISTVIEW_[HV]Scroll *only*.
7759 * Performs vertical/horizontal scrolling by a give amount.
7762 * [I] infoPtr : valid pointer to the listview structure
7763 * [I] dx : amount of horizontal scroll
7764 * [I] dy : amount of vertical scroll
7766 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7768 /* now we can scroll the list */
7769 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7770 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7771 /* if we have focus, adjust rect */
7772 OffsetRect(&infoPtr->rcFocus, dx, dy);
7773 UpdateWindow(infoPtr->hwndSelf);
7778 * Performs vertical scrolling.
7781 * [I] infoPtr : valid pointer to the listview structure
7782 * [I] nScrollCode : scroll code
7783 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7784 * [I] hScrollWnd : scrollbar control window handle
7790 * SB_LINEUP/SB_LINEDOWN:
7791 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7792 * for LVS_REPORT is 1 line
7793 * for LVS_LIST cannot occur
7796 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7797 INT nScrollDiff, HWND hScrollWnd)
7799 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7800 INT nOldScrollPos, nNewScrollPos;
7801 SCROLLINFO scrollInfo;
7804 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7805 debugscrollcode(nScrollCode), nScrollDiff);
7807 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7809 scrollInfo.cbSize = sizeof(SCROLLINFO);
7810 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7812 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7814 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7816 nOldScrollPos = scrollInfo.nPos;
7817 switch (nScrollCode)
7823 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7827 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7831 nScrollDiff = -scrollInfo.nPage;
7835 nScrollDiff = scrollInfo.nPage;
7838 case SB_THUMBPOSITION:
7840 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7847 /* quit right away if pos isn't changing */
7848 if (nScrollDiff == 0) return 0;
7850 /* calculate new position, and handle overflows */
7851 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7852 if (nScrollDiff > 0) {
7853 if (nNewScrollPos < nOldScrollPos ||
7854 nNewScrollPos > scrollInfo.nMax)
7855 nNewScrollPos = scrollInfo.nMax;
7857 if (nNewScrollPos > nOldScrollPos ||
7858 nNewScrollPos < scrollInfo.nMin)
7859 nNewScrollPos = scrollInfo.nMin;
7862 /* set the new position, and reread in case it changed */
7863 scrollInfo.fMask = SIF_POS;
7864 scrollInfo.nPos = nNewScrollPos;
7865 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7867 /* carry on only if it really changed */
7868 if (nNewScrollPos == nOldScrollPos) return 0;
7870 /* now adjust to client coordinates */
7871 nScrollDiff = nOldScrollPos - nNewScrollPos;
7872 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7874 /* and scroll the window */
7875 scroll_list(infoPtr, 0, nScrollDiff);
7882 * Performs horizontal scrolling.
7885 * [I] infoPtr : valid pointer to the listview structure
7886 * [I] nScrollCode : scroll code
7887 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7888 * [I] hScrollWnd : scrollbar control window handle
7894 * SB_LINELEFT/SB_LINERIGHT:
7895 * for LVS_ICON, LVS_SMALLICON 1 pixel
7896 * for LVS_REPORT is 1 pixel
7897 * for LVS_LIST is 1 column --> which is a 1 because the
7898 * scroll is based on columns not pixels
7901 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7902 INT nScrollDiff, HWND hScrollWnd)
7904 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7905 INT nOldScrollPos, nNewScrollPos;
7906 SCROLLINFO scrollInfo;
7908 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7909 debugscrollcode(nScrollCode), nScrollDiff);
7911 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7913 scrollInfo.cbSize = sizeof(SCROLLINFO);
7914 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7916 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7918 nOldScrollPos = scrollInfo.nPos;
7920 switch (nScrollCode)
7934 nScrollDiff = -scrollInfo.nPage;
7938 nScrollDiff = scrollInfo.nPage;
7941 case SB_THUMBPOSITION:
7943 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7950 /* quit right away if pos isn't changing */
7951 if (nScrollDiff == 0) return 0;
7953 /* calculate new position, and handle overflows */
7954 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7955 if (nScrollDiff > 0) {
7956 if (nNewScrollPos < nOldScrollPos ||
7957 nNewScrollPos > scrollInfo.nMax)
7958 nNewScrollPos = scrollInfo.nMax;
7960 if (nNewScrollPos > nOldScrollPos ||
7961 nNewScrollPos < scrollInfo.nMin)
7962 nNewScrollPos = scrollInfo.nMin;
7965 /* set the new position, and reread in case it changed */
7966 scrollInfo.fMask = SIF_POS;
7967 scrollInfo.nPos = nNewScrollPos;
7968 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7970 /* carry on only if it really changed */
7971 if (nNewScrollPos == nOldScrollPos) return 0;
7973 if(uView == LVS_REPORT)
7974 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7976 /* now adjust to client coordinates */
7977 nScrollDiff = nOldScrollPos - nNewScrollPos;
7978 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7980 /* and scroll the window */
7981 scroll_list(infoPtr, nScrollDiff, 0);
7986 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7988 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7989 INT gcWheelDelta = 0;
7990 INT pulScrollLines = 3;
7991 SCROLLINFO scrollInfo;
7993 TRACE("(wheelDelta=%d)\n", wheelDelta);
7995 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7996 gcWheelDelta -= wheelDelta;
7998 scrollInfo.cbSize = sizeof(SCROLLINFO);
7999 scrollInfo.fMask = SIF_POS;
8006 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8007 * should be fixed in the future.
8009 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
8010 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8014 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8016 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
8017 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8018 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
8023 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8034 * [I] infoPtr : valid pointer to the listview structure
8035 * [I] nVirtualKey : virtual key
8036 * [I] lKeyData : key data
8041 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8043 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8044 HWND hwndSelf = infoPtr->hwndSelf;
8046 NMLVKEYDOWN nmKeyDown;
8048 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
8050 /* send LVN_KEYDOWN notification */
8051 nmKeyDown.wVKey = nVirtualKey;
8052 nmKeyDown.flags = 0;
8053 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8054 if (!IsWindow(hwndSelf))
8057 switch (nVirtualKey)
8060 nItem = infoPtr->nFocusedItem;
8064 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8066 if (!notify(infoPtr, NM_RETURN)) return 0;
8067 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8072 if (infoPtr->nItemCount > 0)
8077 if (infoPtr->nItemCount > 0)
8078 nItem = infoPtr->nItemCount - 1;
8082 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8086 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8090 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8094 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8098 if (uView == LVS_REPORT)
8100 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8101 if (infoPtr->nFocusedItem == topidx)
8102 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8107 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8108 * LISTVIEW_GetCountPerRow(infoPtr);
8109 if(nItem < 0) nItem = 0;
8113 if (uView == LVS_REPORT)
8115 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8116 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8117 if (infoPtr->nFocusedItem == topidx + cnt - 1)
8118 nItem = infoPtr->nFocusedItem + cnt - 1;
8120 nItem = topidx + cnt - 1;
8123 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8124 * LISTVIEW_GetCountPerRow(infoPtr);
8125 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8129 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8130 LISTVIEW_KeySelection(infoPtr, nItem);
8140 * [I] infoPtr : valid pointer to the listview structure
8145 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8149 /* if we did not have the focus, there's nothing to do */
8150 if (!infoPtr->bFocus) return 0;
8152 /* send NM_KILLFOCUS notification */
8153 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8155 /* if we have a focus rectagle, get rid of it */
8156 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8158 /* set window focus flag */
8159 infoPtr->bFocus = FALSE;
8161 /* invalidate the selected items before reseting focus flag */
8162 LISTVIEW_InvalidateSelectedItems(infoPtr);
8169 * Processes double click messages (left mouse button).
8172 * [I] infoPtr : valid pointer to the listview structure
8173 * [I] wKey : key flag
8174 * [I] x,y : mouse coordinate
8179 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8181 LVHITTESTINFO htInfo;
8183 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8185 /* send NM_RELEASEDCAPTURE notification */
8186 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8191 /* send NM_DBLCLK notification */
8192 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8193 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8195 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8196 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8203 * Processes mouse down messages (left mouse button).
8206 * infoPtr [I ] valid pointer to the listview structure
8207 * wKey [I ] key flag
8208 * x,y [I ] mouse coordinate
8213 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8215 LVHITTESTINFO lvHitTestInfo;
8216 static BOOL bGroupSelect = TRUE;
8217 POINT pt = { x, y };
8220 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8222 /* send NM_RELEASEDCAPTURE notification */
8223 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8225 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8227 /* set left button down flag and record the click position */
8228 infoPtr->bLButtonDown = TRUE;
8229 infoPtr->ptClickPos = pt;
8231 lvHitTestInfo.pt.x = x;
8232 lvHitTestInfo.pt.y = y;
8234 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8235 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
8236 infoPtr->nEditLabelItem = -1;
8237 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8239 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8241 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
8242 if(state == 1 || state == 2)
8246 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
8247 lvitem.stateMask = LVIS_STATEIMAGEMASK;
8248 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
8253 if (infoPtr->dwStyle & LVS_SINGLESEL)
8255 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8256 infoPtr->nEditLabelItem = nItem;
8258 LISTVIEW_SetSelection(infoPtr, nItem);
8262 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8266 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8267 LISTVIEW_SetItemFocus(infoPtr, nItem);
8268 infoPtr->nSelectionMark = nItem;
8274 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8275 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8277 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8278 infoPtr->nSelectionMark = nItem;
8281 else if (wKey & MK_CONTROL)
8285 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8287 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8288 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8289 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8290 infoPtr->nSelectionMark = nItem;
8292 else if (wKey & MK_SHIFT)
8294 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8298 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8299 infoPtr->nEditLabelItem = nItem;
8301 /* set selection (clears other pre-existing selections) */
8302 LISTVIEW_SetSelection(infoPtr, nItem);
8308 /* remove all selections */
8309 LISTVIEW_DeselectAll(infoPtr);
8318 * Processes mouse up messages (left mouse button).
8321 * infoPtr [I ] valid pointer to the listview structure
8322 * wKey [I ] key flag
8323 * x,y [I ] mouse coordinate
8328 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8330 LVHITTESTINFO lvHitTestInfo;
8332 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8334 if (!infoPtr->bLButtonDown) return 0;
8336 lvHitTestInfo.pt.x = x;
8337 lvHitTestInfo.pt.y = y;
8339 /* send NM_CLICK notification */
8340 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8341 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8343 /* set left button flag */
8344 infoPtr->bLButtonDown = FALSE;
8346 /* if we clicked on a selected item, edit the label */
8347 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8348 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
8355 * Destroys the listview control (called after WM_DESTROY).
8358 * [I] infoPtr : valid pointer to the listview structure
8363 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8367 /* delete all items */
8368 LISTVIEW_DeleteAllItems(infoPtr);
8370 /* destroy data structure */
8371 DPA_Destroy(infoPtr->hdpaItems);
8372 DPA_Destroy(infoPtr->hdpaPosX);
8373 DPA_Destroy(infoPtr->hdpaPosY);
8374 DPA_Destroy(infoPtr->hdpaColumns);
8375 ranges_destroy(infoPtr->selectionRanges);
8377 /* destroy image lists */
8378 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8380 if (infoPtr->himlNormal)
8381 ImageList_Destroy(infoPtr->himlNormal);
8382 if (infoPtr->himlSmall)
8383 ImageList_Destroy(infoPtr->himlSmall);
8384 if (infoPtr->himlState)
8385 ImageList_Destroy(infoPtr->himlState);
8388 /* destroy font, bkgnd brush */
8390 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8391 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8393 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8395 /* free listview info pointer*/
8403 * Handles notifications from header.
8406 * [I] infoPtr : valid pointer to the listview structure
8407 * [I] nCtrlId : control identifier
8408 * [I] lpnmh : notification information
8413 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8415 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8416 HWND hwndSelf = infoPtr->hwndSelf;
8418 TRACE("(lpnmh=%p)\n", lpnmh);
8420 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8422 switch (lpnmh->hdr.code)
8427 COLUMN_INFO *lpColumnInfo;
8431 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8434 /* remove the old line (if any) */
8435 LISTVIEW_DrawTrackLine(infoPtr);
8437 /* compute & draw the new line */
8438 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8439 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
8440 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8441 infoPtr->xTrackLine = x + ptOrigin.x;
8442 LISTVIEW_DrawTrackLine(infoPtr);
8448 /* remove the track line (if any) */
8449 LISTVIEW_DrawTrackLine(infoPtr);
8450 infoPtr->xTrackLine = -1;
8453 case HDN_ITEMCHANGINGW:
8454 case HDN_ITEMCHANGINGA:
8455 return notify_forward_header(infoPtr, lpnmh);
8457 case HDN_ITEMCHANGEDW:
8458 case HDN_ITEMCHANGEDA:
8460 COLUMN_INFO *lpColumnInfo;
8463 notify_forward_header(infoPtr, lpnmh);
8464 if (!IsWindow(hwndSelf))
8467 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8471 hdi.mask = HDI_WIDTH;
8472 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, &hdi)) return 0;
8476 cxy = lpnmh->pitem->cxy;
8478 /* determine how much we change since the last know position */
8479 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8480 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8483 lpColumnInfo->rcHeader.right += dx;
8484 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8485 LISTVIEW_UpdateItemSize(infoPtr);
8486 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8489 RECT rcCol = lpColumnInfo->rcHeader;
8491 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8492 OffsetRect(&rcCol, ptOrigin.x, 0);
8494 rcCol.top = infoPtr->rcList.top;
8495 rcCol.bottom = infoPtr->rcList.bottom;
8497 /* resizing left-aligned columns leaves most of the left side untouched */
8498 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8500 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth + dx;
8501 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
8504 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8510 case HDN_ITEMCLICKW:
8511 case HDN_ITEMCLICKA:
8513 /* Handle sorting by Header Column */
8516 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8518 nmlv.iSubItem = lpnmh->iItem;
8519 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8523 case HDN_DIVIDERDBLCLICKW:
8524 case HDN_DIVIDERDBLCLICKA:
8525 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
8534 * Paint non-client area of control.
8537 * [I] infoPtr : valid pointer to the listview structureof the sender
8538 * [I] region : update region
8541 * TRUE - frame was painted
8542 * FALSE - call default window proc
8544 static BOOL LISTVIEW_NCPaint(LISTVIEW_INFO *infoPtr, HRGN region)
8546 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
8550 int cxEdge = GetSystemMetrics (SM_CXEDGE),
8551 cyEdge = GetSystemMetrics (SM_CYEDGE);
8553 if (!theme) return FALSE;
8555 GetWindowRect(infoPtr->hwndSelf, &r);
8557 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
8558 r.right - cxEdge, r.bottom - cyEdge);
8559 if (region != (HRGN)1)
8560 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
8561 OffsetRect(&r, -r.left, -r.top);
8563 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
8564 OffsetRect(&r, -r.left, -r.top);
8566 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
8567 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
8568 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
8569 ReleaseDC(infoPtr->hwndSelf, dc);
8571 /* Call default proc to get the scrollbars etc. painted */
8572 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
8579 * Determines the type of structure to use.
8582 * [I] infoPtr : valid pointer to the listview structureof the sender
8583 * [I] hwndFrom : listview window handle
8584 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8589 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8591 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8593 if (nCommand != NF_REQUERY) return 0;
8595 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8602 * Paints/Repaints the listview control.
8605 * [I] infoPtr : valid pointer to the listview structure
8606 * [I] hdc : device context handle
8611 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8613 TRACE("(hdc=%p)\n", hdc);
8615 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8617 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8619 infoPtr->bNoItemMetrics = FALSE;
8620 LISTVIEW_UpdateItemSize(infoPtr);
8621 if (uView == LVS_ICON || uView == LVS_SMALLICON)
8622 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8623 LISTVIEW_UpdateScroll(infoPtr);
8626 LISTVIEW_Refresh(infoPtr, hdc);
8631 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8633 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8634 LISTVIEW_Refresh(infoPtr, hdc);
8635 EndPaint(infoPtr->hwndSelf, &ps);
8644 * Paints/Repaints the listview control.
8647 * [I] infoPtr : valid pointer to the listview structure
8648 * [I] hdc : device context handle
8649 * [I] options : drawing options
8654 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
8656 FIXME("Partial Stub: (hdc=%p options=0x%08lx)\n", hdc, options);
8658 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
8661 if (options & PRF_ERASEBKGND)
8662 LISTVIEW_EraseBkgnd(infoPtr, hdc);
8664 if (options & PRF_CLIENT)
8665 LISTVIEW_Paint(infoPtr, hdc);
8673 * Processes double click messages (right mouse button).
8676 * [I] infoPtr : valid pointer to the listview structure
8677 * [I] wKey : key flag
8678 * [I] x,y : mouse coordinate
8683 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8685 LVHITTESTINFO lvHitTestInfo;
8687 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8689 /* send NM_RELEASEDCAPTURE notification */
8690 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8692 /* send NM_RDBLCLK notification */
8693 lvHitTestInfo.pt.x = x;
8694 lvHitTestInfo.pt.y = y;
8695 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8696 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8703 * Processes mouse down messages (right mouse button).
8706 * [I] infoPtr : valid pointer to the listview structure
8707 * [I] wKey : key flag
8708 * [I] x,y : mouse coordinate
8713 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8715 LVHITTESTINFO lvHitTestInfo;
8718 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8720 /* send NM_RELEASEDCAPTURE notification */
8721 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8723 /* make sure the listview control window has the focus */
8724 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8726 /* set right button down flag */
8727 infoPtr->bRButtonDown = TRUE;
8729 /* determine the index of the selected item */
8730 lvHitTestInfo.pt.x = x;
8731 lvHitTestInfo.pt.y = y;
8732 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8734 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8736 LISTVIEW_SetItemFocus(infoPtr, nItem);
8737 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8738 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8739 LISTVIEW_SetSelection(infoPtr, nItem);
8743 LISTVIEW_DeselectAll(infoPtr);
8751 * Processes mouse up messages (right mouse button).
8754 * [I] infoPtr : valid pointer to the listview structure
8755 * [I] wKey : key flag
8756 * [I] x,y : mouse coordinate
8761 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8763 LVHITTESTINFO lvHitTestInfo;
8766 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8768 if (!infoPtr->bRButtonDown) return 0;
8770 /* set button flag */
8771 infoPtr->bRButtonDown = FALSE;
8773 /* Send NM_RClICK notification */
8774 lvHitTestInfo.pt.x = x;
8775 lvHitTestInfo.pt.y = y;
8776 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8777 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
8779 /* Change to screen coordinate for WM_CONTEXTMENU */
8780 pt = lvHitTestInfo.pt;
8781 ClientToScreen(infoPtr->hwndSelf, &pt);
8783 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8784 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8785 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8796 * [I] infoPtr : valid pointer to the listview structure
8797 * [I] hwnd : window handle of window containing the cursor
8798 * [I] nHittest : hit-test code
8799 * [I] wMouseMsg : ideintifier of the mouse message
8802 * TRUE if cursor is set
8805 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8807 LVHITTESTINFO lvHitTestInfo;
8809 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8811 if(!infoPtr->hHotCursor) return FALSE;
8813 GetCursorPos(&lvHitTestInfo.pt);
8814 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8816 SetCursor(infoPtr->hHotCursor);
8826 * [I] infoPtr : valid pointer to the listview structure
8827 * [I] hwndLoseFocus : handle of previously focused window
8832 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8834 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8836 /* if we have the focus already, there's nothing to do */
8837 if (infoPtr->bFocus) return 0;
8839 /* send NM_SETFOCUS notification */
8840 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
8842 /* set window focus flag */
8843 infoPtr->bFocus = TRUE;
8845 /* put the focus rect back on */
8846 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8848 /* redraw all visible selected items */
8849 LISTVIEW_InvalidateSelectedItems(infoPtr);
8859 * [I] infoPtr : valid pointer to the listview structure
8860 * [I] fRedraw : font handle
8861 * [I] fRedraw : redraw flag
8866 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8868 HFONT oldFont = infoPtr->hFont;
8870 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8872 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8873 if (infoPtr->hFont == oldFont) return 0;
8875 LISTVIEW_SaveTextMetrics(infoPtr);
8877 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8878 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8880 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8887 * Message handling for WM_SETREDRAW.
8888 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8891 * [I] infoPtr : valid pointer to the listview structure
8892 * [I] bRedraw: state of redraw flag
8895 * DefWinProc return value
8897 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8899 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8901 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
8902 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8904 infoPtr->bRedraw = bRedraw;
8906 if(!bRedraw) return 0;
8908 if (is_autoarrange(infoPtr))
8909 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8910 LISTVIEW_UpdateScroll(infoPtr);
8912 /* despite what the WM_SETREDRAW docs says, apps expect us
8913 * to invalidate the listview here... stupid! */
8914 LISTVIEW_InvalidateList(infoPtr);
8921 * Resizes the listview control. This function processes WM_SIZE
8922 * messages. At this time, the width and height are not used.
8925 * [I] infoPtr : valid pointer to the listview structure
8926 * [I] Width : new width
8927 * [I] Height : new height
8932 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8934 RECT rcOld = infoPtr->rcList;
8936 TRACE("(width=%d, height=%d)\n", Width, Height);
8938 LISTVIEW_UpdateSize(infoPtr);
8939 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8941 /* do not bother with display related stuff if we're not redrawing */
8942 if (!is_redrawing(infoPtr)) return 0;
8944 if (is_autoarrange(infoPtr))
8945 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8947 LISTVIEW_UpdateScroll(infoPtr);
8949 /* refresh all only for lists whose height changed significantly */
8950 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
8951 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8952 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8953 LISTVIEW_InvalidateList(infoPtr);
8960 * Sets the size information.
8963 * [I] infoPtr : valid pointer to the listview structure
8968 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8970 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8972 TRACE("uView=%d, rcList(old)=%s\n", uView, wine_dbgstr_rect(&infoPtr->rcList));
8974 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
8976 if (uView == LVS_LIST)
8978 /* Apparently the "LIST" style is supposed to have the same
8979 * number of items in a column even if there is no scroll bar.
8980 * Since if a scroll bar already exists then the bottom is already
8981 * reduced, only reduce if the scroll bar does not currently exist.
8982 * The "2" is there to mimic the native control. I think it may be
8983 * related to either padding or edges. (GLA 7/2002)
8985 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
8986 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
8987 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
8989 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
8994 hl.prc = &infoPtr->rcList;
8996 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
8998 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9000 infoPtr->rcList.top = max(wp.cy, 0);
9003 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
9008 * Processes WM_STYLECHANGED messages.
9011 * [I] infoPtr : valid pointer to the listview structure
9012 * [I] wStyleType : window style type (normal or extended)
9013 * [I] lpss : window style information
9018 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9019 const STYLESTRUCT *lpss)
9021 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9022 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9024 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
9025 wStyleType, lpss->styleOld, lpss->styleNew);
9027 if (wStyleType != GWL_STYLE) return 0;
9029 /* FIXME: if LVS_NOSORTHEADER changed, update header */
9030 /* what if LVS_OWNERDATA changed? */
9031 /* or LVS_SINGLESEL */
9032 /* or LVS_SORT{AS,DES}CENDING */
9034 infoPtr->dwStyle = lpss->styleNew;
9036 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9037 ((lpss->styleNew & WS_HSCROLL) == 0))
9038 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9040 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9041 ((lpss->styleNew & WS_VSCROLL) == 0))
9042 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9044 if (uNewView != uOldView)
9046 SIZE oldIconSize = infoPtr->iconSize;
9049 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9050 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9052 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9053 SetRectEmpty(&infoPtr->rcFocus);
9055 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9056 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
9058 if (uNewView == LVS_ICON)
9060 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
9062 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
9063 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9064 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
9067 else if (uNewView == LVS_REPORT)
9072 hl.prc = &infoPtr->rcList;
9074 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9075 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9078 LISTVIEW_UpdateItemSize(infoPtr);
9081 if (uNewView == LVS_REPORT)
9082 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
9084 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9085 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9086 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9088 /* update the size of the client area */
9089 LISTVIEW_UpdateSize(infoPtr);
9091 /* add scrollbars if needed */
9092 LISTVIEW_UpdateScroll(infoPtr);
9094 /* invalidate client area + erase background */
9095 LISTVIEW_InvalidateList(infoPtr);
9102 * Window procedure of the listview control.
9105 static LRESULT WINAPI
9106 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9108 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9110 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
9112 if (!infoPtr && (uMsg != WM_CREATE))
9113 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9117 case LVM_APPROXIMATEVIEWRECT:
9118 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9119 LOWORD(lParam), HIWORD(lParam));
9121 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9123 /* case LVM_CANCELEDITLABEL: */
9125 case LVM_CREATEDRAGIMAGE:
9126 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9128 case LVM_DELETEALLITEMS:
9129 return LISTVIEW_DeleteAllItems(infoPtr);
9131 case LVM_DELETECOLUMN:
9132 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9134 case LVM_DELETEITEM:
9135 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9137 case LVM_EDITLABELW:
9138 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9140 case LVM_EDITLABELA:
9141 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9143 /* case LVM_ENABLEGROUPVIEW: */
9145 case LVM_ENSUREVISIBLE:
9146 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9149 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9152 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9154 case LVM_GETBKCOLOR:
9155 return infoPtr->clrBk;
9157 /* case LVM_GETBKIMAGE: */
9159 case LVM_GETCALLBACKMASK:
9160 return infoPtr->uCallbackMask;
9162 case LVM_GETCOLUMNA:
9163 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9165 case LVM_GETCOLUMNW:
9166 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9168 case LVM_GETCOLUMNORDERARRAY:
9169 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9171 case LVM_GETCOLUMNWIDTH:
9172 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9174 case LVM_GETCOUNTPERPAGE:
9175 return LISTVIEW_GetCountPerPage(infoPtr);
9177 case LVM_GETEDITCONTROL:
9178 return (LRESULT)infoPtr->hwndEdit;
9180 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9181 return infoPtr->dwLvExStyle;
9183 /* case LVM_GETGROUPINFO: */
9185 /* case LVM_GETGROUPMETRICS: */
9188 return (LRESULT)infoPtr->hwndHeader;
9190 case LVM_GETHOTCURSOR:
9191 return (LRESULT)infoPtr->hHotCursor;
9193 case LVM_GETHOTITEM:
9194 return infoPtr->nHotItem;
9196 case LVM_GETHOVERTIME:
9197 return infoPtr->dwHoverTime;
9199 case LVM_GETIMAGELIST:
9200 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9202 /* case LVM_GETINSERTMARK: */
9204 /* case LVM_GETINSERTMARKCOLOR: */
9206 /* case LVM_GETINSERTMARKRECT: */
9208 case LVM_GETISEARCHSTRINGA:
9209 case LVM_GETISEARCHSTRINGW:
9210 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9214 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9217 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9219 case LVM_GETITEMCOUNT:
9220 return infoPtr->nItemCount;
9222 case LVM_GETITEMPOSITION:
9223 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9225 case LVM_GETITEMRECT:
9226 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9228 case LVM_GETITEMSPACING:
9229 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9231 case LVM_GETITEMSTATE:
9232 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9234 case LVM_GETITEMTEXTA:
9235 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9237 case LVM_GETITEMTEXTW:
9238 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9240 case LVM_GETNEXTITEM:
9241 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9243 case LVM_GETNUMBEROFWORKAREAS:
9244 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9248 if (!lParam) return FALSE;
9249 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9252 /* case LVM_GETOUTLINECOLOR: */
9254 /* case LVM_GETSELECTEDCOLUMN: */
9256 case LVM_GETSELECTEDCOUNT:
9257 return LISTVIEW_GetSelectedCount(infoPtr);
9259 case LVM_GETSELECTIONMARK:
9260 return infoPtr->nSelectionMark;
9262 case LVM_GETSTRINGWIDTHA:
9263 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9265 case LVM_GETSTRINGWIDTHW:
9266 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9268 case LVM_GETSUBITEMRECT:
9269 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
9271 case LVM_GETTEXTBKCOLOR:
9272 return infoPtr->clrTextBk;
9274 case LVM_GETTEXTCOLOR:
9275 return infoPtr->clrText;
9277 /* case LVM_GETTILEINFO: */
9279 /* case LVM_GETTILEVIEWINFO: */
9281 case LVM_GETTOOLTIPS:
9282 if( !infoPtr->hwndToolTip )
9283 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9284 return (LRESULT)infoPtr->hwndToolTip;
9286 case LVM_GETTOPINDEX:
9287 return LISTVIEW_GetTopIndex(infoPtr);
9289 /*case LVM_GETUNICODEFORMAT:
9290 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9293 /* case LVM_GETVIEW: */
9295 case LVM_GETVIEWRECT:
9296 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9298 case LVM_GETWORKAREAS:
9299 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9302 /* case LVM_HASGROUP: */
9305 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9307 case LVM_INSERTCOLUMNA:
9308 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9310 case LVM_INSERTCOLUMNW:
9311 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9313 /* case LVM_INSERTGROUP: */
9315 /* case LVM_INSERTGROUPSORTED: */
9317 case LVM_INSERTITEMA:
9318 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9320 case LVM_INSERTITEMW:
9321 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9323 /* case LVM_INSERTMARKHITTEST: */
9325 /* case LVM_ISGROUPVIEWENABLED: */
9327 /* case LVM_MAPIDTOINDEX: */
9329 /* case LVM_MAPINDEXTOID: */
9331 /* case LVM_MOVEGROUP: */
9333 /* case LVM_MOVEITEMTOGROUP: */
9335 case LVM_REDRAWITEMS:
9336 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9338 /* case LVM_REMOVEALLGROUPS: */
9340 /* case LVM_REMOVEGROUP: */
9343 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9345 case LVM_SETBKCOLOR:
9346 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9348 /* case LVM_SETBKIMAGE: */
9350 case LVM_SETCALLBACKMASK:
9351 infoPtr->uCallbackMask = (UINT)wParam;
9354 case LVM_SETCOLUMNA:
9355 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9357 case LVM_SETCOLUMNW:
9358 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9360 case LVM_SETCOLUMNORDERARRAY:
9361 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9363 case LVM_SETCOLUMNWIDTH:
9364 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9366 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9367 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9369 /* case LVM_SETGROUPINFO: */
9371 /* case LVM_SETGROUPMETRICS: */
9373 case LVM_SETHOTCURSOR:
9374 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9376 case LVM_SETHOTITEM:
9377 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9379 case LVM_SETHOVERTIME:
9380 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9382 case LVM_SETICONSPACING:
9383 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9385 case LVM_SETIMAGELIST:
9386 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9388 /* case LVM_SETINFOTIP: */
9390 /* case LVM_SETINSERTMARK: */
9392 /* case LVM_SETINSERTMARKCOLOR: */
9395 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9398 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9400 case LVM_SETITEMCOUNT:
9401 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9403 case LVM_SETITEMPOSITION:
9406 pt.x = (short)LOWORD(lParam);
9407 pt.y = (short)HIWORD(lParam);
9408 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9411 case LVM_SETITEMPOSITION32:
9412 if (lParam == 0) return FALSE;
9413 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9415 case LVM_SETITEMSTATE:
9416 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9418 case LVM_SETITEMTEXTA:
9419 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9421 case LVM_SETITEMTEXTW:
9422 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9424 /* case LVM_SETOUTLINECOLOR: */
9426 /* case LVM_SETSELECTEDCOLUMN: */
9428 case LVM_SETSELECTIONMARK:
9429 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9431 case LVM_SETTEXTBKCOLOR:
9432 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9434 case LVM_SETTEXTCOLOR:
9435 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9437 /* case LVM_SETTILEINFO: */
9439 /* case LVM_SETTILEVIEWINFO: */
9441 /* case LVM_SETTILEWIDTH: */
9443 case LVM_SETTOOLTIPS:
9444 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9446 /* case LVM_SETUNICODEFORMAT: */
9448 /* case LVM_SETVIEW: */
9450 /* case LVM_SETWORKAREAS: */
9452 /* case LVM_SORTGROUPS: */
9455 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9457 /* LVM_SORTITEMSEX: */
9459 case LVM_SUBITEMHITTEST:
9460 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9463 return LISTVIEW_Update(infoPtr, (INT)wParam);
9466 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9469 return LISTVIEW_Command(infoPtr, wParam, lParam);
9472 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9475 return LISTVIEW_Destroy(infoPtr);
9478 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
9481 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9484 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9487 return (LRESULT)infoPtr->hFont;
9490 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9493 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9496 return LISTVIEW_KillFocus(infoPtr);
9498 case WM_LBUTTONDBLCLK:
9499 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9501 case WM_LBUTTONDOWN:
9502 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9505 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9508 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9511 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9514 return LISTVIEW_NCDestroy(infoPtr);
9517 if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
9522 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9523 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9526 case WM_NOTIFYFORMAT:
9527 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9529 case WM_PRINTCLIENT:
9530 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
9533 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9535 case WM_RBUTTONDBLCLK:
9536 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9538 case WM_RBUTTONDOWN:
9539 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9542 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9545 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9550 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9553 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9556 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9559 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9561 case WM_STYLECHANGED:
9562 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9564 case WM_SYSCOLORCHANGE:
9565 COMCTL32_RefreshSysColors();
9568 /* case WM_TIMER: */
9569 case WM_THEMECHANGED:
9570 return LISTVIEW_ThemeChanged(infoPtr);
9573 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9576 if (wParam & (MK_SHIFT | MK_CONTROL))
9577 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9578 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9580 case WM_WINDOWPOSCHANGED:
9581 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
9583 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9584 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9585 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9587 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
9589 MEASUREITEMSTRUCT mis;
9590 mis.CtlType = ODT_LISTVIEW;
9591 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
9595 mis.itemHeight= infoPtr->nItemHeight;
9596 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
9597 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
9598 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
9601 LISTVIEW_UpdateSize(infoPtr);
9602 LISTVIEW_UpdateScroll(infoPtr);
9604 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9606 /* case WM_WININICHANGE: */
9609 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9610 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9613 /* call default window procedure */
9614 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9621 * Registers the window class.
9629 void LISTVIEW_Register(void)
9633 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9634 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9635 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
9636 wndClass.cbClsExtra = 0;
9637 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9638 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9639 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9640 wndClass.lpszClassName = WC_LISTVIEWW;
9641 RegisterClassW(&wndClass);
9646 * Unregisters the window class.
9654 void LISTVIEW_Unregister(void)
9656 UnregisterClassW(WC_LISTVIEWW, NULL);
9661 * Handle any WM_COMMAND messages
9664 * [I] infoPtr : valid pointer to the listview structure
9665 * [I] wParam : the first message parameter
9666 * [I] lParam : the second message parameter
9671 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9673 switch (HIWORD(wParam))
9678 * Adjust the edit window size
9681 HDC hdc = GetDC(infoPtr->hwndEdit);
9682 HFONT hFont, hOldFont = 0;
9687 if (!infoPtr->hwndEdit || !hdc) return 0;
9688 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9689 GetWindowRect(infoPtr->hwndEdit, &rect);
9691 /* Select font to get the right dimension of the string */
9692 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9695 hOldFont = SelectObject(hdc, hFont);
9698 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9700 TEXTMETRICW textMetric;
9702 /* Add Extra spacing for the next character */
9703 GetTextMetricsW(hdc, &textMetric);
9704 sz.cx += (textMetric.tmMaxCharWidth * 2);
9712 rect.bottom - rect.top,
9713 SWP_DRAWFRAME|SWP_NOMOVE);
9716 SelectObject(hdc, hOldFont);
9718 ReleaseDC(infoPtr->hwndEdit, hdc);
9724 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
9733 * Subclassed edit control windproc function
9736 * [I] hwnd : the edit window handle
9737 * [I] uMsg : the message that is to be processed
9738 * [I] wParam : first message parameter
9739 * [I] lParam : second message parameter
9740 * [I] isW : TRUE if input is Unicode
9745 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9747 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
9748 BOOL cancel = FALSE;
9750 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9751 hwnd, uMsg, wParam, lParam, isW);
9756 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9763 WNDPROC editProc = infoPtr->EditWndProc;
9764 infoPtr->EditWndProc = 0;
9765 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
9766 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9770 if (VK_ESCAPE == (INT)wParam)
9775 else if (VK_RETURN == (INT)wParam)
9779 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9783 if (infoPtr->hwndEdit)
9785 LPWSTR buffer = NULL;
9787 infoPtr->hwndEdit = 0;
9790 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9794 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9796 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9797 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9801 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9803 if (buffer) Free(buffer);
9807 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9813 * Subclassed edit control Unicode windproc function
9816 * [I] hwnd : the edit window handle
9817 * [I] uMsg : the message that is to be processed
9818 * [I] wParam : first message parameter
9819 * [I] lParam : second message parameter
9823 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9825 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9830 * Subclassed edit control ANSI windproc function
9833 * [I] hwnd : the edit window handle
9834 * [I] uMsg : the message that is to be processed
9835 * [I] wParam : first message parameter
9836 * [I] lParam : second message parameter
9840 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9842 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9847 * Creates a subclassed edit cotrol
9850 * [I] infoPtr : valid pointer to the listview structure
9851 * [I] text : initial text for the edit
9852 * [I] style : the window style
9853 * [I] isW : TRUE if input is Unicode
9857 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9858 INT x, INT y, INT width, INT height, BOOL isW)
9860 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9865 TEXTMETRICW textMetric;
9866 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
9868 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9870 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
9871 hdc = GetDC(infoPtr->hwndSelf);
9873 /* Select the font to get appropriate metric dimensions */
9874 if(infoPtr->hFont != 0)
9875 hOldFont = SelectObject(hdc, infoPtr->hFont);
9877 /*Get String Length in pixels */
9878 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9880 /*Add Extra spacing for the next character */
9881 GetTextMetricsW(hdc, &textMetric);
9882 sz.cx += (textMetric.tmMaxCharWidth * 2);
9884 if(infoPtr->hFont != 0)
9885 SelectObject(hdc, hOldFont);
9887 ReleaseDC(infoPtr->hwndSelf, hdc);
9889 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9891 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9893 if (!hedit) return 0;
9895 infoPtr->EditWndProc = (WNDPROC)
9896 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
9897 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
9899 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);