4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2001 CodeWeavers Inc.
8 * Copyright 2002 Dimitrie O. Paun
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 * This code was audited for completeness against the documented features
27 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
29 * Unless otherwise noted, we believe this code to be complete, as per
30 * the specification mentioned above.
31 * If you discover missing features, or bugs, please note them below.
35 * Default Message Processing
36 * -- EN_KILLFOCUS should be handled in WM_COMMAND
37 * -- WM_CREATE: create the icon and small icon image lists at this point only if
38 * the LVS_SHAREIMAGELISTS style is not specified.
39 * -- WM_ERASEBKGND: forward this message to the parent window if the bkgnd
41 * -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
42 * or small icon and the LVS_AUTOARRANGE style is specified.
47 * -- Hot item handling, mouse hovering
48 * -- Workareas support
53 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
54 * -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
55 * -- LVA_SNAPTOGRID not implemented
56 * -- LISTVIEW_ApproximateViewRect partially implemented
57 * -- LISTVIEW_[GS]etColumnOrderArray stubs
58 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
59 * -- LISTVIEW_SetIconSpacing is incomplete
60 * -- LISTVIEW_SortItems is broken
61 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
64 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
65 * linear in the number of items in the list, and this is
66 * unacceptable for large lists.
67 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
68 * instead of inserting in the right spot
69 * -- we should keep an ordered array of coordinates in iconic mode
70 * this would allow to frame items (iterator_frameditems),
71 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
79 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
86 * -- LVS_NOSCROLL (see Q137520)
87 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
89 * -- LVS_TYPESTYLEMASK
92 * -- LVS_EX_BORDERSELECT
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_DrawEx(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3774 rcIcon.right - rcIcon.left, rcIcon.bottom - rcIcon.top, infoPtr->clrBk, CLR_DEFAULT,
3775 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3778 /* Don't bother painting item being edited */
3779 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3781 /* draw the selection background, if we're drawing the main item */
3785 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3786 rcSelect.right = rcBox.right;
3788 if (nmlvcd.clrTextBk != CLR_NONE)
3789 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3790 if(lprcFocus) *lprcFocus = rcSelect;
3793 /* figure out the text drawing flags */
3794 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3795 if (uView == LVS_ICON)
3796 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3799 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3801 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3802 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3803 default: uFormat |= DT_LEFT;
3806 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3808 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3809 else rcLabel.left += LABEL_HOR_PADDING;
3811 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3812 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3815 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3816 notify_postpaint(infoPtr, &nmlvcd);
3817 if (cdsubitemmode & CDRF_NEWFONT)
3818 SelectObject(hdc, hOldFont);
3824 * Draws listview items when in owner draw mode.
3827 * [I] infoPtr : valid pointer to the listview structure
3828 * [I] hdc : device context handle
3833 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3835 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3836 DWORD cditemmode = CDRF_DODEFAULT;
3837 NMLVCUSTOMDRAW nmlvcd;
3838 POINT Origin, Position;
3844 ZeroMemory(&dis, sizeof(dis));
3846 /* Get scroll info once before loop */
3847 LISTVIEW_GetOrigin(infoPtr, &Origin);
3849 /* iterate through the invalidated rows */
3850 while(iterator_next(i))
3852 item.iItem = i->nItem;
3854 item.mask = LVIF_PARAM | LVIF_STATE;
3855 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3856 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3858 dis.CtlType = ODT_LISTVIEW;
3860 dis.itemID = item.iItem;
3861 dis.itemAction = ODA_DRAWENTIRE;
3863 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3864 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3865 dis.hwndItem = infoPtr->hwndSelf;
3867 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3868 dis.rcItem.left = Position.x + Origin.x;
3869 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3870 dis.rcItem.top = Position.y + Origin.y;
3871 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3872 dis.itemData = item.lParam;
3874 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
3877 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3878 * structure for the rest. of the paint cycle
3880 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3881 if (cdmode & CDRF_NOTIFYITEMDRAW)
3882 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3884 if (!(cditemmode & CDRF_SKIPDEFAULT))
3886 prepaint_setup (infoPtr, hdc, &nmlvcd);
3887 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3890 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3891 notify_postpaint(infoPtr, &nmlvcd);
3897 * Draws listview items when in report display mode.
3900 * [I] infoPtr : valid pointer to the listview structure
3901 * [I] hdc : device context handle
3902 * [I] cdmode : custom draw mode
3907 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3910 RECT rcClip, rcItem;
3911 POINT Origin, Position;
3917 /* figure out what to draw */
3918 rgntype = GetClipBox(hdc, &rcClip);
3919 if (rgntype == NULLREGION) return;
3921 /* Get scroll info once before loop */
3922 LISTVIEW_GetOrigin(infoPtr, &Origin);
3924 /* narrow down the columns we need to paint */
3925 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3927 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3928 if (rcItem.right + Origin.x >= rcClip.left) break;
3930 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3932 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3933 if (rcItem.left + Origin.x < rcClip.right) break;
3935 iterator_rangeitems(&j, colRange);
3937 /* in full row select, we _have_ to draw the main item */
3938 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3941 /* iterate through the invalidated rows */
3942 while(iterator_next(i))
3944 /* iterate through the invalidated columns */
3945 while(iterator_next(&j))
3947 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3948 Position.x += Origin.x;
3949 Position.y += Origin.y;
3951 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3953 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3955 rcItem.bottom = infoPtr->nItemHeight;
3956 OffsetRect(&rcItem, Position.x, Position.y);
3957 if (!RectVisible(hdc, &rcItem)) continue;
3960 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3963 iterator_destroy(&j);
3968 * Draws listview items when in list display mode.
3971 * [I] infoPtr : valid pointer to the listview structure
3972 * [I] hdc : device context handle
3973 * [I] cdmode : custom draw mode
3978 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3980 POINT Origin, Position;
3982 /* Get scroll info once before loop */
3983 LISTVIEW_GetOrigin(infoPtr, &Origin);
3985 while(iterator_prev(i))
3987 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3988 Position.x += Origin.x;
3989 Position.y += Origin.y;
3991 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3998 * Draws listview items.
4001 * [I] infoPtr : valid pointer to the listview structure
4002 * [I] hdc : device context handle
4007 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
4009 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4010 COLORREF oldTextColor, oldClrTextBk, oldClrText;
4011 NMLVCUSTOMDRAW nmlvcd;
4018 LISTVIEW_DUMP(infoPtr);
4020 infoPtr->bIsDrawing = TRUE;
4022 /* save dc values we're gonna trash while drawing */
4023 hOldFont = SelectObject(hdc, infoPtr->hFont);
4024 oldBkMode = GetBkMode(hdc);
4025 infoPtr->clrTextBkDefault = GetBkColor(hdc);
4026 oldTextColor = GetTextColor(hdc);
4028 oldClrTextBk = infoPtr->clrTextBk;
4029 oldClrText = infoPtr->clrText;
4031 infoPtr->cditemmode = CDRF_DODEFAULT;
4033 GetClientRect(infoPtr->hwndSelf, &rcClient);
4034 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4035 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4036 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4037 prepaint_setup(infoPtr, hdc, &nmlvcd);
4039 /* Use these colors to draw the items */
4040 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4041 infoPtr->clrText = nmlvcd.clrText;
4043 /* nothing to draw */
4044 if(infoPtr->nItemCount == 0) goto enddraw;
4046 /* figure out what we need to draw */
4047 iterator_visibleitems(&i, infoPtr, hdc);
4049 /* send cache hint notification */
4050 if (infoPtr->dwStyle & LVS_OWNERDATA)
4052 RANGE range = iterator_range(&i);
4055 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4056 nmlv.iFrom = range.lower;
4057 nmlv.iTo = range.upper - 1;
4058 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4061 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4062 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4065 if (uView == LVS_REPORT)
4066 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4067 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4068 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4070 /* if we have a focus rect, draw it */
4071 if (infoPtr->bFocus)
4072 DrawFocusRect(hdc, &infoPtr->rcFocus);
4074 iterator_destroy(&i);
4077 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4078 notify_postpaint(infoPtr, &nmlvcd);
4080 infoPtr->clrTextBk = oldClrTextBk;
4081 infoPtr->clrText = oldClrText;
4083 SelectObject(hdc, hOldFont);
4084 SetBkMode(hdc, oldBkMode);
4085 SetBkColor(hdc, infoPtr->clrTextBkDefault);
4086 SetTextColor(hdc, oldTextColor);
4087 infoPtr->bIsDrawing = FALSE;
4093 * Calculates the approximate width and height of a given number of items.
4096 * [I] infoPtr : valid pointer to the listview structure
4097 * [I] nItemCount : number of items
4098 * [I] wWidth : width
4099 * [I] wHeight : height
4102 * Returns a DWORD. The width in the low word and the height in high word.
4104 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
4105 WORD wWidth, WORD wHeight)
4107 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4108 INT nItemCountPerColumn = 1;
4109 INT nColumnCount = 0;
4110 DWORD dwViewRect = 0;
4112 if (nItemCount == -1)
4113 nItemCount = infoPtr->nItemCount;
4115 if (uView == LVS_LIST)
4117 if (wHeight == 0xFFFF)
4119 /* use current height */
4120 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4123 if (wHeight < infoPtr->nItemHeight)
4124 wHeight = infoPtr->nItemHeight;
4128 if (infoPtr->nItemHeight > 0)
4130 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4131 if (nItemCountPerColumn == 0)
4132 nItemCountPerColumn = 1;
4134 if (nItemCount % nItemCountPerColumn != 0)
4135 nColumnCount = nItemCount / nItemCountPerColumn;
4137 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4141 /* Microsoft padding magic */
4142 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4143 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4145 dwViewRect = MAKELONG(wWidth, wHeight);
4147 else if (uView == LVS_REPORT)
4151 if (infoPtr->nItemCount > 0)
4153 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4154 wWidth = rcBox.right - rcBox.left;
4155 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4159 /* use current height and width */
4160 if (wHeight == 0xffff)
4161 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4162 if (wWidth == 0xffff)
4163 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4166 dwViewRect = MAKELONG(wWidth, wHeight);
4168 else if (uView == LVS_SMALLICON)
4169 FIXME("uView == LVS_SMALLICON: not implemented\n");
4170 else if (uView == LVS_ICON)
4171 FIXME("uView == LVS_ICON: not implemented\n");
4179 * Create a drag image list for the specified item.
4182 * [I] infoPtr : valid pointer to the listview structure
4183 * [I] iItem : index of item
4184 * [O] lppt : Upperr-left corner of the image
4187 * Returns a handle to the image list if successful, NULL otherwise.
4189 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4195 HBITMAP hbmp, hOldbmp;
4196 HIMAGELIST dragList = 0;
4197 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
4199 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4202 rcItem.left = LVIR_BOUNDS;
4203 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4206 lppt->x = rcItem.left;
4207 lppt->y = rcItem.top;
4209 size.cx = rcItem.right - rcItem.left;
4210 size.cy = rcItem.bottom - rcItem.top;
4212 hdcOrig = GetDC(infoPtr->hwndSelf);
4213 hdc = CreateCompatibleDC(hdcOrig);
4214 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4215 hOldbmp = SelectObject(hdc, hbmp);
4217 rcItem.left = rcItem.top = 0;
4218 rcItem.right = size.cx;
4219 rcItem.bottom = size.cy;
4220 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4223 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4225 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4226 SelectObject(hdc, hOldbmp);
4227 ImageList_Add(dragList, hbmp, 0);
4230 SelectObject(hdc, hOldbmp);
4234 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4236 TRACE("ret=%p\n", dragList);
4244 * Removes all listview items and subitems.
4247 * [I] infoPtr : valid pointer to the listview structure
4253 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4256 HDPA hdpaSubItems = NULL;
4263 /* we do it directly, to avoid notifications */
4264 ranges_clear(infoPtr->selectionRanges);
4265 infoPtr->nSelectionMark = -1;
4266 infoPtr->nFocusedItem = -1;
4267 SetRectEmpty(&infoPtr->rcFocus);
4268 /* But we are supposed to leave nHotItem as is! */
4271 /* send LVN_DELETEALLITEMS notification */
4272 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4274 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4276 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4278 /* send LVN_DELETEITEM notification, if not suppressed */
4279 if (!bSuppress) notify_deleteitem(infoPtr, i);
4280 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4282 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4283 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4285 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4286 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4289 DPA_Destroy(hdpaSubItems);
4290 DPA_DeletePtr(infoPtr->hdpaItems, i);
4292 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4293 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4294 infoPtr->nItemCount --;
4297 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4298 LISTVIEW_UpdateScroll(infoPtr);
4299 LISTVIEW_InvalidateList(infoPtr);
4306 * Scrolls, and updates the columns, when a column is changing width.
4309 * [I] infoPtr : valid pointer to the listview structure
4310 * [I] nColumn : column to scroll
4311 * [I] dx : amount of scroll, in pixels
4316 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4318 COLUMN_INFO *lpColumnInfo;
4323 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4324 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4325 rcCol = lpColumnInfo->rcHeader;
4326 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4327 rcCol.left = rcCol.right;
4329 /* ajust the other columns */
4330 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4332 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4333 lpColumnInfo->rcHeader.left += dx;
4334 lpColumnInfo->rcHeader.right += dx;
4337 /* do not update screen if not in report mode */
4338 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4340 /* if we have a focus, must first erase the focus rect */
4341 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4343 /* Need to reset the item width when inserting a new column */
4344 infoPtr->nItemWidth += dx;
4346 LISTVIEW_UpdateScroll(infoPtr);
4347 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4349 /* scroll to cover the deleted column, and invalidate for redraw */
4350 rcOld = infoPtr->rcList;
4351 rcOld.left = ptOrigin.x + rcCol.left + dx;
4352 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4354 /* we can restore focus now */
4355 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4360 * Removes a column from the listview control.
4363 * [I] infoPtr : valid pointer to the listview structure
4364 * [I] nColumn : column index
4370 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4374 TRACE("nColumn=%d\n", nColumn);
4376 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4377 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4379 /* While the MSDN specifically says that column zero should not be deleted,
4380 what actually happens is that the column itself is deleted but no items or subitems
4384 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4386 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4389 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4390 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4392 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4394 SUBITEM_INFO *lpSubItem, *lpDelItem;
4396 INT nItem, nSubItem, i;
4398 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4400 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4403 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4405 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4406 if (lpSubItem->iSubItem == nColumn)
4409 lpDelItem = lpSubItem;
4411 else if (lpSubItem->iSubItem > nColumn)
4413 lpSubItem->iSubItem--;
4417 /* if we found our subitem, zapp it */
4421 if (is_textW(lpDelItem->hdr.pszText))
4422 Free(lpDelItem->hdr.pszText);
4427 /* free dpa memory */
4428 DPA_DeletePtr(hdpaSubItems, nSubItem);
4433 /* update the other column info */
4434 LISTVIEW_UpdateItemSize(infoPtr);
4435 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4436 LISTVIEW_InvalidateList(infoPtr);
4438 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4445 * Invalidates the listview after an item's insertion or deletion.
4448 * [I] infoPtr : valid pointer to the listview structure
4449 * [I] nItem : item index
4450 * [I] dir : -1 if deleting, 1 if inserting
4455 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4457 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4458 INT nPerCol, nItemCol, nItemRow;
4462 /* if we don't refresh, what's the point of scrolling? */
4463 if (!is_redrawing(infoPtr)) return;
4465 assert (abs(dir) == 1);
4467 /* arrange icons if autoarrange is on */
4468 if (is_autoarrange(infoPtr))
4470 BOOL arrange = TRUE;
4471 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4472 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4473 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4476 /* scrollbars need updating */
4477 LISTVIEW_UpdateScroll(infoPtr);
4479 /* figure out the item's position */
4480 if (uView == LVS_REPORT)
4481 nPerCol = infoPtr->nItemCount + 1;
4482 else if (uView == LVS_LIST)
4483 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4484 else /* LVS_ICON, or LVS_SMALLICON */
4487 nItemCol = nItem / nPerCol;
4488 nItemRow = nItem % nPerCol;
4489 LISTVIEW_GetOrigin(infoPtr, &Origin);
4491 /* move the items below up a slot */
4492 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4493 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4494 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4495 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4496 OffsetRect(&rcScroll, Origin.x, Origin.y);
4497 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
4498 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4500 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
4501 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4502 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4505 /* report has only that column, so we're done */
4506 if (uView == LVS_REPORT) return;
4508 /* now for LISTs, we have to deal with the columns to the right */
4509 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4511 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4512 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4513 OffsetRect(&rcScroll, Origin.x, Origin.y);
4514 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4515 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4516 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4521 * Removes an item from the listview control.
4524 * [I] infoPtr : valid pointer to the listview structure
4525 * [I] nItem : item index
4531 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4533 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4536 TRACE("(nItem=%d)\n", nItem);
4538 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4540 /* remove selection, and focus */
4542 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4543 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4545 /* send LVN_DELETEITEM notification. */
4546 if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
4548 /* we need to do this here, because we'll be deleting stuff */
4549 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4550 LISTVIEW_InvalidateItem(infoPtr, nItem);
4552 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4558 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4559 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4561 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4562 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4565 DPA_Destroy(hdpaSubItems);
4568 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4570 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4571 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4574 infoPtr->nItemCount--;
4575 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4577 /* now is the invalidation fun */
4578 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4585 * Callback implementation for editlabel control
4588 * [I] infoPtr : valid pointer to the listview structure
4589 * [I] pszText : modified text
4590 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4596 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4598 HWND hwndSelf = infoPtr->hwndSelf;
4599 NMLVDISPINFOW dispInfo;
4601 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4603 ZeroMemory(&dispInfo, sizeof(dispInfo));
4604 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4605 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4606 dispInfo.item.iSubItem = 0;
4607 dispInfo.item.stateMask = ~0;
4608 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4609 /* add the text from the edit in */
4610 dispInfo.item.mask |= LVIF_TEXT;
4611 dispInfo.item.pszText = pszText;
4612 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4614 /* Do we need to update the Item Text */
4615 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4616 if (!IsWindow(hwndSelf))
4618 if (!pszText) return TRUE;
4620 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4622 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nEditLabelItem);
4623 ITEM_INFO* lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
4624 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4626 LISTVIEW_InvalidateItem(infoPtr, infoPtr->nEditLabelItem);
4631 ZeroMemory(&dispInfo, sizeof(dispInfo));
4632 dispInfo.item.mask = LVIF_TEXT;
4633 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4634 dispInfo.item.iSubItem = 0;
4635 dispInfo.item.pszText = pszText;
4636 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4637 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4642 * Begin in place editing of specified list view item
4645 * [I] infoPtr : valid pointer to the listview structure
4646 * [I] nItem : item index
4647 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4653 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4655 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4656 NMLVDISPINFOW dispInfo;
4658 HWND hwndSelf = infoPtr->hwndSelf;
4660 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4662 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4663 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4665 infoPtr->nEditLabelItem = nItem;
4667 /* Is the EditBox still there, if so remove it */
4668 if(infoPtr->hwndEdit != 0)
4670 SetFocus(infoPtr->hwndSelf);
4671 infoPtr->hwndEdit = 0;
4674 LISTVIEW_SetSelection(infoPtr, nItem);
4675 LISTVIEW_SetItemFocus(infoPtr, nItem);
4676 LISTVIEW_InvalidateItem(infoPtr, nItem);
4678 rect.left = LVIR_LABEL;
4679 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4681 ZeroMemory(&dispInfo, sizeof(dispInfo));
4682 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4683 dispInfo.item.iItem = nItem;
4684 dispInfo.item.iSubItem = 0;
4685 dispInfo.item.stateMask = ~0;
4686 dispInfo.item.pszText = szDispText;
4687 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4688 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4690 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4691 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4692 if (!infoPtr->hwndEdit) return 0;
4694 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4696 if (!IsWindow(hwndSelf))
4698 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4699 infoPtr->hwndEdit = 0;
4703 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4704 SetFocus(infoPtr->hwndEdit);
4705 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4706 return infoPtr->hwndEdit;
4712 * Ensures the specified item is visible, scrolling into view if necessary.
4715 * [I] infoPtr : valid pointer to the listview structure
4716 * [I] nItem : item index
4717 * [I] bPartial : partially or entirely visible
4723 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4725 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4726 INT nScrollPosHeight = 0;
4727 INT nScrollPosWidth = 0;
4728 INT nHorzAdjust = 0;
4729 INT nVertAdjust = 0;
4732 RECT rcItem, rcTemp;
4734 rcItem.left = LVIR_BOUNDS;
4735 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4737 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4739 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4741 /* scroll left/right, but in LVS_REPORT mode */
4742 if (uView == LVS_LIST)
4743 nScrollPosWidth = infoPtr->nItemWidth;
4744 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4745 nScrollPosWidth = 1;
4747 if (rcItem.left < infoPtr->rcList.left)
4750 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4755 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4759 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4761 /* scroll up/down, but not in LVS_LIST mode */
4762 if (uView == LVS_REPORT)
4763 nScrollPosHeight = infoPtr->nItemHeight;
4764 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4765 nScrollPosHeight = 1;
4767 if (rcItem.top < infoPtr->rcList.top)
4770 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4775 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4779 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4781 if (nScrollPosWidth)
4783 INT diff = nHorzDiff / nScrollPosWidth;
4784 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4785 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4788 if (nScrollPosHeight)
4790 INT diff = nVertDiff / nScrollPosHeight;
4791 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4792 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4800 * Searches for an item with specific characteristics.
4803 * [I] hwnd : window handle
4804 * [I] nStart : base item index
4805 * [I] lpFindInfo : item information to look for
4808 * SUCCESS : index of item
4811 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4812 const LVFINDINFOW *lpFindInfo)
4814 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4815 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4816 BOOL bWrap = FALSE, bNearest = FALSE;
4817 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4818 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4819 POINT Position, Destination;
4822 if (!lpFindInfo || nItem < 0) return -1;
4825 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4827 lvItem.mask |= LVIF_TEXT;
4828 lvItem.pszText = szDispText;
4829 lvItem.cchTextMax = DISP_TEXT_SIZE;
4832 if (lpFindInfo->flags & LVFI_WRAP)
4835 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4836 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4841 LISTVIEW_GetOrigin(infoPtr, &Origin);
4842 Destination.x = lpFindInfo->pt.x - Origin.x;
4843 Destination.y = lpFindInfo->pt.y - Origin.y;
4844 switch(lpFindInfo->vkDirection)
4846 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4847 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4848 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4849 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4850 case VK_HOME: Destination.x = Destination.y = 0; break;
4851 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4852 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4854 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4855 Destination.x = rcArea.right;
4856 Destination.y = rcArea.bottom;
4858 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4862 else Destination.x = Destination.y = 0;
4864 /* if LVFI_PARAM is specified, all other flags are ignored */
4865 if (lpFindInfo->flags & LVFI_PARAM)
4867 lvItem.mask |= LVIF_PARAM;
4869 lvItem.mask &= ~LVIF_TEXT;
4873 for (; nItem < nLast; nItem++)
4875 lvItem.iItem = nItem;
4876 lvItem.iSubItem = 0;
4877 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4879 if (lvItem.mask & LVIF_PARAM)
4881 if (lpFindInfo->lParam == lvItem.lParam)
4887 if (lvItem.mask & LVIF_TEXT)
4889 if (lpFindInfo->flags & LVFI_PARTIAL)
4891 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4895 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4899 if (!bNearest) return nItem;
4901 /* This is very inefficient. To do a good job here,
4902 * we need a sorted array of (x,y) item positions */
4903 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4905 /* compute the distance^2 to the destination */
4906 xdist = Destination.x - Position.x;
4907 ydist = Destination.y - Position.y;
4908 dist = xdist * xdist + ydist * ydist;
4910 /* remember the distance, and item if it's closer */
4914 nNearestItem = nItem;
4921 nLast = min(nStart + 1, infoPtr->nItemCount);
4926 return nNearestItem;
4931 * Searches for an item with specific characteristics.
4934 * [I] hwnd : window handle
4935 * [I] nStart : base item index
4936 * [I] lpFindInfo : item information to look for
4939 * SUCCESS : index of item
4942 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4943 const LVFINDINFOA *lpFindInfo)
4945 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4949 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4950 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4951 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4952 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4958 * Retrieves the background image of the listview control.
4961 * [I] infoPtr : valid pointer to the listview structure
4962 * [O] lpBkImage : background image attributes
4968 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4970 /* FIXME (listview, "empty stub!\n"); */
4976 * Retrieves column attributes.
4979 * [I] infoPtr : valid pointer to the listview structure
4980 * [I] nColumn : column index
4981 * [IO] lpColumn : column information
4982 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4983 * otherwise it is in fact a LPLVCOLUMNA
4989 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4991 COLUMN_INFO *lpColumnInfo;
4994 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4995 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4997 /* initialize memory */
4998 ZeroMemory(&hdi, sizeof(hdi));
5000 if (lpColumn->mask & LVCF_TEXT)
5002 hdi.mask |= HDI_TEXT;
5003 hdi.pszText = lpColumn->pszText;
5004 hdi.cchTextMax = lpColumn->cchTextMax;
5007 if (lpColumn->mask & LVCF_IMAGE)
5008 hdi.mask |= HDI_IMAGE;
5010 if (lpColumn->mask & LVCF_ORDER)
5011 hdi.mask |= HDI_ORDER;
5013 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
5015 if (lpColumn->mask & LVCF_FMT)
5016 lpColumn->fmt = lpColumnInfo->fmt;
5018 if (lpColumn->mask & LVCF_WIDTH)
5019 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
5021 if (lpColumn->mask & LVCF_IMAGE)
5022 lpColumn->iImage = hdi.iImage;
5024 if (lpColumn->mask & LVCF_ORDER)
5025 lpColumn->iOrder = hdi.iOrder;
5031 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5038 /* FIXME: little hack */
5039 for (i = 0; i < iCount; i++)
5047 * Retrieves the column width.
5050 * [I] infoPtr : valid pointer to the listview structure
5051 * [I] int : column index
5054 * SUCCESS : column width
5057 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
5059 INT nColumnWidth = 0;
5062 TRACE("nColumn=%d\n", nColumn);
5064 /* we have a 'column' in LIST and REPORT mode only */
5065 switch(infoPtr->dwStyle & LVS_TYPEMASK)
5068 nColumnWidth = infoPtr->nItemWidth;
5071 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
5072 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
5073 nColumnWidth = rcHeader.right - rcHeader.left;
5077 TRACE("nColumnWidth=%d\n", nColumnWidth);
5078 return nColumnWidth;
5083 * In list or report display mode, retrieves the number of items that can fit
5084 * vertically in the visible area. In icon or small icon display mode,
5085 * retrieves the total number of visible items.
5088 * [I] infoPtr : valid pointer to the listview structure
5091 * Number of fully visible items.
5093 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
5095 switch (infoPtr->dwStyle & LVS_TYPEMASK)
5099 return infoPtr->nItemCount;
5101 return LISTVIEW_GetCountPerColumn(infoPtr);
5103 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5111 * Retrieves an image list handle.
5114 * [I] infoPtr : valid pointer to the listview structure
5115 * [I] nImageList : image list identifier
5118 * SUCCESS : image list handle
5121 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
5125 case LVSIL_NORMAL: return infoPtr->himlNormal;
5126 case LVSIL_SMALL: return infoPtr->himlSmall;
5127 case LVSIL_STATE: return infoPtr->himlState;
5132 /* LISTVIEW_GetISearchString */
5136 * Retrieves item attributes.
5139 * [I] hwnd : window handle
5140 * [IO] lpLVItem : item info
5141 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5142 * if FALSE, the lpLVItem is a LPLVITEMA.
5145 * This is the internal 'GetItem' interface -- it tries to
5146 * be smart and avoid text copies, if possible, by modifying
5147 * lpLVItem->pszText to point to the text string. Please note
5148 * that this is not always possible (e.g. OWNERDATA), so on
5149 * entry you *must* supply valid values for pszText, and cchTextMax.
5150 * The only difference to the documented interface is that upon
5151 * return, you should use *only* the lpLVItem->pszText, rather than
5152 * the buffer pointer you provided on input. Most code already does
5153 * that, so it's not a problem.
5154 * For the two cases when the text must be copied (that is,
5155 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5161 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5163 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5164 NMLVDISPINFOW dispInfo;
5170 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5172 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5175 if (lpLVItem->mask == 0) return TRUE;
5177 /* make a local copy */
5178 isubitem = lpLVItem->iSubItem;
5180 /* a quick optimization if all we're asked is the focus state
5181 * these queries are worth optimising since they are common,
5182 * and can be answered in constant time, without the heavy accesses */
5183 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5184 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5186 lpLVItem->state = 0;
5187 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5188 lpLVItem->state |= LVIS_FOCUSED;
5192 ZeroMemory(&dispInfo, sizeof(dispInfo));
5194 /* if the app stores all the data, handle it separately */
5195 if (infoPtr->dwStyle & LVS_OWNERDATA)
5197 dispInfo.item.state = 0;
5199 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
5200 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5202 /* NOTE: copy only fields which we _know_ are initialized, some apps
5203 * depend on the uninitialized fields being 0 */
5204 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5205 dispInfo.item.iItem = lpLVItem->iItem;
5206 dispInfo.item.iSubItem = isubitem;
5207 if (lpLVItem->mask & LVIF_TEXT)
5209 dispInfo.item.pszText = lpLVItem->pszText;
5210 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5212 if (lpLVItem->mask & LVIF_STATE)
5213 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5214 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5215 dispInfo.item.stateMask = lpLVItem->stateMask;
5216 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5218 /* full size structure expected - _WIN32IE >= 0x560 */
5219 *lpLVItem = dispInfo.item;
5221 else if (lpLVItem->mask & LVIF_INDENT)
5223 /* indent member expected - _WIN32IE >= 0x300 */
5224 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5228 /* minimal structure expected */
5229 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5231 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5234 /* make sure lParam is zeroed out */
5235 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5237 /* we store only a little state, so if we're not asked, we're done */
5238 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5240 /* if focus is handled by us, report it */
5241 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5243 lpLVItem->state &= ~LVIS_FOCUSED;
5244 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5245 lpLVItem->state |= LVIS_FOCUSED;
5248 /* and do the same for selection, if we handle it */
5249 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5251 lpLVItem->state &= ~LVIS_SELECTED;
5252 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5253 lpLVItem->state |= LVIS_SELECTED;
5259 /* find the item and subitem structures before we proceed */
5260 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5261 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5266 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5267 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5270 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5275 pItemHdr = &lpItem->hdr;
5277 /* Do we need to query the state from the app? */
5278 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5280 dispInfo.item.mask |= LVIF_STATE;
5281 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5284 /* Do we need to enquire about the image? */
5285 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5286 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5288 dispInfo.item.mask |= LVIF_IMAGE;
5289 dispInfo.item.iImage = I_IMAGECALLBACK;
5292 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5293 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5295 dispInfo.item.mask |= LVIF_TEXT;
5296 dispInfo.item.pszText = lpLVItem->pszText;
5297 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5298 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5299 *dispInfo.item.pszText = '\0';
5302 /* If we don't have all the requested info, query the application */
5303 if (dispInfo.item.mask != 0)
5305 dispInfo.item.iItem = lpLVItem->iItem;
5306 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5307 dispInfo.item.lParam = lpItem->lParam;
5308 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5309 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5312 /* we should not store values for subitems */
5313 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5315 /* Now, handle the iImage field */
5316 if (dispInfo.item.mask & LVIF_IMAGE)
5318 lpLVItem->iImage = dispInfo.item.iImage;
5319 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5320 pItemHdr->iImage = dispInfo.item.iImage;
5322 else if (lpLVItem->mask & LVIF_IMAGE)
5324 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5325 lpLVItem->iImage = pItemHdr->iImage;
5327 lpLVItem->iImage = 0;
5330 /* The pszText field */
5331 if (dispInfo.item.mask & LVIF_TEXT)
5333 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5334 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5336 lpLVItem->pszText = dispInfo.item.pszText;
5338 else if (lpLVItem->mask & LVIF_TEXT)
5340 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5341 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5344 /* if this is a subitem, we're done */
5345 if (isubitem) return TRUE;
5347 /* Next is the lParam field */
5348 if (dispInfo.item.mask & LVIF_PARAM)
5350 lpLVItem->lParam = dispInfo.item.lParam;
5351 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5352 lpItem->lParam = dispInfo.item.lParam;
5354 else if (lpLVItem->mask & LVIF_PARAM)
5355 lpLVItem->lParam = lpItem->lParam;
5357 /* ... the state field (this one is different due to uCallbackmask) */
5358 if (lpLVItem->mask & LVIF_STATE)
5360 lpLVItem->state = lpItem->state;
5361 if (dispInfo.item.mask & LVIF_STATE)
5363 lpLVItem->state &= ~dispInfo.item.stateMask;
5364 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5366 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5368 lpLVItem->state &= ~LVIS_FOCUSED;
5369 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5370 lpLVItem->state |= LVIS_FOCUSED;
5372 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5374 lpLVItem->state &= ~LVIS_SELECTED;
5375 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5376 lpLVItem->state |= LVIS_SELECTED;
5380 /* and last, but not least, the indent field */
5381 if (lpLVItem->mask & LVIF_INDENT)
5382 lpLVItem->iIndent = lpItem->iIndent;
5389 * Retrieves item attributes.
5392 * [I] hwnd : window handle
5393 * [IO] lpLVItem : item info
5394 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5395 * if FALSE, the lpLVItem is a LPLVITEMA.
5398 * This is the external 'GetItem' interface -- it properly copies
5399 * the text in the provided buffer.
5405 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5410 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5413 pszText = lpLVItem->pszText;
5414 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5415 if (bResult && lpLVItem->pszText != pszText)
5416 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5417 lpLVItem->pszText = pszText;
5425 * Retrieves the position (upper-left) of the listview control item.
5426 * Note that for LVS_ICON style, the upper-left is that of the icon
5427 * and not the bounding box.
5430 * [I] infoPtr : valid pointer to the listview structure
5431 * [I] nItem : item index
5432 * [O] lpptPosition : coordinate information
5438 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5440 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5443 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5445 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5447 LISTVIEW_GetOrigin(infoPtr, &Origin);
5448 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5450 if (uView == LVS_ICON)
5452 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5453 lpptPosition->y += ICON_TOP_PADDING;
5455 lpptPosition->x += Origin.x;
5456 lpptPosition->y += Origin.y;
5458 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
5465 * Retrieves the bounding rectangle for a listview control item.
5468 * [I] infoPtr : valid pointer to the listview structure
5469 * [I] nItem : item index
5470 * [IO] lprc : bounding rectangle coordinates
5471 * lprc->left specifies the portion of the item for which the bounding
5472 * rectangle will be retrieved.
5474 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5475 * including the icon and label.
5478 * * Experiment shows that native control returns:
5479 * * width = min (48, length of text line)
5480 * * .left = position.x - (width - iconsize.cx)/2
5481 * * .right = .left + width
5482 * * height = #lines of text * ntmHeight + icon height + 8
5483 * * .top = position.y - 2
5484 * * .bottom = .top + height
5485 * * separation between items .y = itemSpacing.cy - height
5486 * * .x = itemSpacing.cx - width
5487 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5490 * * Experiment shows that native control returns:
5491 * * width = iconSize.cx + 16
5492 * * .left = position.x - (width - iconsize.cx)/2
5493 * * .right = .left + width
5494 * * height = iconSize.cy + 4
5495 * * .top = position.y - 2
5496 * * .bottom = .top + height
5497 * * separation between items .y = itemSpacing.cy - height
5498 * * .x = itemSpacing.cx - width
5499 * LVIR_LABEL Returns the bounding rectangle of the item text.
5502 * * Experiment shows that native control returns:
5503 * * width = text length
5504 * * .left = position.x - width/2
5505 * * .right = .left + width
5506 * * height = ntmH * linecount + 2
5507 * * .top = position.y + iconSize.cy + 6
5508 * * .bottom = .top + height
5509 * * separation between items .y = itemSpacing.cy - height
5510 * * .x = itemSpacing.cx - width
5511 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5512 * rectangles, but excludes columns in report view.
5519 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5520 * upon whether the window has the focus currently and on whether the item
5521 * is the one with the focus. Ensure that the control's record of which
5522 * item has the focus agrees with the items' records.
5524 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5526 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5527 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5528 BOOL doLabel = TRUE, oversizedBox = FALSE;
5529 POINT Position, Origin;
5533 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5535 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5537 LISTVIEW_GetOrigin(infoPtr, &Origin);
5538 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5540 /* Be smart and try to figure out the minimum we have to do */
5541 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5542 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5543 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5544 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5545 oversizedBox = TRUE;
5547 /* get what we need from the item before hand, so we make
5548 * only one request. This can speed up things, if data
5549 * is stored on the app side */
5551 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5552 if (doLabel) lvItem.mask |= LVIF_TEXT;
5553 lvItem.iItem = nItem;
5554 lvItem.iSubItem = 0;
5555 lvItem.pszText = szDispText;
5556 lvItem.cchTextMax = DISP_TEXT_SIZE;
5557 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5558 /* we got the state already up, simulate it here, to avoid a reget */
5559 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5561 lvItem.mask |= LVIF_STATE;
5562 lvItem.stateMask = LVIS_FOCUSED;
5563 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5566 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5567 lprc->left = LVIR_BOUNDS;
5571 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5575 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5579 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5582 case LVIR_SELECTBOUNDS:
5583 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5584 UnionRect(lprc, lprc, &label_rect);
5588 WARN("Unknown value: %ld\n", lprc->left);
5592 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5594 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
5601 * Retrieves the spacing between listview control items.
5604 * [I] infoPtr : valid pointer to the listview structure
5605 * [IO] lprc : rectangle to receive the output
5606 * on input, lprc->top = nSubItem
5607 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5609 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5610 * not only those of the first column.
5611 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5617 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5623 if (!lprc) return FALSE;
5625 nColumn = lprc->top;
5627 TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top);
5628 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5630 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5632 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5634 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5636 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5639 lvItem.iItem = nItem;
5640 lvItem.iSubItem = nColumn;
5642 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5646 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5651 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5655 ERR("Unknown bounds=%ld\n", lprc->left);
5659 OffsetRect(lprc, Position.x, Position.y);
5666 * Retrieves the width of a label.
5669 * [I] infoPtr : valid pointer to the listview structure
5672 * SUCCESS : string width (in pixels)
5675 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5677 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5680 TRACE("(nItem=%d)\n", nItem);
5682 lvItem.mask = LVIF_TEXT;
5683 lvItem.iItem = nItem;
5684 lvItem.iSubItem = 0;
5685 lvItem.pszText = szDispText;
5686 lvItem.cchTextMax = DISP_TEXT_SIZE;
5687 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5689 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5694 * Retrieves the spacing between listview control items.
5697 * [I] infoPtr : valid pointer to the listview structure
5698 * [I] bSmall : flag for small or large icon
5701 * Horizontal + vertical spacing
5703 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5709 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5713 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5714 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5716 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5723 * Retrieves the state of a listview control item.
5726 * [I] infoPtr : valid pointer to the listview structure
5727 * [I] nItem : item index
5728 * [I] uMask : state mask
5731 * State specified by the mask.
5733 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5737 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5739 lvItem.iItem = nItem;
5740 lvItem.iSubItem = 0;
5741 lvItem.mask = LVIF_STATE;
5742 lvItem.stateMask = uMask;
5743 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5745 return lvItem.state & uMask;
5750 * Retrieves the text of a listview control item or subitem.
5753 * [I] hwnd : window handle
5754 * [I] nItem : item index
5755 * [IO] lpLVItem : item information
5756 * [I] isW : TRUE if lpLVItem is Unicode
5759 * SUCCESS : string length
5762 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5764 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5766 lpLVItem->mask = LVIF_TEXT;
5767 lpLVItem->iItem = nItem;
5768 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5770 return textlenT(lpLVItem->pszText, isW);
5775 * Searches for an item based on properties + relationships.
5778 * [I] infoPtr : valid pointer to the listview structure
5779 * [I] nItem : item index
5780 * [I] uFlags : relationship flag
5783 * SUCCESS : item index
5786 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5788 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5790 LVFINDINFOW lvFindInfo;
5791 INT nCountPerColumn;
5795 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5796 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5798 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5800 if (uFlags & LVNI_CUT)
5803 if (uFlags & LVNI_DROPHILITED)
5804 uMask |= LVIS_DROPHILITED;
5806 if (uFlags & LVNI_FOCUSED)
5807 uMask |= LVIS_FOCUSED;
5809 if (uFlags & LVNI_SELECTED)
5810 uMask |= LVIS_SELECTED;
5812 /* if we're asked for the focused item, that's only one,
5813 * so it's worth optimizing */
5814 if (uFlags & LVNI_FOCUSED)
5816 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
5817 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5820 if (uFlags & LVNI_ABOVE)
5822 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5827 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5833 /* Special case for autoarrange - move 'til the top of a list */
5834 if (is_autoarrange(infoPtr))
5836 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5837 while (nItem - nCountPerRow >= 0)
5839 nItem -= nCountPerRow;
5840 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5845 lvFindInfo.flags = LVFI_NEARESTXY;
5846 lvFindInfo.vkDirection = VK_UP;
5847 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5848 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5850 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5855 else if (uFlags & LVNI_BELOW)
5857 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5859 while (nItem < infoPtr->nItemCount)
5862 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5868 /* Special case for autoarrange - move 'til the bottom of a list */
5869 if (is_autoarrange(infoPtr))
5871 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5872 while (nItem + nCountPerRow < infoPtr->nItemCount )
5874 nItem += nCountPerRow;
5875 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5880 lvFindInfo.flags = LVFI_NEARESTXY;
5881 lvFindInfo.vkDirection = VK_DOWN;
5882 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5883 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5885 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5890 else if (uFlags & LVNI_TOLEFT)
5892 if (uView == LVS_LIST)
5894 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5895 while (nItem - nCountPerColumn >= 0)
5897 nItem -= nCountPerColumn;
5898 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5902 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5904 /* Special case for autoarrange - move 'ti the beginning of a row */
5905 if (is_autoarrange(infoPtr))
5907 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5908 while (nItem % nCountPerRow > 0)
5911 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5916 lvFindInfo.flags = LVFI_NEARESTXY;
5917 lvFindInfo.vkDirection = VK_LEFT;
5918 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5919 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5921 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5926 else if (uFlags & LVNI_TORIGHT)
5928 if (uView == LVS_LIST)
5930 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5931 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5933 nItem += nCountPerColumn;
5934 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5938 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5940 /* Special case for autoarrange - move 'til the end of a row */
5941 if (is_autoarrange(infoPtr))
5943 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5944 while (nItem % nCountPerRow < nCountPerRow - 1 )
5947 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5952 lvFindInfo.flags = LVFI_NEARESTXY;
5953 lvFindInfo.vkDirection = VK_RIGHT;
5954 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5955 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5957 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5966 /* search by index */
5967 for (i = nItem; i < infoPtr->nItemCount; i++)
5969 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5977 /* LISTVIEW_GetNumberOfWorkAreas */
5981 * Retrieves the origin coordinates when in icon or small icon display mode.
5984 * [I] infoPtr : valid pointer to the listview structure
5985 * [O] lpptOrigin : coordinate information
5990 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5992 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5993 INT nHorzPos = 0, nVertPos = 0;
5994 SCROLLINFO scrollInfo;
5996 scrollInfo.cbSize = sizeof(SCROLLINFO);
5997 scrollInfo.fMask = SIF_POS;
5999 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
6000 nHorzPos = scrollInfo.nPos;
6001 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
6002 nVertPos = scrollInfo.nPos;
6004 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6006 lpptOrigin->x = infoPtr->rcList.left;
6007 lpptOrigin->y = infoPtr->rcList.top;
6008 if (uView == LVS_LIST)
6009 nHorzPos *= infoPtr->nItemWidth;
6010 else if (uView == LVS_REPORT)
6011 nVertPos *= infoPtr->nItemHeight;
6013 lpptOrigin->x -= nHorzPos;
6014 lpptOrigin->y -= nVertPos;
6016 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
6021 * Retrieves the width of a string.
6024 * [I] hwnd : window handle
6025 * [I] lpszText : text string to process
6026 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6029 * SUCCESS : string width (in pixels)
6032 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6037 if (is_textT(lpszText, isW))
6039 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6040 HDC hdc = GetDC(infoPtr->hwndSelf);
6041 HFONT hOldFont = SelectObject(hdc, hFont);
6044 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6046 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6047 SelectObject(hdc, hOldFont);
6048 ReleaseDC(infoPtr->hwndSelf, hdc);
6050 return stringSize.cx;
6055 * Determines which listview item is located at the specified position.
6058 * [I] infoPtr : valid pointer to the listview structure
6059 * [IO] lpht : hit test information
6060 * [I] subitem : fill out iSubItem.
6061 * [I] select : return the index only if the hit selects the item
6064 * (mm 20001022): We must not allow iSubItem to be touched, for
6065 * an app might pass only a structure with space up to iItem!
6066 * (MS Office 97 does that for instance in the file open dialog)
6069 * SUCCESS : item index
6072 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6074 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6075 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6076 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6077 POINT Origin, Position, opt;
6082 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6086 if (subitem) lpht->iSubItem = 0;
6088 if (infoPtr->rcList.left > lpht->pt.x)
6089 lpht->flags |= LVHT_TOLEFT;
6090 else if (infoPtr->rcList.right < lpht->pt.x)
6091 lpht->flags |= LVHT_TORIGHT;
6093 if (infoPtr->rcList.top > lpht->pt.y)
6094 lpht->flags |= LVHT_ABOVE;
6095 else if (infoPtr->rcList.bottom < lpht->pt.y)
6096 lpht->flags |= LVHT_BELOW;
6098 TRACE("lpht->flags=0x%x\n", lpht->flags);
6099 if (lpht->flags) return -1;
6101 lpht->flags |= LVHT_NOWHERE;
6103 LISTVIEW_GetOrigin(infoPtr, &Origin);
6105 /* first deal with the large items */
6106 rcSearch.left = lpht->pt.x;
6107 rcSearch.top = lpht->pt.y;
6108 rcSearch.right = rcSearch.left + 1;
6109 rcSearch.bottom = rcSearch.top + 1;
6111 iterator_frameditems(&i, infoPtr, &rcSearch);
6112 iterator_next(&i); /* go to first item in the sequence */
6114 iterator_destroy(&i);
6116 TRACE("lpht->iItem=%d\n", iItem);
6117 if (iItem == -1) return -1;
6119 lvItem.mask = LVIF_STATE | LVIF_TEXT;
6120 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6121 lvItem.stateMask = LVIS_STATEIMAGEMASK;
6122 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6123 lvItem.iItem = iItem;
6124 lvItem.iSubItem = 0;
6125 lvItem.pszText = szDispText;
6126 lvItem.cchTextMax = DISP_TEXT_SIZE;
6127 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6128 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6130 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
6131 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6132 opt.x = lpht->pt.x - Position.x - Origin.x;
6133 opt.y = lpht->pt.y - Position.y - Origin.y;
6135 if (uView == LVS_REPORT)
6138 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6139 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6140 if (!PtInRect(&rcBounds, opt)) return -1;
6142 if (PtInRect(&rcIcon, opt))
6143 lpht->flags |= LVHT_ONITEMICON;
6144 else if (PtInRect(&rcLabel, opt))
6145 lpht->flags |= LVHT_ONITEMLABEL;
6146 else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6147 lpht->flags |= LVHT_ONITEMSTATEICON;
6148 if (lpht->flags & LVHT_ONITEM)
6149 lpht->flags &= ~LVHT_NOWHERE;
6151 TRACE("lpht->flags=0x%x\n", lpht->flags);
6152 if (uView == LVS_REPORT && subitem)
6156 rcBounds.right = rcBounds.left;
6157 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6159 rcBounds.left = rcBounds.right;
6160 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6161 if (PtInRect(&rcBounds, opt))
6169 if (select && !(uView == LVS_REPORT &&
6170 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6171 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6173 if (uView == LVS_REPORT)
6175 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6176 UnionRect(&rcBounds, &rcBounds, &rcState);
6178 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6180 return lpht->iItem = iItem;
6184 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6185 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6186 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6187 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6188 their own sort proc. when sending LVM_SORTITEMS.
6191 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6193 LVS_SORTXXX must be specified,
6194 LVS_OWNERDRAW is not set,
6195 <item>.pszText is not LPSTR_TEXTCALLBACK.
6197 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6198 are sorted based on item text..."
6200 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6202 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6203 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6204 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6206 /* if we're sorting descending, negate the return value */
6207 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6212 * Inserts a new item in the listview control.
6215 * [I] infoPtr : valid pointer to the listview structure
6216 * [I] lpLVItem : item information
6217 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6220 * SUCCESS : new item index
6223 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6225 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6230 BOOL is_sorted, has_changed;
6232 HWND hwndSelf = infoPtr->hwndSelf;
6234 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6236 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6238 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6239 if (!lpLVItem || lpLVItem->iSubItem) return -1;
6241 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6243 if (!(lpItem = (ITEM_INFO *)Alloc(sizeof(ITEM_INFO)))) return -1;
6245 /* insert item in listview control data structure */
6246 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6247 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6249 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6250 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6252 if (lpLVItem->iItem < 0 && !is_sorted) return -1;
6254 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6255 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6256 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6257 if (nItem == -1) goto fail;
6258 infoPtr->nItemCount++;
6260 /* shift indices first so they don't get tangled */
6261 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6263 /* set the item attributes */
6264 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6266 /* full size structure expected - _WIN32IE >= 0x560 */
6269 else if (lpLVItem->mask & LVIF_INDENT)
6271 /* indent member expected - _WIN32IE >= 0x300 */
6272 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6276 /* minimal structure expected */
6277 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6280 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6282 item.mask |= LVIF_STATE;
6283 item.stateMask |= LVIS_STATEIMAGEMASK;
6284 item.state &= ~LVIS_STATEIMAGEMASK;
6285 item.state |= INDEXTOSTATEIMAGEMASK(1);
6287 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6289 /* if we're sorted, sort the list, and update the index */
6292 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6293 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6294 assert(nItem != -1);
6297 /* make room for the position, if we are in the right mode */
6298 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6300 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6302 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6304 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6309 /* send LVN_INSERTITEM notification */
6310 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6312 nmlv.lParam = lpItem->lParam;
6313 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6314 if (!IsWindow(hwndSelf))
6317 /* align items (set position of each item) */
6318 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6322 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6323 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6325 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6327 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6330 /* now is the invalidation fun */
6331 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6335 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6336 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6337 infoPtr->nItemCount--;
6339 DPA_DeletePtr(hdpaSubItems, 0);
6340 DPA_Destroy (hdpaSubItems);
6347 * Redraws a range of items.
6350 * [I] infoPtr : valid pointer to the listview structure
6351 * [I] nFirst : first item
6352 * [I] nLast : last item
6358 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6362 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6363 max(nFirst, nLast) >= infoPtr->nItemCount)
6366 for (i = nFirst; i <= nLast; i++)
6367 LISTVIEW_InvalidateItem(infoPtr, i);
6374 * Scroll the content of a listview.
6377 * [I] infoPtr : valid pointer to the listview structure
6378 * [I] dx : horizontal scroll amount in pixels
6379 * [I] dy : vertical scroll amount in pixels
6386 * If the control is in report mode (LVS_REPORT) the control can
6387 * be scrolled only in line increments. "dy" will be rounded to the
6388 * nearest number of pixels that are a whole line. Ex: if line height
6389 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6390 * is passed the the scroll will be 0. (per MSDN 7/2002)
6392 * For: (per experimentaion with native control and CSpy ListView)
6393 * LVS_ICON dy=1 = 1 pixel (vertical only)
6395 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6397 * LVS_LIST dx=1 = 1 column (horizontal only)
6398 * but will only scroll 1 column per message
6399 * no matter what the value.
6400 * dy must be 0 or FALSE returned.
6401 * LVS_REPORT dx=1 = 1 pixel
6405 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6407 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6409 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6410 dy /= infoPtr->nItemHeight;
6413 if (dy != 0) return FALSE;
6420 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6421 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6428 * Sets the background color.
6431 * [I] infoPtr : valid pointer to the listview structure
6432 * [I] clrBk : background color
6438 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6440 TRACE("(clrBk=%lx)\n", clrBk);
6442 if(infoPtr->clrBk != clrBk) {
6443 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6444 infoPtr->clrBk = clrBk;
6445 if (clrBk == CLR_NONE)
6446 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6448 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6449 LISTVIEW_InvalidateList(infoPtr);
6455 /* LISTVIEW_SetBkImage */
6457 /*** Helper for {Insert,Set}ColumnT *only* */
6458 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6460 if (lpColumn->mask & LVCF_FMT)
6462 /* format member is valid */
6463 lphdi->mask |= HDI_FORMAT;
6465 /* set text alignment (leftmost column must be left-aligned) */
6466 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6467 lphdi->fmt |= HDF_LEFT;
6468 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6469 lphdi->fmt |= HDF_RIGHT;
6470 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6471 lphdi->fmt |= HDF_CENTER;
6473 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6474 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6476 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6478 lphdi->fmt |= HDF_IMAGE;
6479 lphdi->iImage = I_IMAGECALLBACK;
6483 if (lpColumn->mask & LVCF_WIDTH)
6485 lphdi->mask |= HDI_WIDTH;
6486 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6488 /* make it fill the remainder of the controls width */
6492 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6494 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6495 lphdi->cxy += rcHeader.right - rcHeader.left;
6498 /* retrieve the layout of the header */
6499 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6500 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
6502 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6505 lphdi->cxy = lpColumn->cx;
6508 if (lpColumn->mask & LVCF_TEXT)
6510 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6511 lphdi->fmt |= HDF_STRING;
6512 lphdi->pszText = lpColumn->pszText;
6513 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6516 if (lpColumn->mask & LVCF_IMAGE)
6518 lphdi->mask |= HDI_IMAGE;
6519 lphdi->iImage = lpColumn->iImage;
6522 if (lpColumn->mask & LVCF_ORDER)
6524 lphdi->mask |= HDI_ORDER;
6525 lphdi->iOrder = lpColumn->iOrder;
6532 * Inserts a new column.
6535 * [I] infoPtr : valid pointer to the listview structure
6536 * [I] nColumn : column index
6537 * [I] lpColumn : column information
6538 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6541 * SUCCESS : new column index
6544 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6545 const LVCOLUMNW *lpColumn, BOOL isW)
6547 COLUMN_INFO *lpColumnInfo;
6551 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6553 if (!lpColumn || nColumn < 0) return -1;
6554 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6556 ZeroMemory(&hdi, sizeof(HDITEMW));
6557 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6560 * when the iSubItem is available Windows copies it to the header lParam. It seems
6561 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
6563 if (lpColumn->mask & LVCF_SUBITEM)
6565 hdi.mask |= HDI_LPARAM;
6566 hdi.lParam = lpColumn->iSubItem;
6569 /* insert item in header control */
6570 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6571 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6572 (WPARAM)nColumn, (LPARAM)&hdi);
6573 if (nNewColumn == -1) return -1;
6574 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6576 /* create our own column info */
6577 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6578 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6580 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6581 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6583 /* now we have to actually adjust the data */
6584 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6586 SUBITEM_INFO *lpSubItem;
6590 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6592 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6593 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6595 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6596 if (lpSubItem->iSubItem >= nNewColumn)
6597 lpSubItem->iSubItem++;
6602 /* make space for the new column */
6603 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6604 LISTVIEW_UpdateItemSize(infoPtr);
6609 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6612 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6620 * Sets the attributes of a header item.
6623 * [I] infoPtr : valid pointer to the listview structure
6624 * [I] nColumn : column index
6625 * [I] lpColumn : column attributes
6626 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6632 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6633 const LVCOLUMNW *lpColumn, BOOL isW)
6635 HDITEMW hdi, hdiget;
6638 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6640 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6642 ZeroMemory(&hdi, sizeof(HDITEMW));
6643 if (lpColumn->mask & LVCF_FMT)
6645 hdi.mask |= HDI_FORMAT;
6646 hdiget.mask = HDI_FORMAT;
6647 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6648 hdi.fmt = hdiget.fmt & HDF_STRING;
6650 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6652 /* set header item attributes */
6653 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6654 if (!bResult) return FALSE;
6656 if (lpColumn->mask & LVCF_FMT)
6658 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6659 int oldFmt = lpColumnInfo->fmt;
6661 lpColumnInfo->fmt = lpColumn->fmt;
6662 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6664 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6665 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6674 * Sets the column order array
6677 * [I] infoPtr : valid pointer to the listview structure
6678 * [I] iCount : number of elements in column order array
6679 * [I] lpiArray : pointer to column order array
6685 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6687 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6698 * Sets the width of a column
6701 * [I] infoPtr : valid pointer to the listview structure
6702 * [I] nColumn : column index
6703 * [I] cx : column width
6709 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6711 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6712 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6716 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6718 /* set column width only if in report or list mode */
6719 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6721 /* take care of invalid cx values */
6722 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6723 else if (uView == LVS_LIST && cx < 1) return FALSE;
6725 /* resize all columns if in LVS_LIST mode */
6726 if(uView == LVS_LIST)
6728 infoPtr->nItemWidth = cx;
6729 LISTVIEW_InvalidateList(infoPtr);
6733 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6735 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6740 lvItem.mask = LVIF_TEXT;
6742 lvItem.iSubItem = nColumn;
6743 lvItem.pszText = szDispText;
6744 lvItem.cchTextMax = DISP_TEXT_SIZE;
6745 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6747 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6748 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6749 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6751 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6752 max_cx += infoPtr->iconSize.cx;
6753 max_cx += TRAILING_LABEL_PADDING;
6756 /* autosize based on listview items width */
6757 if(cx == LVSCW_AUTOSIZE)
6759 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6761 /* if iCol is the last column make it fill the remainder of the controls width */
6762 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
6767 LISTVIEW_GetOrigin(infoPtr, &Origin);
6768 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6770 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6774 /* Despite what the MS docs say, if this is not the last
6775 column, then MS resizes the column to the width of the
6776 largest text string in the column, including headers
6777 and items. This is different from LVSCW_AUTOSIZE in that
6778 LVSCW_AUTOSIZE ignores the header string length. */
6781 /* retrieve header text */
6782 hdi.mask = HDI_TEXT;
6783 hdi.cchTextMax = DISP_TEXT_SIZE;
6784 hdi.pszText = szDispText;
6785 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
6787 HDC hdc = GetDC(infoPtr->hwndSelf);
6788 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6791 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6792 cx = size.cx + TRAILING_HEADER_PADDING;
6793 /* FIXME: Take into account the header image, if one is present */
6794 SelectObject(hdc, old_font);
6795 ReleaseDC(infoPtr->hwndSelf, hdc);
6797 cx = max (cx, max_cx);
6801 if (cx < 0) return FALSE;
6803 /* call header to update the column change */
6804 hdi.mask = HDI_WIDTH;
6806 TRACE("hdi.cxy=%d\n", hdi.cxy);
6807 return Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6811 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
6814 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO *infoPtr)
6817 HBITMAP hbm_im, hbm_mask, hbm_orig;
6819 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6820 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6823 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6824 ILC_COLOR | ILC_MASK, 2, 2);
6825 hdc_wnd = GetDC(infoPtr->hwndSelf);
6826 hdc = CreateCompatibleDC(hdc_wnd);
6827 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6828 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6829 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6831 rc.left = rc.top = 0;
6832 rc.right = GetSystemMetrics(SM_CXSMICON);
6833 rc.bottom = GetSystemMetrics(SM_CYSMICON);
6835 hbm_orig = SelectObject(hdc, hbm_mask);
6836 FillRect(hdc, &rc, hbr_white);
6837 InflateRect(&rc, -3, -3);
6838 FillRect(hdc, &rc, hbr_black);
6840 SelectObject(hdc, hbm_im);
6841 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6842 SelectObject(hdc, hbm_orig);
6843 ImageList_Add(himl, hbm_im, hbm_mask);
6845 SelectObject(hdc, hbm_im);
6846 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6847 SelectObject(hdc, hbm_orig);
6848 ImageList_Add(himl, hbm_im, hbm_mask);
6850 DeleteObject(hbm_mask);
6851 DeleteObject(hbm_im);
6859 * Sets the extended listview style.
6862 * [I] infoPtr : valid pointer to the listview structure
6864 * [I] dwStyle : style
6867 * SUCCESS : previous style
6870 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6872 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6876 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6878 infoPtr->dwLvExStyle = dwStyle;
6880 if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES)
6882 HIMAGELIST himl = 0;
6883 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6886 item.mask = LVIF_STATE;
6887 item.stateMask = LVIS_STATEIMAGEMASK;
6888 item.state = INDEXTOSTATEIMAGEMASK(1);
6889 LISTVIEW_SetItemState(infoPtr, -1, &item);
6891 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
6893 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
6896 if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_HEADERDRAGDROP)
6898 DWORD dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
6899 if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
6900 dwStyle |= HDS_DRAGDROP;
6902 dwStyle &= ~HDS_DRAGDROP;
6903 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
6911 * Sets the new hot cursor used during hot tracking and hover selection.
6914 * [I] infoPtr : valid pointer to the listview structure
6915 * [I] hCursor : the new hot cursor handle
6918 * Returns the previous hot cursor
6920 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6922 HCURSOR oldCursor = infoPtr->hHotCursor;
6924 infoPtr->hHotCursor = hCursor;
6932 * Sets the hot item index.
6935 * [I] infoPtr : valid pointer to the listview structure
6936 * [I] iIndex : index
6939 * SUCCESS : previous hot item index
6940 * FAILURE : -1 (no hot item)
6942 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6944 INT iOldIndex = infoPtr->nHotItem;
6946 infoPtr->nHotItem = iIndex;
6954 * Sets the amount of time the cursor must hover over an item before it is selected.
6957 * [I] infoPtr : valid pointer to the listview structure
6958 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6961 * Returns the previous hover time
6963 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6965 DWORD oldHoverTime = infoPtr->dwHoverTime;
6967 infoPtr->dwHoverTime = dwHoverTime;
6969 return oldHoverTime;
6974 * Sets spacing for icons of LVS_ICON style.
6977 * [I] infoPtr : valid pointer to the listview structure
6978 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6979 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6982 * MAKELONG(oldcx, oldcy)
6984 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6986 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6987 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6989 TRACE("requested=(%d,%d)\n", cx, cy);
6991 /* this is supported only for LVS_ICON style */
6992 if (uView != LVS_ICON) return oldspacing;
6994 /* set to defaults, if instructed to */
6995 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6996 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6998 /* if 0 then compute width
6999 * FIXME: Should scan each item and determine max width of
7000 * icon or label, then make that the width */
7002 cx = infoPtr->iconSpacing.cx;
7004 /* if 0 then compute height */
7006 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
7007 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
7010 infoPtr->iconSpacing.cx = cx;
7011 infoPtr->iconSpacing.cy = cy;
7013 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
7014 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
7015 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
7016 infoPtr->ntmHeight);
7018 /* these depend on the iconSpacing */
7019 LISTVIEW_UpdateItemSize(infoPtr);
7024 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
7028 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
7035 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
7036 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7045 * [I] infoPtr : valid pointer to the listview structure
7046 * [I] nType : image list type
7047 * [I] himl : image list handle
7050 * SUCCESS : old image list
7053 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7055 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7056 INT oldHeight = infoPtr->nItemHeight;
7057 HIMAGELIST himlOld = 0;
7059 TRACE("(nType=%d, himl=%p\n", nType, himl);
7064 himlOld = infoPtr->himlNormal;
7065 infoPtr->himlNormal = himl;
7066 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7067 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7071 himlOld = infoPtr->himlSmall;
7072 infoPtr->himlSmall = himl;
7073 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7077 himlOld = infoPtr->himlState;
7078 infoPtr->himlState = himl;
7079 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7080 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7084 ERR("Unknown icon type=%d\n", nType);
7088 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7089 if (infoPtr->nItemHeight != oldHeight)
7090 LISTVIEW_UpdateScroll(infoPtr);
7097 * Preallocates memory (does *not* set the actual count of items !)
7100 * [I] infoPtr : valid pointer to the listview structure
7101 * [I] nItems : item count (projected number of items to allocate)
7102 * [I] dwFlags : update flags
7108 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7110 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
7112 if (infoPtr->dwStyle & LVS_OWNERDATA)
7114 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7115 INT nOldCount = infoPtr->nItemCount;
7117 if (nItems < nOldCount)
7119 RANGE range = { nItems, nOldCount };
7120 ranges_del(infoPtr->selectionRanges, range);
7121 if (infoPtr->nFocusedItem >= nItems)
7123 infoPtr->nFocusedItem = -1;
7124 SetRectEmpty(&infoPtr->rcFocus);
7128 infoPtr->nItemCount = nItems;
7129 LISTVIEW_UpdateScroll(infoPtr);
7131 /* the flags are valid only in ownerdata report and list modes */
7132 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7134 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7135 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7137 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7138 LISTVIEW_InvalidateList(infoPtr);
7145 LISTVIEW_GetOrigin(infoPtr, &Origin);
7146 nFrom = min(nOldCount, nItems);
7147 nTo = max(nOldCount, nItems);
7149 if (uView == LVS_REPORT)
7152 rcErase.top = nFrom * infoPtr->nItemHeight;
7153 rcErase.right = infoPtr->nItemWidth;
7154 rcErase.bottom = nTo * infoPtr->nItemHeight;
7155 OffsetRect(&rcErase, Origin.x, Origin.y);
7156 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7157 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7161 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7163 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7164 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7165 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7166 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7167 OffsetRect(&rcErase, Origin.x, Origin.y);
7168 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7169 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7171 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7173 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7174 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7175 OffsetRect(&rcErase, Origin.x, Origin.y);
7176 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7177 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7183 /* According to MSDN for non-LVS_OWNERDATA this is just
7184 * a performance issue. The control allocates its internal
7185 * data structures for the number of items specified. It
7186 * cuts down on the number of memory allocations. Therefore
7187 * we will just issue a WARN here
7189 WARN("for non-ownerdata performance option not implemented.\n");
7197 * Sets the position of an item.
7200 * [I] infoPtr : valid pointer to the listview structure
7201 * [I] nItem : item index
7202 * [I] pt : coordinate
7208 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7210 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7213 TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7215 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7216 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7218 LISTVIEW_GetOrigin(infoPtr, &Origin);
7220 /* This point value seems to be an undocumented feature.
7221 * The best guess is that it means either at the origin,
7222 * or at true beginning of the list. I will assume the origin. */
7223 if ((pt.x == -1) && (pt.y == -1))
7226 if (uView == LVS_ICON)
7228 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7229 pt.y -= ICON_TOP_PADDING;
7234 infoPtr->bAutoarrange = FALSE;
7236 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7241 * Sets the state of one or many items.
7244 * [I] infoPtr : valid pointer to the listview structure
7245 * [I] nItem : item index
7246 * [I] lpLVItem : item or subitem info
7252 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7254 BOOL bResult = TRUE;
7257 lvItem.iItem = nItem;
7258 lvItem.iSubItem = 0;
7259 lvItem.mask = LVIF_STATE;
7260 lvItem.state = lpLVItem->state;
7261 lvItem.stateMask = lpLVItem->stateMask;
7262 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7266 /* apply to all items */
7267 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7268 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7271 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7274 * Update selection mark
7276 * Investigation on windows 2k showed that selection mark was updated
7277 * whenever a new selection was made, but if the selected item was
7278 * unselected it was not updated.
7280 * we are probably still not 100% accurate, but this at least sets the
7281 * proper selection mark when it is needed
7284 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7285 ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark)))
7288 infoPtr->nSelectionMark = -1;
7289 for (i = 0; i < infoPtr->nItemCount; i++)
7291 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7293 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7295 infoPtr->nSelectionMark = i;
7299 else if (ranges_contain(infoPtr->selectionRanges, i))
7301 infoPtr->nSelectionMark = i;
7312 * Sets the text of an item or subitem.
7315 * [I] hwnd : window handle
7316 * [I] nItem : item index
7317 * [I] lpLVItem : item or subitem info
7318 * [I] isW : TRUE if input is Unicode
7324 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7328 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7330 lvItem.iItem = nItem;
7331 lvItem.iSubItem = lpLVItem->iSubItem;
7332 lvItem.mask = LVIF_TEXT;
7333 lvItem.pszText = lpLVItem->pszText;
7334 lvItem.cchTextMax = lpLVItem->cchTextMax;
7336 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7338 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7343 * Set item index that marks the start of a multiple selection.
7346 * [I] infoPtr : valid pointer to the listview structure
7347 * [I] nIndex : index
7350 * Index number or -1 if there is no selection mark.
7352 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7354 INT nOldIndex = infoPtr->nSelectionMark;
7356 TRACE("(nIndex=%d)\n", nIndex);
7358 infoPtr->nSelectionMark = nIndex;
7365 * Sets the text background color.
7368 * [I] infoPtr : valid pointer to the listview structure
7369 * [I] clrTextBk : text background color
7375 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7377 TRACE("(clrTextBk=%lx)\n", clrTextBk);
7379 if (infoPtr->clrTextBk != clrTextBk)
7381 infoPtr->clrTextBk = clrTextBk;
7382 LISTVIEW_InvalidateList(infoPtr);
7390 * Sets the text foreground color.
7393 * [I] infoPtr : valid pointer to the listview structure
7394 * [I] clrText : text color
7400 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7402 TRACE("(clrText=%lx)\n", clrText);
7404 if (infoPtr->clrText != clrText)
7406 infoPtr->clrText = clrText;
7407 LISTVIEW_InvalidateList(infoPtr);
7415 * Determines which listview item is located at the specified position.
7418 * [I] infoPtr : valid pointer to the listview structure
7419 * [I] hwndNewToolTip : handle to new ToolTip
7424 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7426 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7427 infoPtr->hwndToolTip = hwndNewToolTip;
7428 return hwndOldToolTip;
7431 /* LISTVIEW_SetUnicodeFormat */
7432 /* LISTVIEW_SetWorkAreas */
7436 * Callback internally used by LISTVIEW_SortItems()
7439 * [I] first : pointer to first ITEM_INFO to compare
7440 * [I] second : pointer to second ITEM_INFO to compare
7441 * [I] lParam : HWND of control
7444 * if first comes before second : negative
7445 * if first comes after second : positive
7446 * if first and second are equivalent : zero
7448 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7450 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7451 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7452 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7454 /* Forward the call to the client defined callback */
7455 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7460 * Sorts the listview items.
7463 * [I] infoPtr : valid pointer to the listview structure
7464 * [I] pfnCompare : application-defined value
7465 * [I] lParamSort : pointer to comparision callback
7471 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7473 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7476 LPVOID selectionMarkItem;
7480 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7482 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7484 if (!pfnCompare) return FALSE;
7485 if (!infoPtr->hdpaItems) return FALSE;
7487 /* if there are 0 or 1 items, there is no need to sort */
7488 if (infoPtr->nItemCount < 2) return TRUE;
7490 if (infoPtr->nFocusedItem >= 0)
7492 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7493 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7494 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7496 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7497 /* clear the lpItem->state for non-selected ones */
7498 /* remove the selection ranges */
7500 infoPtr->pfnCompare = pfnCompare;
7501 infoPtr->lParamSort = lParamSort;
7502 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7504 /* Adjust selections and indices so that they are the way they should
7505 * be after the sort (otherwise, the list items move around, but
7506 * whatever is at the item's previous original position will be
7509 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7510 for (i=0; i < infoPtr->nItemCount; i++)
7512 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7513 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7515 if (lpItem->state & LVIS_SELECTED)
7517 item.state = LVIS_SELECTED;
7518 item.stateMask = LVIS_SELECTED;
7519 LISTVIEW_SetItemState(infoPtr, i, &item);
7521 if (lpItem->state & LVIS_FOCUSED)
7523 infoPtr->nFocusedItem = i;
7524 lpItem->state &= ~LVIS_FOCUSED;
7527 if (selectionMarkItem != NULL)
7528 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7529 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7531 /* refresh the display */
7532 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7533 LISTVIEW_InvalidateList(infoPtr);
7540 * Update theme handle after a theme change.
7543 * [I] infoPtr : valid pointer to the listview structure
7547 * FAILURE : something else
7549 static LRESULT LISTVIEW_ThemeChanged(LISTVIEW_INFO *infoPtr)
7551 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7552 CloseThemeData(theme);
7553 OpenThemeData(infoPtr->hwndSelf, themeClass);
7559 * Updates an items or rearranges the listview control.
7562 * [I] infoPtr : valid pointer to the listview structure
7563 * [I] nItem : item index
7569 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7571 TRACE("(nItem=%d)\n", nItem);
7573 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7575 /* rearrange with default alignment style */
7576 if (is_autoarrange(infoPtr))
7577 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7579 LISTVIEW_InvalidateItem(infoPtr, nItem);
7586 * Draw the track line at the place defined in the infoPtr structure.
7587 * The line is drawn with a XOR pen so drawing the line for the second time
7588 * in the same place erases the line.
7591 * [I] infoPtr : valid pointer to the listview structure
7597 static BOOL LISTVIEW_DrawTrackLine(LISTVIEW_INFO *infoPtr)
7603 if (infoPtr->xTrackLine == -1)
7606 if (!(hdc = GetDC(infoPtr->hwndSelf)))
7608 hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
7609 oldROP = SetROP2(hdc, R2_XORPEN);
7610 MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
7611 LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
7612 SetROP2(hdc, oldROP);
7613 SelectObject(hdc, hOldPen);
7614 ReleaseDC(infoPtr->hwndSelf, hdc);
7621 * Creates the listview control.
7624 * [I] hwnd : window handle
7625 * [I] lpcs : the create parameters
7631 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7633 LISTVIEW_INFO *infoPtr;
7634 UINT uView = lpcs->style & LVS_TYPEMASK;
7637 TRACE("(lpcs=%p)\n", lpcs);
7639 /* initialize info pointer */
7640 infoPtr = (LISTVIEW_INFO *)Alloc(sizeof(LISTVIEW_INFO));
7641 if (!infoPtr) return -1;
7643 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
7645 infoPtr->hwndSelf = hwnd;
7646 infoPtr->dwStyle = lpcs->style;
7647 /* determine the type of structures to use */
7648 infoPtr->hwndNotify = lpcs->hwndParent;
7649 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7650 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7652 /* initialize color information */
7653 infoPtr->clrBk = CLR_NONE;
7654 infoPtr->clrText = comctl32_color.clrWindowText;
7655 infoPtr->clrTextBk = CLR_DEFAULT;
7656 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7658 /* set default values */
7659 infoPtr->nFocusedItem = -1;
7660 infoPtr->nSelectionMark = -1;
7661 infoPtr->nHotItem = -1;
7662 infoPtr->bRedraw = TRUE;
7663 infoPtr->bNoItemMetrics = TRUE;
7664 infoPtr->bDoChangeNotify = TRUE;
7665 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7666 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7667 infoPtr->nEditLabelItem = -1;
7668 infoPtr->dwHoverTime = -1; /* default system hover time */
7669 infoPtr->nMeasureItemHeight = 0;
7670 infoPtr->xTrackLine = -1; /* no track line */
7672 /* get default font (icon title) */
7673 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7674 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7675 infoPtr->hFont = infoPtr->hDefaultFont;
7676 LISTVIEW_SaveTextMetrics(infoPtr);
7679 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7680 WS_CHILD | HDS_HORZ | HDS_FULLDRAG | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7681 0, 0, 0, 0, hwnd, NULL,
7682 lpcs->hInstance, NULL);
7683 if (!infoPtr->hwndHeader) goto fail;
7685 /* set header unicode format */
7686 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7688 /* set header font */
7689 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7691 /* allocate memory for the data structure */
7692 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7693 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7694 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7695 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7696 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7698 /* initialize the icon sizes */
7699 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7700 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7702 /* init item size to avoid division by 0 */
7703 LISTVIEW_UpdateItemSize (infoPtr);
7705 if (uView == LVS_REPORT)
7707 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7709 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7713 /* set HDS_HIDDEN flag to hide the header bar */
7714 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7715 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7719 OpenThemeData(hwnd, themeClass);
7724 DestroyWindow(infoPtr->hwndHeader);
7725 ranges_destroy(infoPtr->selectionRanges);
7726 DPA_Destroy(infoPtr->hdpaItems);
7727 DPA_Destroy(infoPtr->hdpaPosX);
7728 DPA_Destroy(infoPtr->hdpaPosY);
7729 DPA_Destroy(infoPtr->hdpaColumns);
7736 * Destroys the listview control.
7739 * [I] infoPtr : valid pointer to the listview structure
7745 static LRESULT LISTVIEW_Destroy(LISTVIEW_INFO *infoPtr)
7747 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7748 CloseThemeData(theme);
7754 * Enables the listview control.
7757 * [I] infoPtr : valid pointer to the listview structure
7758 * [I] bEnable : specifies whether to enable or disable the window
7764 static BOOL LISTVIEW_Enable(LISTVIEW_INFO *infoPtr, BOOL bEnable)
7766 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
7767 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7773 * Erases the background of the listview control.
7776 * [I] infoPtr : valid pointer to the listview structure
7777 * [I] hdc : device context handle
7783 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7787 TRACE("(hdc=%p)\n", hdc);
7789 if (!GetClipBox(hdc, &rc)) return FALSE;
7791 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7797 * Helper function for LISTVIEW_[HV]Scroll *only*.
7798 * Performs vertical/horizontal scrolling by a give amount.
7801 * [I] infoPtr : valid pointer to the listview structure
7802 * [I] dx : amount of horizontal scroll
7803 * [I] dy : amount of vertical scroll
7805 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7807 /* now we can scroll the list */
7808 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7809 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7810 /* if we have focus, adjust rect */
7811 OffsetRect(&infoPtr->rcFocus, dx, dy);
7812 UpdateWindow(infoPtr->hwndSelf);
7817 * Performs vertical scrolling.
7820 * [I] infoPtr : valid pointer to the listview structure
7821 * [I] nScrollCode : scroll code
7822 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7823 * [I] hScrollWnd : scrollbar control window handle
7829 * SB_LINEUP/SB_LINEDOWN:
7830 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7831 * for LVS_REPORT is 1 line
7832 * for LVS_LIST cannot occur
7835 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7836 INT nScrollDiff, HWND hScrollWnd)
7838 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7839 INT nOldScrollPos, nNewScrollPos;
7840 SCROLLINFO scrollInfo;
7843 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7844 debugscrollcode(nScrollCode), nScrollDiff);
7846 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7848 scrollInfo.cbSize = sizeof(SCROLLINFO);
7849 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7851 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7853 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7855 nOldScrollPos = scrollInfo.nPos;
7856 switch (nScrollCode)
7862 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7866 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7870 nScrollDiff = -scrollInfo.nPage;
7874 nScrollDiff = scrollInfo.nPage;
7877 case SB_THUMBPOSITION:
7879 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7886 /* quit right away if pos isn't changing */
7887 if (nScrollDiff == 0) return 0;
7889 /* calculate new position, and handle overflows */
7890 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7891 if (nScrollDiff > 0) {
7892 if (nNewScrollPos < nOldScrollPos ||
7893 nNewScrollPos > scrollInfo.nMax)
7894 nNewScrollPos = scrollInfo.nMax;
7896 if (nNewScrollPos > nOldScrollPos ||
7897 nNewScrollPos < scrollInfo.nMin)
7898 nNewScrollPos = scrollInfo.nMin;
7901 /* set the new position, and reread in case it changed */
7902 scrollInfo.fMask = SIF_POS;
7903 scrollInfo.nPos = nNewScrollPos;
7904 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7906 /* carry on only if it really changed */
7907 if (nNewScrollPos == nOldScrollPos) return 0;
7909 /* now adjust to client coordinates */
7910 nScrollDiff = nOldScrollPos - nNewScrollPos;
7911 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7913 /* and scroll the window */
7914 scroll_list(infoPtr, 0, nScrollDiff);
7921 * Performs horizontal scrolling.
7924 * [I] infoPtr : valid pointer to the listview structure
7925 * [I] nScrollCode : scroll code
7926 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7927 * [I] hScrollWnd : scrollbar control window handle
7933 * SB_LINELEFT/SB_LINERIGHT:
7934 * for LVS_ICON, LVS_SMALLICON 1 pixel
7935 * for LVS_REPORT is 1 pixel
7936 * for LVS_LIST is 1 column --> which is a 1 because the
7937 * scroll is based on columns not pixels
7940 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7941 INT nScrollDiff, HWND hScrollWnd)
7943 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7944 INT nOldScrollPos, nNewScrollPos;
7945 SCROLLINFO scrollInfo;
7947 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7948 debugscrollcode(nScrollCode), nScrollDiff);
7950 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7952 scrollInfo.cbSize = sizeof(SCROLLINFO);
7953 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7955 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7957 nOldScrollPos = scrollInfo.nPos;
7959 switch (nScrollCode)
7973 nScrollDiff = -scrollInfo.nPage;
7977 nScrollDiff = scrollInfo.nPage;
7980 case SB_THUMBPOSITION:
7982 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7989 /* quit right away if pos isn't changing */
7990 if (nScrollDiff == 0) return 0;
7992 /* calculate new position, and handle overflows */
7993 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7994 if (nScrollDiff > 0) {
7995 if (nNewScrollPos < nOldScrollPos ||
7996 nNewScrollPos > scrollInfo.nMax)
7997 nNewScrollPos = scrollInfo.nMax;
7999 if (nNewScrollPos > nOldScrollPos ||
8000 nNewScrollPos < scrollInfo.nMin)
8001 nNewScrollPos = scrollInfo.nMin;
8004 /* set the new position, and reread in case it changed */
8005 scrollInfo.fMask = SIF_POS;
8006 scrollInfo.nPos = nNewScrollPos;
8007 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
8009 /* carry on only if it really changed */
8010 if (nNewScrollPos == nOldScrollPos) return 0;
8012 if(uView == LVS_REPORT)
8013 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
8015 /* now adjust to client coordinates */
8016 nScrollDiff = nOldScrollPos - nNewScrollPos;
8017 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
8019 /* and scroll the window */
8020 scroll_list(infoPtr, nScrollDiff, 0);
8025 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
8027 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8028 INT gcWheelDelta = 0;
8029 INT pulScrollLines = 3;
8030 SCROLLINFO scrollInfo;
8032 TRACE("(wheelDelta=%d)\n", wheelDelta);
8034 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8035 gcWheelDelta -= wheelDelta;
8037 scrollInfo.cbSize = sizeof(SCROLLINFO);
8038 scrollInfo.fMask = SIF_POS;
8045 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8046 * should be fixed in the future.
8048 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
8049 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8053 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8055 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
8056 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8057 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
8062 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8073 * [I] infoPtr : valid pointer to the listview structure
8074 * [I] nVirtualKey : virtual key
8075 * [I] lKeyData : key data
8080 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8082 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8083 HWND hwndSelf = infoPtr->hwndSelf;
8085 NMLVKEYDOWN nmKeyDown;
8087 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
8089 /* send LVN_KEYDOWN notification */
8090 nmKeyDown.wVKey = nVirtualKey;
8091 nmKeyDown.flags = 0;
8092 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8093 if (!IsWindow(hwndSelf))
8096 switch (nVirtualKey)
8099 nItem = infoPtr->nFocusedItem;
8103 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8105 if (!notify(infoPtr, NM_RETURN)) return 0;
8106 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8111 if (infoPtr->nItemCount > 0)
8116 if (infoPtr->nItemCount > 0)
8117 nItem = infoPtr->nItemCount - 1;
8121 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8125 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8129 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8133 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8137 if (uView == LVS_REPORT)
8139 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8140 if (infoPtr->nFocusedItem == topidx)
8141 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8146 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8147 * LISTVIEW_GetCountPerRow(infoPtr);
8148 if(nItem < 0) nItem = 0;
8152 if (uView == LVS_REPORT)
8154 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8155 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8156 if (infoPtr->nFocusedItem == topidx + cnt - 1)
8157 nItem = infoPtr->nFocusedItem + cnt - 1;
8159 nItem = topidx + cnt - 1;
8162 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8163 * LISTVIEW_GetCountPerRow(infoPtr);
8164 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8168 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8169 LISTVIEW_KeySelection(infoPtr, nItem);
8179 * [I] infoPtr : valid pointer to the listview structure
8184 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8188 /* if we did not have the focus, there's nothing to do */
8189 if (!infoPtr->bFocus) return 0;
8191 /* send NM_KILLFOCUS notification */
8192 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8194 /* if we have a focus rectagle, get rid of it */
8195 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8197 /* set window focus flag */
8198 infoPtr->bFocus = FALSE;
8200 /* invalidate the selected items before reseting focus flag */
8201 LISTVIEW_InvalidateSelectedItems(infoPtr);
8208 * Processes double click messages (left mouse button).
8211 * [I] infoPtr : valid pointer to the listview structure
8212 * [I] wKey : key flag
8213 * [I] x,y : mouse coordinate
8218 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8220 LVHITTESTINFO htInfo;
8222 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8224 /* send NM_RELEASEDCAPTURE notification */
8225 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8230 /* send NM_DBLCLK notification */
8231 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8232 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8234 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8235 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8242 * Processes mouse down messages (left mouse button).
8245 * infoPtr [I ] valid pointer to the listview structure
8246 * wKey [I ] key flag
8247 * x,y [I ] mouse coordinate
8252 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8254 LVHITTESTINFO lvHitTestInfo;
8255 static BOOL bGroupSelect = TRUE;
8256 POINT pt = { x, y };
8259 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8261 /* send NM_RELEASEDCAPTURE notification */
8262 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8264 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8266 /* set left button down flag and record the click position */
8267 infoPtr->bLButtonDown = TRUE;
8268 infoPtr->ptClickPos = pt;
8270 lvHitTestInfo.pt.x = x;
8271 lvHitTestInfo.pt.y = y;
8273 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8274 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
8275 infoPtr->nEditLabelItem = -1;
8276 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8278 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8280 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
8281 if(state == 1 || state == 2)
8285 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
8286 lvitem.stateMask = LVIS_STATEIMAGEMASK;
8287 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
8292 if (infoPtr->dwStyle & LVS_SINGLESEL)
8294 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8295 infoPtr->nEditLabelItem = nItem;
8297 LISTVIEW_SetSelection(infoPtr, nItem);
8301 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8305 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8306 LISTVIEW_SetItemFocus(infoPtr, nItem);
8307 infoPtr->nSelectionMark = nItem;
8313 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8314 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8316 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8317 infoPtr->nSelectionMark = nItem;
8320 else if (wKey & MK_CONTROL)
8324 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8326 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8327 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8328 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8329 infoPtr->nSelectionMark = nItem;
8331 else if (wKey & MK_SHIFT)
8333 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8337 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8338 infoPtr->nEditLabelItem = nItem;
8340 /* set selection (clears other pre-existing selections) */
8341 LISTVIEW_SetSelection(infoPtr, nItem);
8347 /* remove all selections */
8348 LISTVIEW_DeselectAll(infoPtr);
8357 * Processes mouse up messages (left mouse button).
8360 * infoPtr [I ] valid pointer to the listview structure
8361 * wKey [I ] key flag
8362 * x,y [I ] mouse coordinate
8367 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8369 LVHITTESTINFO lvHitTestInfo;
8371 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8373 if (!infoPtr->bLButtonDown) return 0;
8375 lvHitTestInfo.pt.x = x;
8376 lvHitTestInfo.pt.y = y;
8378 /* send NM_CLICK notification */
8379 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8380 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8382 /* set left button flag */
8383 infoPtr->bLButtonDown = FALSE;
8385 /* if we clicked on a selected item, edit the label */
8386 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8387 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
8394 * Destroys the listview control (called after WM_DESTROY).
8397 * [I] infoPtr : valid pointer to the listview structure
8402 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8406 /* delete all items */
8407 LISTVIEW_DeleteAllItems(infoPtr);
8409 /* destroy data structure */
8410 DPA_Destroy(infoPtr->hdpaItems);
8411 DPA_Destroy(infoPtr->hdpaPosX);
8412 DPA_Destroy(infoPtr->hdpaPosY);
8413 DPA_Destroy(infoPtr->hdpaColumns);
8414 ranges_destroy(infoPtr->selectionRanges);
8416 /* destroy image lists */
8417 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8419 if (infoPtr->himlNormal)
8420 ImageList_Destroy(infoPtr->himlNormal);
8421 if (infoPtr->himlSmall)
8422 ImageList_Destroy(infoPtr->himlSmall);
8423 if (infoPtr->himlState)
8424 ImageList_Destroy(infoPtr->himlState);
8427 /* destroy font, bkgnd brush */
8429 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8430 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8432 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8434 /* free listview info pointer*/
8442 * Handles notifications from header.
8445 * [I] infoPtr : valid pointer to the listview structure
8446 * [I] nCtrlId : control identifier
8447 * [I] lpnmh : notification information
8452 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8454 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8455 HWND hwndSelf = infoPtr->hwndSelf;
8457 TRACE("(lpnmh=%p)\n", lpnmh);
8459 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8461 switch (lpnmh->hdr.code)
8466 COLUMN_INFO *lpColumnInfo;
8470 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8473 /* remove the old line (if any) */
8474 LISTVIEW_DrawTrackLine(infoPtr);
8476 /* compute & draw the new line */
8477 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8478 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
8479 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8480 infoPtr->xTrackLine = x + ptOrigin.x;
8481 LISTVIEW_DrawTrackLine(infoPtr);
8487 /* remove the track line (if any) */
8488 LISTVIEW_DrawTrackLine(infoPtr);
8489 infoPtr->xTrackLine = -1;
8493 FIXME("Changing column order not implemented\n");
8496 case HDN_ITEMCHANGINGW:
8497 case HDN_ITEMCHANGINGA:
8498 return notify_forward_header(infoPtr, lpnmh);
8500 case HDN_ITEMCHANGEDW:
8501 case HDN_ITEMCHANGEDA:
8503 COLUMN_INFO *lpColumnInfo;
8506 notify_forward_header(infoPtr, lpnmh);
8507 if (!IsWindow(hwndSelf))
8510 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8514 hdi.mask = HDI_WIDTH;
8515 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, &hdi)) return 0;
8519 cxy = lpnmh->pitem->cxy;
8521 /* determine how much we change since the last know position */
8522 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8523 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8526 lpColumnInfo->rcHeader.right += dx;
8527 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8528 LISTVIEW_UpdateItemSize(infoPtr);
8529 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8532 RECT rcCol = lpColumnInfo->rcHeader;
8534 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8535 OffsetRect(&rcCol, ptOrigin.x, 0);
8537 rcCol.top = infoPtr->rcList.top;
8538 rcCol.bottom = infoPtr->rcList.bottom;
8540 /* resizing left-aligned columns leaves most of the left side untouched */
8541 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8543 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth + dx;
8544 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
8547 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8553 case HDN_ITEMCLICKW:
8554 case HDN_ITEMCLICKA:
8556 /* Handle sorting by Header Column */
8559 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8561 nmlv.iSubItem = lpnmh->iItem;
8562 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8566 case HDN_DIVIDERDBLCLICKW:
8567 case HDN_DIVIDERDBLCLICKA:
8568 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
8577 * Paint non-client area of control.
8580 * [I] infoPtr : valid pointer to the listview structureof the sender
8581 * [I] region : update region
8584 * TRUE - frame was painted
8585 * FALSE - call default window proc
8587 static BOOL LISTVIEW_NCPaint(LISTVIEW_INFO *infoPtr, HRGN region)
8589 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
8593 int cxEdge = GetSystemMetrics (SM_CXEDGE),
8594 cyEdge = GetSystemMetrics (SM_CYEDGE);
8596 if (!theme) return FALSE;
8598 GetWindowRect(infoPtr->hwndSelf, &r);
8600 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
8601 r.right - cxEdge, r.bottom - cyEdge);
8602 if (region != (HRGN)1)
8603 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
8604 OffsetRect(&r, -r.left, -r.top);
8606 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
8607 OffsetRect(&r, -r.left, -r.top);
8609 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
8610 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
8611 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
8612 ReleaseDC(infoPtr->hwndSelf, dc);
8614 /* Call default proc to get the scrollbars etc. painted */
8615 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
8622 * Determines the type of structure to use.
8625 * [I] infoPtr : valid pointer to the listview structureof the sender
8626 * [I] hwndFrom : listview window handle
8627 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8632 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8634 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8636 if (nCommand != NF_REQUERY) return 0;
8638 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8645 * Paints/Repaints the listview control.
8648 * [I] infoPtr : valid pointer to the listview structure
8649 * [I] hdc : device context handle
8654 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8656 TRACE("(hdc=%p)\n", hdc);
8658 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8660 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8662 infoPtr->bNoItemMetrics = FALSE;
8663 LISTVIEW_UpdateItemSize(infoPtr);
8664 if (uView == LVS_ICON || uView == LVS_SMALLICON)
8665 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8666 LISTVIEW_UpdateScroll(infoPtr);
8669 LISTVIEW_Refresh(infoPtr, hdc);
8674 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8676 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8677 LISTVIEW_Refresh(infoPtr, hdc);
8678 EndPaint(infoPtr->hwndSelf, &ps);
8687 * Paints/Repaints the listview control.
8690 * [I] infoPtr : valid pointer to the listview structure
8691 * [I] hdc : device context handle
8692 * [I] options : drawing options
8697 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
8699 FIXME("Partial Stub: (hdc=%p options=0x%08lx)\n", hdc, options);
8701 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
8704 if (options & PRF_ERASEBKGND)
8705 LISTVIEW_EraseBkgnd(infoPtr, hdc);
8707 if (options & PRF_CLIENT)
8708 LISTVIEW_Paint(infoPtr, hdc);
8716 * Processes double click messages (right mouse button).
8719 * [I] infoPtr : valid pointer to the listview structure
8720 * [I] wKey : key flag
8721 * [I] x,y : mouse coordinate
8726 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8728 LVHITTESTINFO lvHitTestInfo;
8730 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8732 /* send NM_RELEASEDCAPTURE notification */
8733 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8735 /* send NM_RDBLCLK notification */
8736 lvHitTestInfo.pt.x = x;
8737 lvHitTestInfo.pt.y = y;
8738 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8739 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8746 * Processes mouse down messages (right mouse button).
8749 * [I] infoPtr : valid pointer to the listview structure
8750 * [I] wKey : key flag
8751 * [I] x,y : mouse coordinate
8756 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8758 LVHITTESTINFO lvHitTestInfo;
8761 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8763 /* send NM_RELEASEDCAPTURE notification */
8764 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8766 /* make sure the listview control window has the focus */
8767 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8769 /* set right button down flag */
8770 infoPtr->bRButtonDown = TRUE;
8772 /* determine the index of the selected item */
8773 lvHitTestInfo.pt.x = x;
8774 lvHitTestInfo.pt.y = y;
8775 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8777 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8779 LISTVIEW_SetItemFocus(infoPtr, nItem);
8780 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8781 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8782 LISTVIEW_SetSelection(infoPtr, nItem);
8786 LISTVIEW_DeselectAll(infoPtr);
8794 * Processes mouse up messages (right mouse button).
8797 * [I] infoPtr : valid pointer to the listview structure
8798 * [I] wKey : key flag
8799 * [I] x,y : mouse coordinate
8804 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8806 LVHITTESTINFO lvHitTestInfo;
8809 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8811 if (!infoPtr->bRButtonDown) return 0;
8813 /* set button flag */
8814 infoPtr->bRButtonDown = FALSE;
8816 /* Send NM_RClICK notification */
8817 lvHitTestInfo.pt.x = x;
8818 lvHitTestInfo.pt.y = y;
8819 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8820 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
8822 /* Change to screen coordinate for WM_CONTEXTMENU */
8823 pt = lvHitTestInfo.pt;
8824 ClientToScreen(infoPtr->hwndSelf, &pt);
8826 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8827 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8828 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8839 * [I] infoPtr : valid pointer to the listview structure
8840 * [I] hwnd : window handle of window containing the cursor
8841 * [I] nHittest : hit-test code
8842 * [I] wMouseMsg : ideintifier of the mouse message
8845 * TRUE if cursor is set
8848 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8850 LVHITTESTINFO lvHitTestInfo;
8852 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8854 if(!infoPtr->hHotCursor) return FALSE;
8856 GetCursorPos(&lvHitTestInfo.pt);
8857 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8859 SetCursor(infoPtr->hHotCursor);
8869 * [I] infoPtr : valid pointer to the listview structure
8870 * [I] hwndLoseFocus : handle of previously focused window
8875 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8877 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8879 /* if we have the focus already, there's nothing to do */
8880 if (infoPtr->bFocus) return 0;
8882 /* send NM_SETFOCUS notification */
8883 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
8885 /* set window focus flag */
8886 infoPtr->bFocus = TRUE;
8888 /* put the focus rect back on */
8889 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8891 /* redraw all visible selected items */
8892 LISTVIEW_InvalidateSelectedItems(infoPtr);
8902 * [I] infoPtr : valid pointer to the listview structure
8903 * [I] fRedraw : font handle
8904 * [I] fRedraw : redraw flag
8909 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8911 HFONT oldFont = infoPtr->hFont;
8913 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8915 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8916 if (infoPtr->hFont == oldFont) return 0;
8918 LISTVIEW_SaveTextMetrics(infoPtr);
8920 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8921 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8923 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8930 * Message handling for WM_SETREDRAW.
8931 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8934 * [I] infoPtr : valid pointer to the listview structure
8935 * [I] bRedraw: state of redraw flag
8938 * DefWinProc return value
8940 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8942 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8944 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
8945 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8947 infoPtr->bRedraw = bRedraw;
8949 if(!bRedraw) return 0;
8951 if (is_autoarrange(infoPtr))
8952 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8953 LISTVIEW_UpdateScroll(infoPtr);
8955 /* despite what the WM_SETREDRAW docs says, apps expect us
8956 * to invalidate the listview here... stupid! */
8957 LISTVIEW_InvalidateList(infoPtr);
8964 * Resizes the listview control. This function processes WM_SIZE
8965 * messages. At this time, the width and height are not used.
8968 * [I] infoPtr : valid pointer to the listview structure
8969 * [I] Width : new width
8970 * [I] Height : new height
8975 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8977 RECT rcOld = infoPtr->rcList;
8979 TRACE("(width=%d, height=%d)\n", Width, Height);
8981 LISTVIEW_UpdateSize(infoPtr);
8982 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8984 /* do not bother with display related stuff if we're not redrawing */
8985 if (!is_redrawing(infoPtr)) return 0;
8987 if (is_autoarrange(infoPtr))
8988 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8990 LISTVIEW_UpdateScroll(infoPtr);
8992 /* refresh all only for lists whose height changed significantly */
8993 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
8994 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8995 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8996 LISTVIEW_InvalidateList(infoPtr);
9003 * Sets the size information.
9006 * [I] infoPtr : valid pointer to the listview structure
9011 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
9013 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9015 TRACE("uView=%d, rcList(old)=%s\n", uView, wine_dbgstr_rect(&infoPtr->rcList));
9017 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
9019 if (uView == LVS_LIST)
9021 /* Apparently the "LIST" style is supposed to have the same
9022 * number of items in a column even if there is no scroll bar.
9023 * Since if a scroll bar already exists then the bottom is already
9024 * reduced, only reduce if the scroll bar does not currently exist.
9025 * The "2" is there to mimic the native control. I think it may be
9026 * related to either padding or edges. (GLA 7/2002)
9028 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
9029 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
9030 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
9032 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
9037 hl.prc = &infoPtr->rcList;
9039 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9041 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9043 infoPtr->rcList.top = max(wp.cy, 0);
9046 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
9051 * Processes WM_STYLECHANGED messages.
9054 * [I] infoPtr : valid pointer to the listview structure
9055 * [I] wStyleType : window style type (normal or extended)
9056 * [I] lpss : window style information
9061 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9062 const STYLESTRUCT *lpss)
9064 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9065 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9067 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
9068 wStyleType, lpss->styleOld, lpss->styleNew);
9070 if (wStyleType != GWL_STYLE) return 0;
9072 /* FIXME: if LVS_NOSORTHEADER changed, update header */
9073 /* what if LVS_OWNERDATA changed? */
9074 /* or LVS_SINGLESEL */
9075 /* or LVS_SORT{AS,DES}CENDING */
9077 infoPtr->dwStyle = lpss->styleNew;
9079 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9080 ((lpss->styleNew & WS_HSCROLL) == 0))
9081 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9083 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9084 ((lpss->styleNew & WS_VSCROLL) == 0))
9085 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9087 if (uNewView != uOldView)
9089 SIZE oldIconSize = infoPtr->iconSize;
9092 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9093 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9095 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9096 SetRectEmpty(&infoPtr->rcFocus);
9098 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9099 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
9101 if (uNewView == LVS_ICON)
9103 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
9105 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
9106 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9107 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
9110 else if (uNewView == LVS_REPORT)
9115 hl.prc = &infoPtr->rcList;
9117 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9118 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9121 LISTVIEW_UpdateItemSize(infoPtr);
9124 if (uNewView == LVS_REPORT)
9125 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
9127 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9128 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9129 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9131 /* update the size of the client area */
9132 LISTVIEW_UpdateSize(infoPtr);
9134 /* add scrollbars if needed */
9135 LISTVIEW_UpdateScroll(infoPtr);
9137 /* invalidate client area + erase background */
9138 LISTVIEW_InvalidateList(infoPtr);
9145 * Window procedure of the listview control.
9148 static LRESULT WINAPI
9149 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9151 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9153 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
9155 if (!infoPtr && (uMsg != WM_CREATE))
9156 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9160 case LVM_APPROXIMATEVIEWRECT:
9161 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9162 LOWORD(lParam), HIWORD(lParam));
9164 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9166 /* case LVM_CANCELEDITLABEL: */
9168 case LVM_CREATEDRAGIMAGE:
9169 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9171 case LVM_DELETEALLITEMS:
9172 return LISTVIEW_DeleteAllItems(infoPtr);
9174 case LVM_DELETECOLUMN:
9175 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9177 case LVM_DELETEITEM:
9178 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9180 case LVM_EDITLABELW:
9181 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9183 case LVM_EDITLABELA:
9184 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9186 /* case LVM_ENABLEGROUPVIEW: */
9188 case LVM_ENSUREVISIBLE:
9189 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9192 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9195 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9197 case LVM_GETBKCOLOR:
9198 return infoPtr->clrBk;
9200 /* case LVM_GETBKIMAGE: */
9202 case LVM_GETCALLBACKMASK:
9203 return infoPtr->uCallbackMask;
9205 case LVM_GETCOLUMNA:
9206 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9208 case LVM_GETCOLUMNW:
9209 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9211 case LVM_GETCOLUMNORDERARRAY:
9212 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9214 case LVM_GETCOLUMNWIDTH:
9215 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9217 case LVM_GETCOUNTPERPAGE:
9218 return LISTVIEW_GetCountPerPage(infoPtr);
9220 case LVM_GETEDITCONTROL:
9221 return (LRESULT)infoPtr->hwndEdit;
9223 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9224 return infoPtr->dwLvExStyle;
9226 /* case LVM_GETGROUPINFO: */
9228 /* case LVM_GETGROUPMETRICS: */
9231 return (LRESULT)infoPtr->hwndHeader;
9233 case LVM_GETHOTCURSOR:
9234 return (LRESULT)infoPtr->hHotCursor;
9236 case LVM_GETHOTITEM:
9237 return infoPtr->nHotItem;
9239 case LVM_GETHOVERTIME:
9240 return infoPtr->dwHoverTime;
9242 case LVM_GETIMAGELIST:
9243 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9245 /* case LVM_GETINSERTMARK: */
9247 /* case LVM_GETINSERTMARKCOLOR: */
9249 /* case LVM_GETINSERTMARKRECT: */
9251 case LVM_GETISEARCHSTRINGA:
9252 case LVM_GETISEARCHSTRINGW:
9253 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9257 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9260 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9262 case LVM_GETITEMCOUNT:
9263 return infoPtr->nItemCount;
9265 case LVM_GETITEMPOSITION:
9266 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9268 case LVM_GETITEMRECT:
9269 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9271 case LVM_GETITEMSPACING:
9272 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9274 case LVM_GETITEMSTATE:
9275 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9277 case LVM_GETITEMTEXTA:
9278 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9280 case LVM_GETITEMTEXTW:
9281 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9283 case LVM_GETNEXTITEM:
9284 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9286 case LVM_GETNUMBEROFWORKAREAS:
9287 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9291 if (!lParam) return FALSE;
9292 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9295 /* case LVM_GETOUTLINECOLOR: */
9297 /* case LVM_GETSELECTEDCOLUMN: */
9299 case LVM_GETSELECTEDCOUNT:
9300 return LISTVIEW_GetSelectedCount(infoPtr);
9302 case LVM_GETSELECTIONMARK:
9303 return infoPtr->nSelectionMark;
9305 case LVM_GETSTRINGWIDTHA:
9306 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9308 case LVM_GETSTRINGWIDTHW:
9309 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9311 case LVM_GETSUBITEMRECT:
9312 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
9314 case LVM_GETTEXTBKCOLOR:
9315 return infoPtr->clrTextBk;
9317 case LVM_GETTEXTCOLOR:
9318 return infoPtr->clrText;
9320 /* case LVM_GETTILEINFO: */
9322 /* case LVM_GETTILEVIEWINFO: */
9324 case LVM_GETTOOLTIPS:
9325 if( !infoPtr->hwndToolTip )
9326 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9327 return (LRESULT)infoPtr->hwndToolTip;
9329 case LVM_GETTOPINDEX:
9330 return LISTVIEW_GetTopIndex(infoPtr);
9332 /*case LVM_GETUNICODEFORMAT:
9333 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9336 /* case LVM_GETVIEW: */
9338 case LVM_GETVIEWRECT:
9339 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9341 case LVM_GETWORKAREAS:
9342 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9345 /* case LVM_HASGROUP: */
9348 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9350 case LVM_INSERTCOLUMNA:
9351 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9353 case LVM_INSERTCOLUMNW:
9354 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9356 /* case LVM_INSERTGROUP: */
9358 /* case LVM_INSERTGROUPSORTED: */
9360 case LVM_INSERTITEMA:
9361 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9363 case LVM_INSERTITEMW:
9364 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9366 /* case LVM_INSERTMARKHITTEST: */
9368 /* case LVM_ISGROUPVIEWENABLED: */
9370 /* case LVM_MAPIDTOINDEX: */
9372 /* case LVM_MAPINDEXTOID: */
9374 /* case LVM_MOVEGROUP: */
9376 /* case LVM_MOVEITEMTOGROUP: */
9378 case LVM_REDRAWITEMS:
9379 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9381 /* case LVM_REMOVEALLGROUPS: */
9383 /* case LVM_REMOVEGROUP: */
9386 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9388 case LVM_SETBKCOLOR:
9389 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9391 /* case LVM_SETBKIMAGE: */
9393 case LVM_SETCALLBACKMASK:
9394 infoPtr->uCallbackMask = (UINT)wParam;
9397 case LVM_SETCOLUMNA:
9398 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9400 case LVM_SETCOLUMNW:
9401 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9403 case LVM_SETCOLUMNORDERARRAY:
9404 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9406 case LVM_SETCOLUMNWIDTH:
9407 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9409 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9410 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9412 /* case LVM_SETGROUPINFO: */
9414 /* case LVM_SETGROUPMETRICS: */
9416 case LVM_SETHOTCURSOR:
9417 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9419 case LVM_SETHOTITEM:
9420 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9422 case LVM_SETHOVERTIME:
9423 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9425 case LVM_SETICONSPACING:
9426 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9428 case LVM_SETIMAGELIST:
9429 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9431 /* case LVM_SETINFOTIP: */
9433 /* case LVM_SETINSERTMARK: */
9435 /* case LVM_SETINSERTMARKCOLOR: */
9438 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9441 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9443 case LVM_SETITEMCOUNT:
9444 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9446 case LVM_SETITEMPOSITION:
9449 pt.x = (short)LOWORD(lParam);
9450 pt.y = (short)HIWORD(lParam);
9451 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9454 case LVM_SETITEMPOSITION32:
9455 if (lParam == 0) return FALSE;
9456 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9458 case LVM_SETITEMSTATE:
9459 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9461 case LVM_SETITEMTEXTA:
9462 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9464 case LVM_SETITEMTEXTW:
9465 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9467 /* case LVM_SETOUTLINECOLOR: */
9469 /* case LVM_SETSELECTEDCOLUMN: */
9471 case LVM_SETSELECTIONMARK:
9472 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9474 case LVM_SETTEXTBKCOLOR:
9475 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9477 case LVM_SETTEXTCOLOR:
9478 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9480 /* case LVM_SETTILEINFO: */
9482 /* case LVM_SETTILEVIEWINFO: */
9484 /* case LVM_SETTILEWIDTH: */
9486 case LVM_SETTOOLTIPS:
9487 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9489 /* case LVM_SETUNICODEFORMAT: */
9491 /* case LVM_SETVIEW: */
9493 /* case LVM_SETWORKAREAS: */
9495 /* case LVM_SORTGROUPS: */
9498 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9500 /* LVM_SORTITEMSEX: */
9502 case LVM_SUBITEMHITTEST:
9503 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9506 return LISTVIEW_Update(infoPtr, (INT)wParam);
9509 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9512 return LISTVIEW_Command(infoPtr, wParam, lParam);
9515 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9518 return LISTVIEW_Destroy(infoPtr);
9521 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
9524 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9527 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9530 return (LRESULT)infoPtr->hFont;
9533 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9536 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9539 return LISTVIEW_KillFocus(infoPtr);
9541 case WM_LBUTTONDBLCLK:
9542 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9544 case WM_LBUTTONDOWN:
9545 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9548 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9551 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9554 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9557 return LISTVIEW_NCDestroy(infoPtr);
9560 if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
9565 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9566 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9569 case WM_NOTIFYFORMAT:
9570 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9572 case WM_PRINTCLIENT:
9573 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
9576 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9578 case WM_RBUTTONDBLCLK:
9579 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9581 case WM_RBUTTONDOWN:
9582 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9585 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9588 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9593 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9596 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9599 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9602 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9604 case WM_STYLECHANGED:
9605 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9607 case WM_SYSCOLORCHANGE:
9608 COMCTL32_RefreshSysColors();
9611 /* case WM_TIMER: */
9612 case WM_THEMECHANGED:
9613 return LISTVIEW_ThemeChanged(infoPtr);
9616 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9619 if (wParam & (MK_SHIFT | MK_CONTROL))
9620 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9621 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9623 case WM_WINDOWPOSCHANGED:
9624 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
9626 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9627 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9628 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9630 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
9632 MEASUREITEMSTRUCT mis;
9633 mis.CtlType = ODT_LISTVIEW;
9634 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
9638 mis.itemHeight= infoPtr->nItemHeight;
9639 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
9640 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
9641 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
9644 LISTVIEW_UpdateSize(infoPtr);
9645 LISTVIEW_UpdateScroll(infoPtr);
9647 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9649 /* case WM_WININICHANGE: */
9652 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9653 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9656 /* call default window procedure */
9657 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9664 * Registers the window class.
9672 void LISTVIEW_Register(void)
9676 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9677 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9678 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
9679 wndClass.cbClsExtra = 0;
9680 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9681 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9682 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9683 wndClass.lpszClassName = WC_LISTVIEWW;
9684 RegisterClassW(&wndClass);
9689 * Unregisters the window class.
9697 void LISTVIEW_Unregister(void)
9699 UnregisterClassW(WC_LISTVIEWW, NULL);
9704 * Handle any WM_COMMAND messages
9707 * [I] infoPtr : valid pointer to the listview structure
9708 * [I] wParam : the first message parameter
9709 * [I] lParam : the second message parameter
9714 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9716 switch (HIWORD(wParam))
9721 * Adjust the edit window size
9724 HDC hdc = GetDC(infoPtr->hwndEdit);
9725 HFONT hFont, hOldFont = 0;
9730 if (!infoPtr->hwndEdit || !hdc) return 0;
9731 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9732 GetWindowRect(infoPtr->hwndEdit, &rect);
9734 /* Select font to get the right dimension of the string */
9735 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9738 hOldFont = SelectObject(hdc, hFont);
9741 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9743 TEXTMETRICW textMetric;
9745 /* Add Extra spacing for the next character */
9746 GetTextMetricsW(hdc, &textMetric);
9747 sz.cx += (textMetric.tmMaxCharWidth * 2);
9755 rect.bottom - rect.top,
9756 SWP_DRAWFRAME|SWP_NOMOVE);
9759 SelectObject(hdc, hOldFont);
9761 ReleaseDC(infoPtr->hwndEdit, hdc);
9767 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
9776 * Subclassed edit control windproc function
9779 * [I] hwnd : the edit window handle
9780 * [I] uMsg : the message that is to be processed
9781 * [I] wParam : first message parameter
9782 * [I] lParam : second message parameter
9783 * [I] isW : TRUE if input is Unicode
9788 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9790 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
9791 BOOL cancel = FALSE;
9793 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9794 hwnd, uMsg, wParam, lParam, isW);
9799 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9806 WNDPROC editProc = infoPtr->EditWndProc;
9807 infoPtr->EditWndProc = 0;
9808 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
9809 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9813 if (VK_ESCAPE == (INT)wParam)
9818 else if (VK_RETURN == (INT)wParam)
9822 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9826 if (infoPtr->hwndEdit)
9828 LPWSTR buffer = NULL;
9830 infoPtr->hwndEdit = 0;
9833 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9837 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9839 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9840 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9844 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9846 if (buffer) Free(buffer);
9850 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9856 * Subclassed edit control Unicode windproc function
9859 * [I] hwnd : the edit window handle
9860 * [I] uMsg : the message that is to be processed
9861 * [I] wParam : first message parameter
9862 * [I] lParam : second message parameter
9866 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9868 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9873 * Subclassed edit control ANSI windproc function
9876 * [I] hwnd : the edit window handle
9877 * [I] uMsg : the message that is to be processed
9878 * [I] wParam : first message parameter
9879 * [I] lParam : second message parameter
9883 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9885 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9890 * Creates a subclassed edit cotrol
9893 * [I] infoPtr : valid pointer to the listview structure
9894 * [I] text : initial text for the edit
9895 * [I] style : the window style
9896 * [I] isW : TRUE if input is Unicode
9900 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9901 INT x, INT y, INT width, INT height, BOOL isW)
9903 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9908 TEXTMETRICW textMetric;
9909 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
9911 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9913 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
9914 hdc = GetDC(infoPtr->hwndSelf);
9916 /* Select the font to get appropriate metric dimensions */
9917 if(infoPtr->hFont != 0)
9918 hOldFont = SelectObject(hdc, infoPtr->hFont);
9920 /*Get String Length in pixels */
9921 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9923 /*Add Extra spacing for the next character */
9924 GetTextMetricsW(hdc, &textMetric);
9925 sz.cx += (textMetric.tmMaxCharWidth * 2);
9927 if(infoPtr->hFont != 0)
9928 SelectObject(hdc, hOldFont);
9930 ReleaseDC(infoPtr->hwndSelf, hdc);
9932 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9934 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9936 if (!hedit) return 0;
9938 infoPtr->EditWndProc = (WNDPROC)
9939 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
9940 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
9942 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);