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_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3774 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3777 /* Don't bother painting item being edited */
3778 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3780 /* draw the selection background, if we're drawing the main item */
3784 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3785 rcSelect.right = rcBox.right;
3787 if (nmlvcd.clrTextBk != CLR_NONE)
3788 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3789 if(lprcFocus) *lprcFocus = rcSelect;
3792 /* figure out the text drawing flags */
3793 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3794 if (uView == LVS_ICON)
3795 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3798 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3800 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3801 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3802 default: uFormat |= DT_LEFT;
3805 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3807 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3808 else rcLabel.left += LABEL_HOR_PADDING;
3810 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3811 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3814 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3815 notify_postpaint(infoPtr, &nmlvcd);
3816 if (cdsubitemmode & CDRF_NEWFONT)
3817 SelectObject(hdc, hOldFont);
3823 * Draws listview items when in owner draw mode.
3826 * [I] infoPtr : valid pointer to the listview structure
3827 * [I] hdc : device context handle
3832 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3834 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3835 DWORD cditemmode = CDRF_DODEFAULT;
3836 NMLVCUSTOMDRAW nmlvcd;
3837 POINT Origin, Position;
3843 ZeroMemory(&dis, sizeof(dis));
3845 /* Get scroll info once before loop */
3846 LISTVIEW_GetOrigin(infoPtr, &Origin);
3848 /* iterate through the invalidated rows */
3849 while(iterator_next(i))
3851 item.iItem = i->nItem;
3853 item.mask = LVIF_PARAM | LVIF_STATE;
3854 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3855 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3857 dis.CtlType = ODT_LISTVIEW;
3859 dis.itemID = item.iItem;
3860 dis.itemAction = ODA_DRAWENTIRE;
3862 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3863 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3864 dis.hwndItem = infoPtr->hwndSelf;
3866 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3867 dis.rcItem.left = Position.x + Origin.x;
3868 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3869 dis.rcItem.top = Position.y + Origin.y;
3870 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3871 dis.itemData = item.lParam;
3873 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
3876 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3877 * structure for the rest. of the paint cycle
3879 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3880 if (cdmode & CDRF_NOTIFYITEMDRAW)
3881 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3883 if (!(cditemmode & CDRF_SKIPDEFAULT))
3885 prepaint_setup (infoPtr, hdc, &nmlvcd);
3886 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3889 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3890 notify_postpaint(infoPtr, &nmlvcd);
3896 * Draws listview items when in report display mode.
3899 * [I] infoPtr : valid pointer to the listview structure
3900 * [I] hdc : device context handle
3901 * [I] cdmode : custom draw mode
3906 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3909 RECT rcClip, rcItem;
3910 POINT Origin, Position;
3916 /* figure out what to draw */
3917 rgntype = GetClipBox(hdc, &rcClip);
3918 if (rgntype == NULLREGION) return;
3920 /* Get scroll info once before loop */
3921 LISTVIEW_GetOrigin(infoPtr, &Origin);
3923 /* narrow down the columns we need to paint */
3924 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3926 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3927 if (rcItem.right + Origin.x >= rcClip.left) break;
3929 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3931 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3932 if (rcItem.left + Origin.x < rcClip.right) break;
3934 iterator_rangeitems(&j, colRange);
3936 /* in full row select, we _have_ to draw the main item */
3937 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3940 /* iterate through the invalidated rows */
3941 while(iterator_next(i))
3943 /* iterate through the invalidated columns */
3944 while(iterator_next(&j))
3946 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3947 Position.x += Origin.x;
3948 Position.y += Origin.y;
3950 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3952 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3954 rcItem.bottom = infoPtr->nItemHeight;
3955 OffsetRect(&rcItem, Position.x, Position.y);
3956 if (!RectVisible(hdc, &rcItem)) continue;
3959 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3962 iterator_destroy(&j);
3967 * Draws listview items when in list display mode.
3970 * [I] infoPtr : valid pointer to the listview structure
3971 * [I] hdc : device context handle
3972 * [I] cdmode : custom draw mode
3977 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3979 POINT Origin, Position;
3981 /* Get scroll info once before loop */
3982 LISTVIEW_GetOrigin(infoPtr, &Origin);
3984 while(iterator_prev(i))
3986 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3987 Position.x += Origin.x;
3988 Position.y += Origin.y;
3990 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3997 * Draws listview items.
4000 * [I] infoPtr : valid pointer to the listview structure
4001 * [I] hdc : device context handle
4006 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
4008 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4009 COLORREF oldTextColor, oldClrTextBk, oldClrText;
4010 NMLVCUSTOMDRAW nmlvcd;
4017 LISTVIEW_DUMP(infoPtr);
4019 infoPtr->bIsDrawing = TRUE;
4021 /* save dc values we're gonna trash while drawing */
4022 hOldFont = SelectObject(hdc, infoPtr->hFont);
4023 oldBkMode = GetBkMode(hdc);
4024 infoPtr->clrTextBkDefault = GetBkColor(hdc);
4025 oldTextColor = GetTextColor(hdc);
4027 oldClrTextBk = infoPtr->clrTextBk;
4028 oldClrText = infoPtr->clrText;
4030 infoPtr->cditemmode = CDRF_DODEFAULT;
4032 GetClientRect(infoPtr->hwndSelf, &rcClient);
4033 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4034 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4035 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4036 prepaint_setup(infoPtr, hdc, &nmlvcd);
4038 /* Use these colors to draw the items */
4039 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4040 infoPtr->clrText = nmlvcd.clrText;
4042 /* nothing to draw */
4043 if(infoPtr->nItemCount == 0) goto enddraw;
4045 /* figure out what we need to draw */
4046 iterator_visibleitems(&i, infoPtr, hdc);
4048 /* send cache hint notification */
4049 if (infoPtr->dwStyle & LVS_OWNERDATA)
4051 RANGE range = iterator_range(&i);
4054 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4055 nmlv.iFrom = range.lower;
4056 nmlv.iTo = range.upper - 1;
4057 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4060 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4061 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4064 if (uView == LVS_REPORT)
4065 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4066 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4067 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4069 /* if we have a focus rect, draw it */
4070 if (infoPtr->bFocus)
4071 DrawFocusRect(hdc, &infoPtr->rcFocus);
4073 iterator_destroy(&i);
4076 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4077 notify_postpaint(infoPtr, &nmlvcd);
4079 infoPtr->clrTextBk = oldClrTextBk;
4080 infoPtr->clrText = oldClrText;
4082 SelectObject(hdc, hOldFont);
4083 SetBkMode(hdc, oldBkMode);
4084 SetBkColor(hdc, infoPtr->clrTextBkDefault);
4085 SetTextColor(hdc, oldTextColor);
4086 infoPtr->bIsDrawing = FALSE;
4092 * Calculates the approximate width and height of a given number of items.
4095 * [I] infoPtr : valid pointer to the listview structure
4096 * [I] nItemCount : number of items
4097 * [I] wWidth : width
4098 * [I] wHeight : height
4101 * Returns a DWORD. The width in the low word and the height in high word.
4103 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
4104 WORD wWidth, WORD wHeight)
4106 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4107 INT nItemCountPerColumn = 1;
4108 INT nColumnCount = 0;
4109 DWORD dwViewRect = 0;
4111 if (nItemCount == -1)
4112 nItemCount = infoPtr->nItemCount;
4114 if (uView == LVS_LIST)
4116 if (wHeight == 0xFFFF)
4118 /* use current height */
4119 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4122 if (wHeight < infoPtr->nItemHeight)
4123 wHeight = infoPtr->nItemHeight;
4127 if (infoPtr->nItemHeight > 0)
4129 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4130 if (nItemCountPerColumn == 0)
4131 nItemCountPerColumn = 1;
4133 if (nItemCount % nItemCountPerColumn != 0)
4134 nColumnCount = nItemCount / nItemCountPerColumn;
4136 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4140 /* Microsoft padding magic */
4141 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4142 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4144 dwViewRect = MAKELONG(wWidth, wHeight);
4146 else if (uView == LVS_REPORT)
4150 if (infoPtr->nItemCount > 0)
4152 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4153 wWidth = rcBox.right - rcBox.left;
4154 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4158 /* use current height and width */
4159 if (wHeight == 0xffff)
4160 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4161 if (wWidth == 0xffff)
4162 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4165 dwViewRect = MAKELONG(wWidth, wHeight);
4167 else if (uView == LVS_SMALLICON)
4168 FIXME("uView == LVS_SMALLICON: not implemented\n");
4169 else if (uView == LVS_ICON)
4170 FIXME("uView == LVS_ICON: not implemented\n");
4178 * Create a drag image list for the specified item.
4181 * [I] infoPtr : valid pointer to the listview structure
4182 * [I] iItem : index of item
4183 * [O] lppt : Upperr-left corner of the image
4186 * Returns a handle to the image list if successful, NULL otherwise.
4188 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4194 HBITMAP hbmp, hOldbmp;
4195 HIMAGELIST dragList = 0;
4196 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
4198 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4201 rcItem.left = LVIR_BOUNDS;
4202 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4205 lppt->x = rcItem.left;
4206 lppt->y = rcItem.top;
4208 size.cx = rcItem.right - rcItem.left;
4209 size.cy = rcItem.bottom - rcItem.top;
4211 hdcOrig = GetDC(infoPtr->hwndSelf);
4212 hdc = CreateCompatibleDC(hdcOrig);
4213 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4214 hOldbmp = SelectObject(hdc, hbmp);
4216 rcItem.left = rcItem.top = 0;
4217 rcItem.right = size.cx;
4218 rcItem.bottom = size.cy;
4219 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4222 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4224 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4225 SelectObject(hdc, hOldbmp);
4226 ImageList_Add(dragList, hbmp, 0);
4229 SelectObject(hdc, hOldbmp);
4233 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4235 TRACE("ret=%p\n", dragList);
4243 * Removes all listview items and subitems.
4246 * [I] infoPtr : valid pointer to the listview structure
4252 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4255 HDPA hdpaSubItems = NULL;
4262 /* we do it directly, to avoid notifications */
4263 ranges_clear(infoPtr->selectionRanges);
4264 infoPtr->nSelectionMark = -1;
4265 infoPtr->nFocusedItem = -1;
4266 SetRectEmpty(&infoPtr->rcFocus);
4267 /* But we are supposed to leave nHotItem as is! */
4270 /* send LVN_DELETEALLITEMS notification */
4271 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4273 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4275 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4277 /* send LVN_DELETEITEM notification, if not suppressed */
4278 if (!bSuppress) notify_deleteitem(infoPtr, i);
4279 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4281 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4282 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4284 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4285 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4288 DPA_Destroy(hdpaSubItems);
4289 DPA_DeletePtr(infoPtr->hdpaItems, i);
4291 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4292 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4293 infoPtr->nItemCount --;
4296 LISTVIEW_UpdateScroll(infoPtr);
4298 LISTVIEW_InvalidateList(infoPtr);
4305 * Scrolls, and updates the columns, when a column is changing width.
4308 * [I] infoPtr : valid pointer to the listview structure
4309 * [I] nColumn : column to scroll
4310 * [I] dx : amount of scroll, in pixels
4315 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4317 COLUMN_INFO *lpColumnInfo;
4322 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4323 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4324 rcCol = lpColumnInfo->rcHeader;
4325 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4326 rcCol.left = rcCol.right;
4328 /* ajust the other columns */
4329 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4331 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4332 lpColumnInfo->rcHeader.left += dx;
4333 lpColumnInfo->rcHeader.right += dx;
4336 /* do not update screen if not in report mode */
4337 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4339 /* if we have a focus, must first erase the focus rect */
4340 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4342 /* Need to reset the item width when inserting a new column */
4343 infoPtr->nItemWidth += dx;
4345 LISTVIEW_UpdateScroll(infoPtr);
4346 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4348 /* scroll to cover the deleted column, and invalidate for redraw */
4349 rcOld = infoPtr->rcList;
4350 rcOld.left = ptOrigin.x + rcCol.left + dx;
4351 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4353 /* we can restore focus now */
4354 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4359 * Removes a column from the listview control.
4362 * [I] infoPtr : valid pointer to the listview structure
4363 * [I] nColumn : column index
4369 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4373 TRACE("nColumn=%d\n", nColumn);
4375 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4376 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4378 /* While the MSDN specifically says that column zero should not be deleted,
4379 what actually happens is that the column itself is deleted but no items or subitems
4383 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4385 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4388 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4389 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4391 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4393 SUBITEM_INFO *lpSubItem, *lpDelItem;
4395 INT nItem, nSubItem, i;
4397 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4399 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4402 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4404 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4405 if (lpSubItem->iSubItem == nColumn)
4408 lpDelItem = lpSubItem;
4410 else if (lpSubItem->iSubItem > nColumn)
4412 lpSubItem->iSubItem--;
4416 /* if we found our subitem, zapp it */
4420 if (is_textW(lpDelItem->hdr.pszText))
4421 Free(lpDelItem->hdr.pszText);
4426 /* free dpa memory */
4427 DPA_DeletePtr(hdpaSubItems, nSubItem);
4432 /* update the other column info */
4433 LISTVIEW_UpdateItemSize(infoPtr);
4434 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4435 LISTVIEW_InvalidateList(infoPtr);
4437 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4444 * Invalidates the listview after an item's insertion or deletion.
4447 * [I] infoPtr : valid pointer to the listview structure
4448 * [I] nItem : item index
4449 * [I] dir : -1 if deleting, 1 if inserting
4454 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4456 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4457 INT nPerCol, nItemCol, nItemRow;
4461 /* if we don't refresh, what's the point of scrolling? */
4462 if (!is_redrawing(infoPtr)) return;
4464 assert (abs(dir) == 1);
4466 /* arrange icons if autoarrange is on */
4467 if (is_autoarrange(infoPtr))
4469 BOOL arrange = TRUE;
4470 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4471 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4472 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4475 /* scrollbars need updating */
4476 LISTVIEW_UpdateScroll(infoPtr);
4478 /* figure out the item's position */
4479 if (uView == LVS_REPORT)
4480 nPerCol = infoPtr->nItemCount + 1;
4481 else if (uView == LVS_LIST)
4482 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4483 else /* LVS_ICON, or LVS_SMALLICON */
4486 nItemCol = nItem / nPerCol;
4487 nItemRow = nItem % nPerCol;
4488 LISTVIEW_GetOrigin(infoPtr, &Origin);
4490 /* move the items below up a slot */
4491 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4492 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4493 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4494 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4495 OffsetRect(&rcScroll, Origin.x, Origin.y);
4496 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
4497 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4499 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
4500 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4501 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4504 /* report has only that column, so we're done */
4505 if (uView == LVS_REPORT) return;
4507 /* now for LISTs, we have to deal with the columns to the right */
4508 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4510 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4511 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4512 OffsetRect(&rcScroll, Origin.x, Origin.y);
4513 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4514 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4515 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4520 * Removes an item from the listview control.
4523 * [I] infoPtr : valid pointer to the listview structure
4524 * [I] nItem : item index
4530 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4532 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4535 TRACE("(nItem=%d)\n", nItem);
4537 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4539 /* remove selection, and focus */
4541 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4542 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4544 /* send LVN_DELETEITEM notification. */
4545 if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
4547 /* we need to do this here, because we'll be deleting stuff */
4548 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4549 LISTVIEW_InvalidateItem(infoPtr, nItem);
4551 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4557 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4558 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4560 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4561 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4564 DPA_Destroy(hdpaSubItems);
4567 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4569 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4570 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4573 infoPtr->nItemCount--;
4574 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4576 /* now is the invalidation fun */
4577 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4584 * Callback implementation for editlabel control
4587 * [I] infoPtr : valid pointer to the listview structure
4588 * [I] pszText : modified text
4589 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4595 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4597 HWND hwndSelf = infoPtr->hwndSelf;
4598 NMLVDISPINFOW dispInfo;
4600 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4602 ZeroMemory(&dispInfo, sizeof(dispInfo));
4603 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4604 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4605 dispInfo.item.iSubItem = 0;
4606 dispInfo.item.stateMask = ~0;
4607 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4608 /* add the text from the edit in */
4609 dispInfo.item.mask |= LVIF_TEXT;
4610 dispInfo.item.pszText = pszText;
4611 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4613 /* Do we need to update the Item Text */
4614 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4615 if (!IsWindow(hwndSelf))
4617 if (!pszText) return TRUE;
4619 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4621 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nEditLabelItem);
4622 ITEM_INFO* lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
4623 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4625 LISTVIEW_InvalidateItem(infoPtr, infoPtr->nEditLabelItem);
4630 ZeroMemory(&dispInfo, sizeof(dispInfo));
4631 dispInfo.item.mask = LVIF_TEXT;
4632 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4633 dispInfo.item.iSubItem = 0;
4634 dispInfo.item.pszText = pszText;
4635 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4636 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4641 * Begin in place editing of specified list view item
4644 * [I] infoPtr : valid pointer to the listview structure
4645 * [I] nItem : item index
4646 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4652 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4654 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4655 NMLVDISPINFOW dispInfo;
4657 HWND hwndSelf = infoPtr->hwndSelf;
4659 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4661 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4662 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4664 infoPtr->nEditLabelItem = nItem;
4666 /* Is the EditBox still there, if so remove it */
4667 if(infoPtr->hwndEdit != 0)
4669 SetFocus(infoPtr->hwndSelf);
4670 infoPtr->hwndEdit = 0;
4673 LISTVIEW_SetSelection(infoPtr, nItem);
4674 LISTVIEW_SetItemFocus(infoPtr, nItem);
4675 LISTVIEW_InvalidateItem(infoPtr, nItem);
4677 rect.left = LVIR_LABEL;
4678 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4680 ZeroMemory(&dispInfo, sizeof(dispInfo));
4681 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4682 dispInfo.item.iItem = nItem;
4683 dispInfo.item.iSubItem = 0;
4684 dispInfo.item.stateMask = ~0;
4685 dispInfo.item.pszText = szDispText;
4686 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4687 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4689 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4690 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4691 if (!infoPtr->hwndEdit) return 0;
4693 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4695 if (!IsWindow(hwndSelf))
4697 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4698 infoPtr->hwndEdit = 0;
4702 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4703 SetFocus(infoPtr->hwndEdit);
4704 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4705 return infoPtr->hwndEdit;
4711 * Ensures the specified item is visible, scrolling into view if necessary.
4714 * [I] infoPtr : valid pointer to the listview structure
4715 * [I] nItem : item index
4716 * [I] bPartial : partially or entirely visible
4722 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4724 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4725 INT nScrollPosHeight = 0;
4726 INT nScrollPosWidth = 0;
4727 INT nHorzAdjust = 0;
4728 INT nVertAdjust = 0;
4731 RECT rcItem, rcTemp;
4733 rcItem.left = LVIR_BOUNDS;
4734 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4736 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4738 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4740 /* scroll left/right, but in LVS_REPORT mode */
4741 if (uView == LVS_LIST)
4742 nScrollPosWidth = infoPtr->nItemWidth;
4743 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4744 nScrollPosWidth = 1;
4746 if (rcItem.left < infoPtr->rcList.left)
4749 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4754 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4758 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4760 /* scroll up/down, but not in LVS_LIST mode */
4761 if (uView == LVS_REPORT)
4762 nScrollPosHeight = infoPtr->nItemHeight;
4763 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4764 nScrollPosHeight = 1;
4766 if (rcItem.top < infoPtr->rcList.top)
4769 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4774 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4778 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4780 if (nScrollPosWidth)
4782 INT diff = nHorzDiff / nScrollPosWidth;
4783 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4784 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4787 if (nScrollPosHeight)
4789 INT diff = nVertDiff / nScrollPosHeight;
4790 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4791 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4799 * Searches for an item with specific characteristics.
4802 * [I] hwnd : window handle
4803 * [I] nStart : base item index
4804 * [I] lpFindInfo : item information to look for
4807 * SUCCESS : index of item
4810 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4811 const LVFINDINFOW *lpFindInfo)
4813 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4814 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4815 BOOL bWrap = FALSE, bNearest = FALSE;
4816 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4817 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4818 POINT Position, Destination;
4821 if (!lpFindInfo || nItem < 0) return -1;
4824 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4826 lvItem.mask |= LVIF_TEXT;
4827 lvItem.pszText = szDispText;
4828 lvItem.cchTextMax = DISP_TEXT_SIZE;
4831 if (lpFindInfo->flags & LVFI_WRAP)
4834 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4835 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4840 LISTVIEW_GetOrigin(infoPtr, &Origin);
4841 Destination.x = lpFindInfo->pt.x - Origin.x;
4842 Destination.y = lpFindInfo->pt.y - Origin.y;
4843 switch(lpFindInfo->vkDirection)
4845 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4846 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4847 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4848 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4849 case VK_HOME: Destination.x = Destination.y = 0; break;
4850 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4851 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4853 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4854 Destination.x = rcArea.right;
4855 Destination.y = rcArea.bottom;
4857 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4861 else Destination.x = Destination.y = 0;
4863 /* if LVFI_PARAM is specified, all other flags are ignored */
4864 if (lpFindInfo->flags & LVFI_PARAM)
4866 lvItem.mask |= LVIF_PARAM;
4868 lvItem.mask &= ~LVIF_TEXT;
4872 for (; nItem < nLast; nItem++)
4874 lvItem.iItem = nItem;
4875 lvItem.iSubItem = 0;
4876 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4878 if (lvItem.mask & LVIF_PARAM)
4880 if (lpFindInfo->lParam == lvItem.lParam)
4886 if (lvItem.mask & LVIF_TEXT)
4888 if (lpFindInfo->flags & LVFI_PARTIAL)
4890 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4894 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4898 if (!bNearest) return nItem;
4900 /* This is very inefficient. To do a good job here,
4901 * we need a sorted array of (x,y) item positions */
4902 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4904 /* compute the distance^2 to the destination */
4905 xdist = Destination.x - Position.x;
4906 ydist = Destination.y - Position.y;
4907 dist = xdist * xdist + ydist * ydist;
4909 /* remember the distance, and item if it's closer */
4913 nNearestItem = nItem;
4920 nLast = min(nStart + 1, infoPtr->nItemCount);
4925 return nNearestItem;
4930 * Searches for an item with specific characteristics.
4933 * [I] hwnd : window handle
4934 * [I] nStart : base item index
4935 * [I] lpFindInfo : item information to look for
4938 * SUCCESS : index of item
4941 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4942 const LVFINDINFOA *lpFindInfo)
4944 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4948 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4949 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4950 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4951 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4957 * Retrieves the background image of the listview control.
4960 * [I] infoPtr : valid pointer to the listview structure
4961 * [O] lpBkImage : background image attributes
4967 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4969 /* FIXME (listview, "empty stub!\n"); */
4975 * Retrieves column attributes.
4978 * [I] infoPtr : valid pointer to the listview structure
4979 * [I] nColumn : column index
4980 * [IO] lpColumn : column information
4981 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4982 * otherwise it is in fact a LPLVCOLUMNA
4988 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4990 COLUMN_INFO *lpColumnInfo;
4993 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4994 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4996 /* initialize memory */
4997 ZeroMemory(&hdi, sizeof(hdi));
4999 if (lpColumn->mask & LVCF_TEXT)
5001 hdi.mask |= HDI_TEXT;
5002 hdi.pszText = lpColumn->pszText;
5003 hdi.cchTextMax = lpColumn->cchTextMax;
5006 if (lpColumn->mask & LVCF_IMAGE)
5007 hdi.mask |= HDI_IMAGE;
5009 if (lpColumn->mask & LVCF_ORDER)
5010 hdi.mask |= HDI_ORDER;
5012 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
5014 if (lpColumn->mask & LVCF_FMT)
5015 lpColumn->fmt = lpColumnInfo->fmt;
5017 if (lpColumn->mask & LVCF_WIDTH)
5018 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
5020 if (lpColumn->mask & LVCF_IMAGE)
5021 lpColumn->iImage = hdi.iImage;
5023 if (lpColumn->mask & LVCF_ORDER)
5024 lpColumn->iOrder = hdi.iOrder;
5030 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5037 /* FIXME: little hack */
5038 for (i = 0; i < iCount; i++)
5046 * Retrieves the column width.
5049 * [I] infoPtr : valid pointer to the listview structure
5050 * [I] int : column index
5053 * SUCCESS : column width
5056 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
5058 INT nColumnWidth = 0;
5061 TRACE("nColumn=%d\n", nColumn);
5063 /* we have a 'column' in LIST and REPORT mode only */
5064 switch(infoPtr->dwStyle & LVS_TYPEMASK)
5067 nColumnWidth = infoPtr->nItemWidth;
5070 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
5071 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
5072 nColumnWidth = rcHeader.right - rcHeader.left;
5076 TRACE("nColumnWidth=%d\n", nColumnWidth);
5077 return nColumnWidth;
5082 * In list or report display mode, retrieves the number of items that can fit
5083 * vertically in the visible area. In icon or small icon display mode,
5084 * retrieves the total number of visible items.
5087 * [I] infoPtr : valid pointer to the listview structure
5090 * Number of fully visible items.
5092 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
5094 switch (infoPtr->dwStyle & LVS_TYPEMASK)
5098 return infoPtr->nItemCount;
5100 return LISTVIEW_GetCountPerColumn(infoPtr);
5102 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5110 * Retrieves an image list handle.
5113 * [I] infoPtr : valid pointer to the listview structure
5114 * [I] nImageList : image list identifier
5117 * SUCCESS : image list handle
5120 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
5124 case LVSIL_NORMAL: return infoPtr->himlNormal;
5125 case LVSIL_SMALL: return infoPtr->himlSmall;
5126 case LVSIL_STATE: return infoPtr->himlState;
5131 /* LISTVIEW_GetISearchString */
5135 * Retrieves item attributes.
5138 * [I] hwnd : window handle
5139 * [IO] lpLVItem : item info
5140 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5141 * if FALSE, the lpLVItem is a LPLVITEMA.
5144 * This is the internal 'GetItem' interface -- it tries to
5145 * be smart and avoid text copies, if possible, by modifying
5146 * lpLVItem->pszText to point to the text string. Please note
5147 * that this is not always possible (e.g. OWNERDATA), so on
5148 * entry you *must* supply valid values for pszText, and cchTextMax.
5149 * The only difference to the documented interface is that upon
5150 * return, you should use *only* the lpLVItem->pszText, rather than
5151 * the buffer pointer you provided on input. Most code already does
5152 * that, so it's not a problem.
5153 * For the two cases when the text must be copied (that is,
5154 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5160 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5162 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5163 NMLVDISPINFOW dispInfo;
5169 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5171 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5174 if (lpLVItem->mask == 0) return TRUE;
5176 /* make a local copy */
5177 isubitem = lpLVItem->iSubItem;
5179 /* a quick optimization if all we're asked is the focus state
5180 * these queries are worth optimising since they are common,
5181 * and can be answered in constant time, without the heavy accesses */
5182 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5183 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5185 lpLVItem->state = 0;
5186 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5187 lpLVItem->state |= LVIS_FOCUSED;
5191 ZeroMemory(&dispInfo, sizeof(dispInfo));
5193 /* if the app stores all the data, handle it separately */
5194 if (infoPtr->dwStyle & LVS_OWNERDATA)
5196 dispInfo.item.state = 0;
5198 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
5199 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5201 /* NOTE: copy only fields which we _know_ are initialized, some apps
5202 * depend on the uninitialized fields being 0 */
5203 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5204 dispInfo.item.iItem = lpLVItem->iItem;
5205 dispInfo.item.iSubItem = isubitem;
5206 if (lpLVItem->mask & LVIF_TEXT)
5208 dispInfo.item.pszText = lpLVItem->pszText;
5209 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5211 if (lpLVItem->mask & LVIF_STATE)
5212 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5213 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5214 dispInfo.item.stateMask = lpLVItem->stateMask;
5215 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5217 /* full size structure expected - _WIN32IE >= 0x560 */
5218 *lpLVItem = dispInfo.item;
5220 else if (lpLVItem->mask & LVIF_INDENT)
5222 /* indent member expected - _WIN32IE >= 0x300 */
5223 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5227 /* minimal structure expected */
5228 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5230 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5233 /* make sure lParam is zeroed out */
5234 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5236 /* we store only a little state, so if we're not asked, we're done */
5237 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5239 /* if focus is handled by us, report it */
5240 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5242 lpLVItem->state &= ~LVIS_FOCUSED;
5243 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5244 lpLVItem->state |= LVIS_FOCUSED;
5247 /* and do the same for selection, if we handle it */
5248 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5250 lpLVItem->state &= ~LVIS_SELECTED;
5251 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5252 lpLVItem->state |= LVIS_SELECTED;
5258 /* find the item and subitem structures before we proceed */
5259 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5260 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5265 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5266 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5269 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5274 pItemHdr = &lpItem->hdr;
5276 /* Do we need to query the state from the app? */
5277 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5279 dispInfo.item.mask |= LVIF_STATE;
5280 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5283 /* Do we need to enquire about the image? */
5284 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5285 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5287 dispInfo.item.mask |= LVIF_IMAGE;
5288 dispInfo.item.iImage = I_IMAGECALLBACK;
5291 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5292 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5294 dispInfo.item.mask |= LVIF_TEXT;
5295 dispInfo.item.pszText = lpLVItem->pszText;
5296 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5297 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5298 *dispInfo.item.pszText = '\0';
5301 /* If we don't have all the requested info, query the application */
5302 if (dispInfo.item.mask != 0)
5304 dispInfo.item.iItem = lpLVItem->iItem;
5305 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5306 dispInfo.item.lParam = lpItem->lParam;
5307 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5308 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5311 /* we should not store values for subitems */
5312 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5314 /* Now, handle the iImage field */
5315 if (dispInfo.item.mask & LVIF_IMAGE)
5317 lpLVItem->iImage = dispInfo.item.iImage;
5318 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5319 pItemHdr->iImage = dispInfo.item.iImage;
5321 else if (lpLVItem->mask & LVIF_IMAGE)
5323 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5324 lpLVItem->iImage = pItemHdr->iImage;
5326 lpLVItem->iImage = 0;
5329 /* The pszText field */
5330 if (dispInfo.item.mask & LVIF_TEXT)
5332 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5333 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5335 lpLVItem->pszText = dispInfo.item.pszText;
5337 else if (lpLVItem->mask & LVIF_TEXT)
5339 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5340 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5343 /* if this is a subitem, we're done */
5344 if (isubitem) return TRUE;
5346 /* Next is the lParam field */
5347 if (dispInfo.item.mask & LVIF_PARAM)
5349 lpLVItem->lParam = dispInfo.item.lParam;
5350 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5351 lpItem->lParam = dispInfo.item.lParam;
5353 else if (lpLVItem->mask & LVIF_PARAM)
5354 lpLVItem->lParam = lpItem->lParam;
5356 /* ... the state field (this one is different due to uCallbackmask) */
5357 if (lpLVItem->mask & LVIF_STATE)
5359 lpLVItem->state = lpItem->state;
5360 if (dispInfo.item.mask & LVIF_STATE)
5362 lpLVItem->state &= ~dispInfo.item.stateMask;
5363 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5365 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5367 lpLVItem->state &= ~LVIS_FOCUSED;
5368 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5369 lpLVItem->state |= LVIS_FOCUSED;
5371 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5373 lpLVItem->state &= ~LVIS_SELECTED;
5374 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5375 lpLVItem->state |= LVIS_SELECTED;
5379 /* and last, but not least, the indent field */
5380 if (lpLVItem->mask & LVIF_INDENT)
5381 lpLVItem->iIndent = lpItem->iIndent;
5388 * Retrieves item attributes.
5391 * [I] hwnd : window handle
5392 * [IO] lpLVItem : item info
5393 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5394 * if FALSE, the lpLVItem is a LPLVITEMA.
5397 * This is the external 'GetItem' interface -- it properly copies
5398 * the text in the provided buffer.
5404 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5409 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5412 pszText = lpLVItem->pszText;
5413 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5414 if (bResult && lpLVItem->pszText != pszText)
5415 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5416 lpLVItem->pszText = pszText;
5424 * Retrieves the position (upper-left) of the listview control item.
5425 * Note that for LVS_ICON style, the upper-left is that of the icon
5426 * and not the bounding box.
5429 * [I] infoPtr : valid pointer to the listview structure
5430 * [I] nItem : item index
5431 * [O] lpptPosition : coordinate information
5437 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5439 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5442 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5444 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5446 LISTVIEW_GetOrigin(infoPtr, &Origin);
5447 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5449 if (uView == LVS_ICON)
5451 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5452 lpptPosition->y += ICON_TOP_PADDING;
5454 lpptPosition->x += Origin.x;
5455 lpptPosition->y += Origin.y;
5457 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
5464 * Retrieves the bounding rectangle for a listview control item.
5467 * [I] infoPtr : valid pointer to the listview structure
5468 * [I] nItem : item index
5469 * [IO] lprc : bounding rectangle coordinates
5470 * lprc->left specifies the portion of the item for which the bounding
5471 * rectangle will be retrieved.
5473 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5474 * including the icon and label.
5477 * * Experiment shows that native control returns:
5478 * * width = min (48, length of text line)
5479 * * .left = position.x - (width - iconsize.cx)/2
5480 * * .right = .left + width
5481 * * height = #lines of text * ntmHeight + icon height + 8
5482 * * .top = position.y - 2
5483 * * .bottom = .top + height
5484 * * separation between items .y = itemSpacing.cy - height
5485 * * .x = itemSpacing.cx - width
5486 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5489 * * Experiment shows that native control returns:
5490 * * width = iconSize.cx + 16
5491 * * .left = position.x - (width - iconsize.cx)/2
5492 * * .right = .left + width
5493 * * height = iconSize.cy + 4
5494 * * .top = position.y - 2
5495 * * .bottom = .top + height
5496 * * separation between items .y = itemSpacing.cy - height
5497 * * .x = itemSpacing.cx - width
5498 * LVIR_LABEL Returns the bounding rectangle of the item text.
5501 * * Experiment shows that native control returns:
5502 * * width = text length
5503 * * .left = position.x - width/2
5504 * * .right = .left + width
5505 * * height = ntmH * linecount + 2
5506 * * .top = position.y + iconSize.cy + 6
5507 * * .bottom = .top + height
5508 * * separation between items .y = itemSpacing.cy - height
5509 * * .x = itemSpacing.cx - width
5510 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5511 * rectangles, but excludes columns in report view.
5518 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5519 * upon whether the window has the focus currently and on whether the item
5520 * is the one with the focus. Ensure that the control's record of which
5521 * item has the focus agrees with the items' records.
5523 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5525 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5526 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5527 BOOL doLabel = TRUE, oversizedBox = FALSE;
5528 POINT Position, Origin;
5532 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5534 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5536 LISTVIEW_GetOrigin(infoPtr, &Origin);
5537 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5539 /* Be smart and try to figure out the minimum we have to do */
5540 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5541 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5542 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5543 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5544 oversizedBox = TRUE;
5546 /* get what we need from the item before hand, so we make
5547 * only one request. This can speed up things, if data
5548 * is stored on the app side */
5550 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5551 if (doLabel) lvItem.mask |= LVIF_TEXT;
5552 lvItem.iItem = nItem;
5553 lvItem.iSubItem = 0;
5554 lvItem.pszText = szDispText;
5555 lvItem.cchTextMax = DISP_TEXT_SIZE;
5556 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5557 /* we got the state already up, simulate it here, to avoid a reget */
5558 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5560 lvItem.mask |= LVIF_STATE;
5561 lvItem.stateMask = LVIS_FOCUSED;
5562 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5565 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5566 lprc->left = LVIR_BOUNDS;
5570 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5574 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5578 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5581 case LVIR_SELECTBOUNDS:
5582 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5583 UnionRect(lprc, lprc, &label_rect);
5587 WARN("Unknown value: %ld\n", lprc->left);
5591 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5593 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
5600 * Retrieves the spacing between listview control items.
5603 * [I] infoPtr : valid pointer to the listview structure
5604 * [IO] lprc : rectangle to receive the output
5605 * on input, lprc->top = nSubItem
5606 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5608 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5609 * not only those of the first column.
5610 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5616 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5620 INT nColumn = lprc->top;
5622 if (!lprc) return FALSE;
5624 TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top);
5625 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5627 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5629 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5631 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5633 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5636 lvItem.iItem = nItem;
5637 lvItem.iSubItem = nColumn;
5639 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5643 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5648 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5652 ERR("Unknown bounds=%ld\n", lprc->left);
5656 OffsetRect(lprc, Position.x, Position.y);
5663 * Retrieves the width of a label.
5666 * [I] infoPtr : valid pointer to the listview structure
5669 * SUCCESS : string width (in pixels)
5672 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5674 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5677 TRACE("(nItem=%d)\n", nItem);
5679 lvItem.mask = LVIF_TEXT;
5680 lvItem.iItem = nItem;
5681 lvItem.iSubItem = 0;
5682 lvItem.pszText = szDispText;
5683 lvItem.cchTextMax = DISP_TEXT_SIZE;
5684 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5686 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5691 * Retrieves the spacing between listview control items.
5694 * [I] infoPtr : valid pointer to the listview structure
5695 * [I] bSmall : flag for small or large icon
5698 * Horizontal + vertical spacing
5700 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5706 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5710 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5711 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5713 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5720 * Retrieves the state of a listview control item.
5723 * [I] infoPtr : valid pointer to the listview structure
5724 * [I] nItem : item index
5725 * [I] uMask : state mask
5728 * State specified by the mask.
5730 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5734 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5736 lvItem.iItem = nItem;
5737 lvItem.iSubItem = 0;
5738 lvItem.mask = LVIF_STATE;
5739 lvItem.stateMask = uMask;
5740 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5742 return lvItem.state & uMask;
5747 * Retrieves the text of a listview control item or subitem.
5750 * [I] hwnd : window handle
5751 * [I] nItem : item index
5752 * [IO] lpLVItem : item information
5753 * [I] isW : TRUE if lpLVItem is Unicode
5756 * SUCCESS : string length
5759 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5761 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5763 lpLVItem->mask = LVIF_TEXT;
5764 lpLVItem->iItem = nItem;
5765 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5767 return textlenT(lpLVItem->pszText, isW);
5772 * Searches for an item based on properties + relationships.
5775 * [I] infoPtr : valid pointer to the listview structure
5776 * [I] nItem : item index
5777 * [I] uFlags : relationship flag
5780 * SUCCESS : item index
5783 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5785 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5787 LVFINDINFOW lvFindInfo;
5788 INT nCountPerColumn;
5792 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5793 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5795 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5797 if (uFlags & LVNI_CUT)
5800 if (uFlags & LVNI_DROPHILITED)
5801 uMask |= LVIS_DROPHILITED;
5803 if (uFlags & LVNI_FOCUSED)
5804 uMask |= LVIS_FOCUSED;
5806 if (uFlags & LVNI_SELECTED)
5807 uMask |= LVIS_SELECTED;
5809 /* if we're asked for the focused item, that's only one,
5810 * so it's worth optimizing */
5811 if (uFlags & LVNI_FOCUSED)
5813 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
5814 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5817 if (uFlags & LVNI_ABOVE)
5819 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5824 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5830 /* Special case for autoarrange - move 'til the top of a list */
5831 if (is_autoarrange(infoPtr))
5833 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5834 while (nItem - nCountPerRow >= 0)
5836 nItem -= nCountPerRow;
5837 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5842 lvFindInfo.flags = LVFI_NEARESTXY;
5843 lvFindInfo.vkDirection = VK_UP;
5844 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5845 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5847 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5852 else if (uFlags & LVNI_BELOW)
5854 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5856 while (nItem < infoPtr->nItemCount)
5859 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5865 /* Special case for autoarrange - move 'til the bottom of a list */
5866 if (is_autoarrange(infoPtr))
5868 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5869 while (nItem + nCountPerRow < infoPtr->nItemCount )
5871 nItem += nCountPerRow;
5872 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5877 lvFindInfo.flags = LVFI_NEARESTXY;
5878 lvFindInfo.vkDirection = VK_DOWN;
5879 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5880 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5882 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5887 else if (uFlags & LVNI_TOLEFT)
5889 if (uView == LVS_LIST)
5891 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5892 while (nItem - nCountPerColumn >= 0)
5894 nItem -= nCountPerColumn;
5895 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5899 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5901 /* Special case for autoarrange - move 'ti the beginning of a row */
5902 if (is_autoarrange(infoPtr))
5904 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5905 while (nItem % nCountPerRow > 0)
5908 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5913 lvFindInfo.flags = LVFI_NEARESTXY;
5914 lvFindInfo.vkDirection = VK_LEFT;
5915 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5916 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5918 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5923 else if (uFlags & LVNI_TORIGHT)
5925 if (uView == LVS_LIST)
5927 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5928 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5930 nItem += nCountPerColumn;
5931 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5935 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5937 /* Special case for autoarrange - move 'til the end of a row */
5938 if (is_autoarrange(infoPtr))
5940 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5941 while (nItem % nCountPerRow < nCountPerRow - 1 )
5944 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5949 lvFindInfo.flags = LVFI_NEARESTXY;
5950 lvFindInfo.vkDirection = VK_RIGHT;
5951 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5952 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5954 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5963 /* search by index */
5964 for (i = nItem; i < infoPtr->nItemCount; i++)
5966 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5974 /* LISTVIEW_GetNumberOfWorkAreas */
5978 * Retrieves the origin coordinates when in icon or small icon display mode.
5981 * [I] infoPtr : valid pointer to the listview structure
5982 * [O] lpptOrigin : coordinate information
5987 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5989 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5990 INT nHorzPos = 0, nVertPos = 0;
5991 SCROLLINFO scrollInfo;
5993 scrollInfo.cbSize = sizeof(SCROLLINFO);
5994 scrollInfo.fMask = SIF_POS;
5996 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5997 nHorzPos = scrollInfo.nPos;
5998 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5999 nVertPos = scrollInfo.nPos;
6001 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
6003 lpptOrigin->x = infoPtr->rcList.left;
6004 lpptOrigin->y = infoPtr->rcList.top;
6005 if (uView == LVS_LIST)
6006 nHorzPos *= infoPtr->nItemWidth;
6007 else if (uView == LVS_REPORT)
6008 nVertPos *= infoPtr->nItemHeight;
6010 lpptOrigin->x -= nHorzPos;
6011 lpptOrigin->y -= nVertPos;
6013 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
6018 * Retrieves the width of a string.
6021 * [I] hwnd : window handle
6022 * [I] lpszText : text string to process
6023 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6026 * SUCCESS : string width (in pixels)
6029 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6034 if (is_textT(lpszText, isW))
6036 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6037 HDC hdc = GetDC(infoPtr->hwndSelf);
6038 HFONT hOldFont = SelectObject(hdc, hFont);
6041 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6043 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6044 SelectObject(hdc, hOldFont);
6045 ReleaseDC(infoPtr->hwndSelf, hdc);
6047 return stringSize.cx;
6052 * Determines which listview item is located at the specified position.
6055 * [I] infoPtr : valid pointer to the listview structure
6056 * [IO] lpht : hit test information
6057 * [I] subitem : fill out iSubItem.
6058 * [I] select : return the index only if the hit selects the item
6061 * (mm 20001022): We must not allow iSubItem to be touched, for
6062 * an app might pass only a structure with space up to iItem!
6063 * (MS Office 97 does that for instance in the file open dialog)
6066 * SUCCESS : item index
6069 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6071 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6072 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6073 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6074 POINT Origin, Position, opt;
6079 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6083 if (subitem) lpht->iSubItem = 0;
6085 if (infoPtr->rcList.left > lpht->pt.x)
6086 lpht->flags |= LVHT_TOLEFT;
6087 else if (infoPtr->rcList.right < lpht->pt.x)
6088 lpht->flags |= LVHT_TORIGHT;
6090 if (infoPtr->rcList.top > lpht->pt.y)
6091 lpht->flags |= LVHT_ABOVE;
6092 else if (infoPtr->rcList.bottom < lpht->pt.y)
6093 lpht->flags |= LVHT_BELOW;
6095 TRACE("lpht->flags=0x%x\n", lpht->flags);
6096 if (lpht->flags) return -1;
6098 lpht->flags |= LVHT_NOWHERE;
6100 LISTVIEW_GetOrigin(infoPtr, &Origin);
6102 /* first deal with the large items */
6103 rcSearch.left = lpht->pt.x;
6104 rcSearch.top = lpht->pt.y;
6105 rcSearch.right = rcSearch.left + 1;
6106 rcSearch.bottom = rcSearch.top + 1;
6108 iterator_frameditems(&i, infoPtr, &rcSearch);
6109 iterator_next(&i); /* go to first item in the sequence */
6111 iterator_destroy(&i);
6113 TRACE("lpht->iItem=%d\n", iItem);
6114 if (iItem == -1) return -1;
6116 lvItem.mask = LVIF_STATE | LVIF_TEXT;
6117 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6118 lvItem.stateMask = LVIS_STATEIMAGEMASK;
6119 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6120 lvItem.iItem = iItem;
6121 lvItem.iSubItem = 0;
6122 lvItem.pszText = szDispText;
6123 lvItem.cchTextMax = DISP_TEXT_SIZE;
6124 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6125 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6127 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
6128 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6129 opt.x = lpht->pt.x - Position.x - Origin.x;
6130 opt.y = lpht->pt.y - Position.y - Origin.y;
6132 if (uView == LVS_REPORT)
6135 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6136 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6137 if (!PtInRect(&rcBounds, opt)) return -1;
6139 if (PtInRect(&rcIcon, opt))
6140 lpht->flags |= LVHT_ONITEMICON;
6141 else if (PtInRect(&rcLabel, opt))
6142 lpht->flags |= LVHT_ONITEMLABEL;
6143 else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6144 lpht->flags |= LVHT_ONITEMSTATEICON;
6145 if (lpht->flags & LVHT_ONITEM)
6146 lpht->flags &= ~LVHT_NOWHERE;
6148 TRACE("lpht->flags=0x%x\n", lpht->flags);
6149 if (uView == LVS_REPORT && subitem)
6153 rcBounds.right = rcBounds.left;
6154 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6156 rcBounds.left = rcBounds.right;
6157 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6158 if (PtInRect(&rcBounds, opt))
6166 if (select && !(uView == LVS_REPORT &&
6167 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6168 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6170 if (uView == LVS_REPORT)
6172 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6173 UnionRect(&rcBounds, &rcBounds, &rcState);
6175 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6177 return lpht->iItem = iItem;
6181 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6182 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6183 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6184 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6185 their own sort proc. when sending LVM_SORTITEMS.
6188 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6190 LVS_SORTXXX must be specified,
6191 LVS_OWNERDRAW is not set,
6192 <item>.pszText is not LPSTR_TEXTCALLBACK.
6194 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6195 are sorted based on item text..."
6197 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6199 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6200 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6201 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6203 /* if we're sorting descending, negate the return value */
6204 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6209 * Inserts a new item in the listview control.
6212 * [I] infoPtr : valid pointer to the listview structure
6213 * [I] lpLVItem : item information
6214 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6217 * SUCCESS : new item index
6220 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6222 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6227 BOOL is_sorted, has_changed;
6229 HWND hwndSelf = infoPtr->hwndSelf;
6231 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6233 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6235 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6236 if (!lpLVItem || lpLVItem->iSubItem) return -1;
6238 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6240 if (!(lpItem = (ITEM_INFO *)Alloc(sizeof(ITEM_INFO)))) return -1;
6242 /* insert item in listview control data structure */
6243 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6244 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6246 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6247 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6249 if (lpLVItem->iItem < 0 && !is_sorted) return -1;
6251 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6252 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6253 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6254 if (nItem == -1) goto fail;
6255 infoPtr->nItemCount++;
6257 /* shift indices first so they don't get tangled */
6258 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6260 /* set the item attributes */
6261 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6263 /* full size structure expected - _WIN32IE >= 0x560 */
6266 else if (lpLVItem->mask & LVIF_INDENT)
6268 /* indent member expected - _WIN32IE >= 0x300 */
6269 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6273 /* minimal structure expected */
6274 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6277 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) item.state &= ~LVIS_STATEIMAGEMASK;
6278 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6280 /* if we're sorted, sort the list, and update the index */
6283 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6284 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6285 assert(nItem != -1);
6288 /* make room for the position, if we are in the right mode */
6289 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6291 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6293 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6295 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6300 /* send LVN_INSERTITEM notification */
6301 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6303 nmlv.lParam = lpItem->lParam;
6304 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6305 if (!IsWindow(hwndSelf))
6308 /* align items (set position of each item) */
6309 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6313 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6314 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6316 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6318 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6321 /* now is the invalidation fun */
6322 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6326 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6327 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6328 infoPtr->nItemCount--;
6330 DPA_DeletePtr(hdpaSubItems, 0);
6331 DPA_Destroy (hdpaSubItems);
6338 * Redraws a range of items.
6341 * [I] infoPtr : valid pointer to the listview structure
6342 * [I] nFirst : first item
6343 * [I] nLast : last item
6349 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6353 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6354 max(nFirst, nLast) >= infoPtr->nItemCount)
6357 for (i = nFirst; i <= nLast; i++)
6358 LISTVIEW_InvalidateItem(infoPtr, i);
6365 * Scroll the content of a listview.
6368 * [I] infoPtr : valid pointer to the listview structure
6369 * [I] dx : horizontal scroll amount in pixels
6370 * [I] dy : vertical scroll amount in pixels
6377 * If the control is in report mode (LVS_REPORT) the control can
6378 * be scrolled only in line increments. "dy" will be rounded to the
6379 * nearest number of pixels that are a whole line. Ex: if line height
6380 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6381 * is passed the the scroll will be 0. (per MSDN 7/2002)
6383 * For: (per experimentaion with native control and CSpy ListView)
6384 * LVS_ICON dy=1 = 1 pixel (vertical only)
6386 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6388 * LVS_LIST dx=1 = 1 column (horizontal only)
6389 * but will only scroll 1 column per message
6390 * no matter what the value.
6391 * dy must be 0 or FALSE returned.
6392 * LVS_REPORT dx=1 = 1 pixel
6396 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6398 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6400 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6401 dy /= infoPtr->nItemHeight;
6404 if (dy != 0) return FALSE;
6411 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6412 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6419 * Sets the background color.
6422 * [I] infoPtr : valid pointer to the listview structure
6423 * [I] clrBk : background color
6429 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6431 TRACE("(clrBk=%lx)\n", clrBk);
6433 if(infoPtr->clrBk != clrBk) {
6434 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6435 infoPtr->clrBk = clrBk;
6436 if (clrBk == CLR_NONE)
6437 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6439 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6440 LISTVIEW_InvalidateList(infoPtr);
6446 /* LISTVIEW_SetBkImage */
6448 /*** Helper for {Insert,Set}ColumnT *only* */
6449 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6451 if (lpColumn->mask & LVCF_FMT)
6453 /* format member is valid */
6454 lphdi->mask |= HDI_FORMAT;
6456 /* set text alignment (leftmost column must be left-aligned) */
6457 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6458 lphdi->fmt |= HDF_LEFT;
6459 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6460 lphdi->fmt |= HDF_RIGHT;
6461 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6462 lphdi->fmt |= HDF_CENTER;
6464 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6465 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6467 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6469 lphdi->fmt |= HDF_IMAGE;
6470 lphdi->iImage = I_IMAGECALLBACK;
6474 if (lpColumn->mask & LVCF_WIDTH)
6476 lphdi->mask |= HDI_WIDTH;
6477 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6479 /* make it fill the remainder of the controls width */
6483 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6485 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6486 lphdi->cxy += rcHeader.right - rcHeader.left;
6489 /* retrieve the layout of the header */
6490 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6491 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
6493 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6496 lphdi->cxy = lpColumn->cx;
6499 if (lpColumn->mask & LVCF_TEXT)
6501 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6502 lphdi->fmt |= HDF_STRING;
6503 lphdi->pszText = lpColumn->pszText;
6504 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6507 if (lpColumn->mask & LVCF_IMAGE)
6509 lphdi->mask |= HDI_IMAGE;
6510 lphdi->iImage = lpColumn->iImage;
6513 if (lpColumn->mask & LVCF_ORDER)
6515 lphdi->mask |= HDI_ORDER;
6516 lphdi->iOrder = lpColumn->iOrder;
6523 * Inserts a new column.
6526 * [I] infoPtr : valid pointer to the listview structure
6527 * [I] nColumn : column index
6528 * [I] lpColumn : column information
6529 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6532 * SUCCESS : new column index
6535 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6536 const LVCOLUMNW *lpColumn, BOOL isW)
6538 COLUMN_INFO *lpColumnInfo;
6542 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6544 if (!lpColumn || nColumn < 0) return -1;
6545 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6547 ZeroMemory(&hdi, sizeof(HDITEMW));
6548 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6551 * when the iSubItem is available Windows copies it to the header lParam. It seems
6552 * to happen only in LVM_INSERTCOLUMN - not in LVM_SETCOLUMN
6554 if (lpColumn->mask & LVCF_SUBITEM)
6556 hdi.mask |= HDI_LPARAM;
6557 hdi.lParam = lpColumn->iSubItem;
6560 /* insert item in header control */
6561 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6562 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6563 (WPARAM)nColumn, (LPARAM)&hdi);
6564 if (nNewColumn == -1) return -1;
6565 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6567 /* create our own column info */
6568 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6569 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6571 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6572 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6574 /* now we have to actually adjust the data */
6575 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6577 SUBITEM_INFO *lpSubItem;
6581 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6583 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6584 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6586 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6587 if (lpSubItem->iSubItem >= nNewColumn)
6588 lpSubItem->iSubItem++;
6593 /* make space for the new column */
6594 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6599 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6602 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6610 * Sets the attributes of a header item.
6613 * [I] infoPtr : valid pointer to the listview structure
6614 * [I] nColumn : column index
6615 * [I] lpColumn : column attributes
6616 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6622 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6623 const LVCOLUMNW *lpColumn, BOOL isW)
6625 HDITEMW hdi, hdiget;
6628 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6630 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6632 ZeroMemory(&hdi, sizeof(HDITEMW));
6633 if (lpColumn->mask & LVCF_FMT)
6635 hdi.mask |= HDI_FORMAT;
6636 hdiget.mask = HDI_FORMAT;
6637 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6638 hdi.fmt = hdiget.fmt & HDF_STRING;
6640 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6642 /* set header item attributes */
6643 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6644 if (!bResult) return FALSE;
6646 if (lpColumn->mask & LVCF_FMT)
6648 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6649 int oldFmt = lpColumnInfo->fmt;
6651 lpColumnInfo->fmt = lpColumn->fmt;
6652 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6654 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6655 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6664 * Sets the column order array
6667 * [I] infoPtr : valid pointer to the listview structure
6668 * [I] iCount : number of elements in column order array
6669 * [I] lpiArray : pointer to column order array
6675 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6677 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6688 * Sets the width of a column
6691 * [I] infoPtr : valid pointer to the listview structure
6692 * [I] nColumn : column index
6693 * [I] cx : column width
6699 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6701 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6702 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6706 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6708 /* set column width only if in report or list mode */
6709 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6711 /* take care of invalid cx values */
6712 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6713 else if (uView == LVS_LIST && cx < 1) return FALSE;
6715 /* resize all columns if in LVS_LIST mode */
6716 if(uView == LVS_LIST)
6718 infoPtr->nItemWidth = cx;
6719 LISTVIEW_InvalidateList(infoPtr);
6723 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6725 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6730 lvItem.mask = LVIF_TEXT;
6732 lvItem.iSubItem = nColumn;
6733 lvItem.pszText = szDispText;
6734 lvItem.cchTextMax = DISP_TEXT_SIZE;
6735 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6737 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6738 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6739 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6741 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6742 max_cx += infoPtr->iconSize.cx;
6743 max_cx += TRAILING_LABEL_PADDING;
6746 /* autosize based on listview items width */
6747 if(cx == LVSCW_AUTOSIZE)
6749 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6751 /* if iCol is the last column make it fill the remainder of the controls width */
6752 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
6757 LISTVIEW_GetOrigin(infoPtr, &Origin);
6758 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6760 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6764 /* Despite what the MS docs say, if this is not the last
6765 column, then MS resizes the column to the width of the
6766 largest text string in the column, including headers
6767 and items. This is different from LVSCW_AUTOSIZE in that
6768 LVSCW_AUTOSIZE ignores the header string length. */
6771 /* retrieve header text */
6772 hdi.mask = HDI_TEXT;
6773 hdi.cchTextMax = DISP_TEXT_SIZE;
6774 hdi.pszText = szDispText;
6775 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
6777 HDC hdc = GetDC(infoPtr->hwndSelf);
6778 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6781 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6782 cx = size.cx + TRAILING_HEADER_PADDING;
6783 /* FIXME: Take into account the header image, if one is present */
6784 SelectObject(hdc, old_font);
6785 ReleaseDC(infoPtr->hwndSelf, hdc);
6787 cx = max (cx, max_cx);
6791 if (cx < 0) return FALSE;
6793 /* call header to update the column change */
6794 hdi.mask = HDI_WIDTH;
6796 TRACE("hdi.cxy=%d\n", hdi.cxy);
6797 return Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6801 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
6804 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO *infoPtr)
6807 HBITMAP hbm_im, hbm_mask, hbm_orig;
6809 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6810 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6813 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6814 ILC_COLOR | ILC_MASK, 2, 2);
6815 hdc_wnd = GetDC(infoPtr->hwndSelf);
6816 hdc = CreateCompatibleDC(hdc_wnd);
6817 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6818 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6819 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6821 rc.left = rc.top = 0;
6822 rc.right = GetSystemMetrics(SM_CXSMICON);
6823 rc.bottom = GetSystemMetrics(SM_CYSMICON);
6825 hbm_orig = SelectObject(hdc, hbm_mask);
6826 FillRect(hdc, &rc, hbr_white);
6827 InflateRect(&rc, -3, -3);
6828 FillRect(hdc, &rc, hbr_black);
6830 SelectObject(hdc, hbm_im);
6831 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6832 SelectObject(hdc, hbm_orig);
6833 ImageList_Add(himl, hbm_im, hbm_mask);
6835 SelectObject(hdc, hbm_im);
6836 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6837 SelectObject(hdc, hbm_orig);
6838 ImageList_Add(himl, hbm_im, hbm_mask);
6840 DeleteObject(hbm_mask);
6841 DeleteObject(hbm_im);
6849 * Sets the extended listview style.
6852 * [I] infoPtr : valid pointer to the listview structure
6854 * [I] dwStyle : style
6857 * SUCCESS : previous style
6860 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6862 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6866 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6868 infoPtr->dwLvExStyle = dwStyle;
6870 if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES)
6872 HIMAGELIST himl = 0;
6873 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6874 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
6875 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
6878 if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_HEADERDRAGDROP)
6880 DWORD dwStyle = GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE);
6881 if (infoPtr->dwLvExStyle & LVS_EX_HEADERDRAGDROP)
6882 dwStyle |= HDS_DRAGDROP;
6884 dwStyle &= ~HDS_DRAGDROP;
6885 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE, dwStyle);
6893 * Sets the new hot cursor used during hot tracking and hover selection.
6896 * [I] infoPtr : valid pointer to the listview structure
6897 * [I] hCursor : the new hot cursor handle
6900 * Returns the previous hot cursor
6902 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6904 HCURSOR oldCursor = infoPtr->hHotCursor;
6906 infoPtr->hHotCursor = hCursor;
6914 * Sets the hot item index.
6917 * [I] infoPtr : valid pointer to the listview structure
6918 * [I] iIndex : index
6921 * SUCCESS : previous hot item index
6922 * FAILURE : -1 (no hot item)
6924 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6926 INT iOldIndex = infoPtr->nHotItem;
6928 infoPtr->nHotItem = iIndex;
6936 * Sets the amount of time the cursor must hover over an item before it is selected.
6939 * [I] infoPtr : valid pointer to the listview structure
6940 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6943 * Returns the previous hover time
6945 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6947 DWORD oldHoverTime = infoPtr->dwHoverTime;
6949 infoPtr->dwHoverTime = dwHoverTime;
6951 return oldHoverTime;
6956 * Sets spacing for icons of LVS_ICON style.
6959 * [I] infoPtr : valid pointer to the listview structure
6960 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6961 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6964 * MAKELONG(oldcx, oldcy)
6966 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6968 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6969 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6971 TRACE("requested=(%d,%d)\n", cx, cy);
6973 /* this is supported only for LVS_ICON style */
6974 if (uView != LVS_ICON) return oldspacing;
6976 /* set to defaults, if instructed to */
6977 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6978 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6980 /* if 0 then compute width
6981 * FIXME: Should scan each item and determine max width of
6982 * icon or label, then make that the width */
6984 cx = infoPtr->iconSpacing.cx;
6986 /* if 0 then compute height */
6988 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6989 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6992 infoPtr->iconSpacing.cx = cx;
6993 infoPtr->iconSpacing.cy = cy;
6995 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6996 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6997 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6998 infoPtr->ntmHeight);
7000 /* these depend on the iconSpacing */
7001 LISTVIEW_UpdateItemSize(infoPtr);
7006 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
7010 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
7017 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
7018 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
7027 * [I] infoPtr : valid pointer to the listview structure
7028 * [I] nType : image list type
7029 * [I] himl : image list handle
7032 * SUCCESS : old image list
7035 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
7037 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7038 INT oldHeight = infoPtr->nItemHeight;
7039 HIMAGELIST himlOld = 0;
7041 TRACE("(nType=%d, himl=%p\n", nType, himl);
7046 himlOld = infoPtr->himlNormal;
7047 infoPtr->himlNormal = himl;
7048 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7049 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7053 himlOld = infoPtr->himlSmall;
7054 infoPtr->himlSmall = himl;
7055 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7059 himlOld = infoPtr->himlState;
7060 infoPtr->himlState = himl;
7061 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7062 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7066 ERR("Unknown icon type=%d\n", nType);
7070 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7071 if (infoPtr->nItemHeight != oldHeight)
7072 LISTVIEW_UpdateScroll(infoPtr);
7079 * Preallocates memory (does *not* set the actual count of items !)
7082 * [I] infoPtr : valid pointer to the listview structure
7083 * [I] nItems : item count (projected number of items to allocate)
7084 * [I] dwFlags : update flags
7090 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7092 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
7094 if (infoPtr->dwStyle & LVS_OWNERDATA)
7096 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7097 INT nOldCount = infoPtr->nItemCount;
7099 if (nItems < nOldCount)
7101 RANGE range = { nItems, nOldCount };
7102 ranges_del(infoPtr->selectionRanges, range);
7103 if (infoPtr->nFocusedItem >= nItems)
7105 infoPtr->nFocusedItem = -1;
7106 SetRectEmpty(&infoPtr->rcFocus);
7110 infoPtr->nItemCount = nItems;
7111 LISTVIEW_UpdateScroll(infoPtr);
7113 /* the flags are valid only in ownerdata report and list modes */
7114 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7116 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7117 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7119 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7120 LISTVIEW_InvalidateList(infoPtr);
7127 LISTVIEW_GetOrigin(infoPtr, &Origin);
7128 nFrom = min(nOldCount, nItems);
7129 nTo = max(nOldCount, nItems);
7131 if (uView == LVS_REPORT)
7134 rcErase.top = nFrom * infoPtr->nItemHeight;
7135 rcErase.right = infoPtr->nItemWidth;
7136 rcErase.bottom = nTo * infoPtr->nItemHeight;
7137 OffsetRect(&rcErase, Origin.x, Origin.y);
7138 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7139 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7143 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7145 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7146 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7147 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7148 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7149 OffsetRect(&rcErase, Origin.x, Origin.y);
7150 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7151 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7153 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7155 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7156 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7157 OffsetRect(&rcErase, Origin.x, Origin.y);
7158 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7159 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7165 /* According to MSDN for non-LVS_OWNERDATA this is just
7166 * a performance issue. The control allocates its internal
7167 * data structures for the number of items specified. It
7168 * cuts down on the number of memory allocations. Therefore
7169 * we will just issue a WARN here
7171 WARN("for non-ownerdata performance option not implemented.\n");
7179 * Sets the position of an item.
7182 * [I] infoPtr : valid pointer to the listview structure
7183 * [I] nItem : item index
7184 * [I] pt : coordinate
7190 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7192 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7195 TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7197 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7198 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7200 LISTVIEW_GetOrigin(infoPtr, &Origin);
7202 /* This point value seems to be an undocumented feature.
7203 * The best guess is that it means either at the origin,
7204 * or at true beginning of the list. I will assume the origin. */
7205 if ((pt.x == -1) && (pt.y == -1))
7208 if (uView == LVS_ICON)
7210 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7211 pt.y -= ICON_TOP_PADDING;
7216 infoPtr->bAutoarrange = FALSE;
7218 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7223 * Sets the state of one or many items.
7226 * [I] infoPtr : valid pointer to the listview structure
7227 * [I] nItem : item index
7228 * [I] lpLVItem : item or subitem info
7234 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7236 BOOL bResult = TRUE;
7239 lvItem.iItem = nItem;
7240 lvItem.iSubItem = 0;
7241 lvItem.mask = LVIF_STATE;
7242 lvItem.state = lpLVItem->state;
7243 lvItem.stateMask = lpLVItem->stateMask;
7244 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7248 /* apply to all items */
7249 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7250 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7253 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7256 * Update selection mark
7258 * Investigation on windows 2k showed that selection mark was updated
7259 * whenever a new selection was made, but if the selected item was
7260 * unselected it was not updated.
7262 * we are probably still not 100% accurate, but this at least sets the
7263 * proper selection mark when it is needed
7266 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7267 ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark)))
7270 infoPtr->nSelectionMark = -1;
7271 for (i = 0; i < infoPtr->nItemCount; i++)
7273 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7275 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7277 infoPtr->nSelectionMark = i;
7281 else if (ranges_contain(infoPtr->selectionRanges, i))
7283 infoPtr->nSelectionMark = i;
7294 * Sets the text of an item or subitem.
7297 * [I] hwnd : window handle
7298 * [I] nItem : item index
7299 * [I] lpLVItem : item or subitem info
7300 * [I] isW : TRUE if input is Unicode
7306 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7310 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7312 lvItem.iItem = nItem;
7313 lvItem.iSubItem = lpLVItem->iSubItem;
7314 lvItem.mask = LVIF_TEXT;
7315 lvItem.pszText = lpLVItem->pszText;
7316 lvItem.cchTextMax = lpLVItem->cchTextMax;
7318 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7320 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7325 * Set item index that marks the start of a multiple selection.
7328 * [I] infoPtr : valid pointer to the listview structure
7329 * [I] nIndex : index
7332 * Index number or -1 if there is no selection mark.
7334 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7336 INT nOldIndex = infoPtr->nSelectionMark;
7338 TRACE("(nIndex=%d)\n", nIndex);
7340 infoPtr->nSelectionMark = nIndex;
7347 * Sets the text background color.
7350 * [I] infoPtr : valid pointer to the listview structure
7351 * [I] clrTextBk : text background color
7357 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7359 TRACE("(clrTextBk=%lx)\n", clrTextBk);
7361 if (infoPtr->clrTextBk != clrTextBk)
7363 infoPtr->clrTextBk = clrTextBk;
7364 LISTVIEW_InvalidateList(infoPtr);
7372 * Sets the text foreground color.
7375 * [I] infoPtr : valid pointer to the listview structure
7376 * [I] clrText : text color
7382 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7384 TRACE("(clrText=%lx)\n", clrText);
7386 if (infoPtr->clrText != clrText)
7388 infoPtr->clrText = clrText;
7389 LISTVIEW_InvalidateList(infoPtr);
7397 * Determines which listview item is located at the specified position.
7400 * [I] infoPtr : valid pointer to the listview structure
7401 * [I] hwndNewToolTip : handle to new ToolTip
7406 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7408 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7409 infoPtr->hwndToolTip = hwndNewToolTip;
7410 return hwndOldToolTip;
7413 /* LISTVIEW_SetUnicodeFormat */
7414 /* LISTVIEW_SetWorkAreas */
7418 * Callback internally used by LISTVIEW_SortItems()
7421 * [I] first : pointer to first ITEM_INFO to compare
7422 * [I] second : pointer to second ITEM_INFO to compare
7423 * [I] lParam : HWND of control
7426 * if first comes before second : negative
7427 * if first comes after second : positive
7428 * if first and second are equivalent : zero
7430 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7432 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7433 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7434 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7436 /* Forward the call to the client defined callback */
7437 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7442 * Sorts the listview items.
7445 * [I] infoPtr : valid pointer to the listview structure
7446 * [I] pfnCompare : application-defined value
7447 * [I] lParamSort : pointer to comparision callback
7453 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7455 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7458 LPVOID selectionMarkItem;
7462 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7464 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7466 if (!pfnCompare) return FALSE;
7467 if (!infoPtr->hdpaItems) return FALSE;
7469 /* if there are 0 or 1 items, there is no need to sort */
7470 if (infoPtr->nItemCount < 2) return TRUE;
7472 if (infoPtr->nFocusedItem >= 0)
7474 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7475 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7476 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7478 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7479 /* clear the lpItem->state for non-selected ones */
7480 /* remove the selection ranges */
7482 infoPtr->pfnCompare = pfnCompare;
7483 infoPtr->lParamSort = lParamSort;
7484 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7486 /* Adjust selections and indices so that they are the way they should
7487 * be after the sort (otherwise, the list items move around, but
7488 * whatever is at the item's previous original position will be
7491 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7492 for (i=0; i < infoPtr->nItemCount; i++)
7494 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7495 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7497 if (lpItem->state & LVIS_SELECTED)
7499 item.state = LVIS_SELECTED;
7500 item.stateMask = LVIS_SELECTED;
7501 LISTVIEW_SetItemState(infoPtr, i, &item);
7503 if (lpItem->state & LVIS_FOCUSED)
7505 infoPtr->nFocusedItem = i;
7506 lpItem->state &= ~LVIS_FOCUSED;
7509 if (selectionMarkItem != NULL)
7510 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7511 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7513 /* refresh the display */
7514 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7515 LISTVIEW_InvalidateList(infoPtr);
7522 * Update theme handle after a theme change.
7525 * [I] infoPtr : valid pointer to the listview structure
7529 * FAILURE : something else
7531 static LRESULT LISTVIEW_ThemeChanged(LISTVIEW_INFO *infoPtr)
7533 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7534 CloseThemeData(theme);
7535 OpenThemeData(infoPtr->hwndSelf, themeClass);
7541 * Updates an items or rearranges the listview control.
7544 * [I] infoPtr : valid pointer to the listview structure
7545 * [I] nItem : item index
7551 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7553 TRACE("(nItem=%d)\n", nItem);
7555 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7557 /* rearrange with default alignment style */
7558 if (is_autoarrange(infoPtr))
7559 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7561 LISTVIEW_InvalidateItem(infoPtr, nItem);
7568 * Draw the track line at the place defined in the infoPtr structure.
7569 * The line is drawn with a XOR pen so drawing the line for the second time
7570 * in the same place erases the line.
7573 * [I] infoPtr : valid pointer to the listview structure
7579 static BOOL LISTVIEW_DrawTrackLine(LISTVIEW_INFO *infoPtr)
7585 if (infoPtr->xTrackLine == -1)
7588 if (!(hdc = GetDC(infoPtr->hwndSelf)))
7590 hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
7591 oldROP = SetROP2(hdc, R2_XORPEN);
7592 MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
7593 LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
7594 SetROP2(hdc, oldROP);
7595 SelectObject(hdc, hOldPen);
7596 ReleaseDC(infoPtr->hwndSelf, hdc);
7603 * Creates the listview control.
7606 * [I] hwnd : window handle
7607 * [I] lpcs : the create parameters
7613 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7615 LISTVIEW_INFO *infoPtr;
7616 UINT uView = lpcs->style & LVS_TYPEMASK;
7619 TRACE("(lpcs=%p)\n", lpcs);
7621 /* initialize info pointer */
7622 infoPtr = (LISTVIEW_INFO *)Alloc(sizeof(LISTVIEW_INFO));
7623 if (!infoPtr) return -1;
7625 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
7627 infoPtr->hwndSelf = hwnd;
7628 infoPtr->dwStyle = lpcs->style;
7629 /* determine the type of structures to use */
7630 infoPtr->hwndNotify = lpcs->hwndParent;
7631 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7632 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7634 /* initialize color information */
7635 infoPtr->clrBk = CLR_NONE;
7636 infoPtr->clrText = comctl32_color.clrWindowText;
7637 infoPtr->clrTextBk = CLR_DEFAULT;
7638 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7640 /* set default values */
7641 infoPtr->nFocusedItem = -1;
7642 infoPtr->nSelectionMark = -1;
7643 infoPtr->nHotItem = -1;
7644 infoPtr->bRedraw = TRUE;
7645 infoPtr->bNoItemMetrics = TRUE;
7646 infoPtr->bDoChangeNotify = TRUE;
7647 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7648 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7649 infoPtr->nEditLabelItem = -1;
7650 infoPtr->dwHoverTime = -1; /* default system hover time */
7651 infoPtr->nMeasureItemHeight = 0;
7652 infoPtr->xTrackLine = -1; /* no track line */
7654 /* get default font (icon title) */
7655 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7656 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7657 infoPtr->hFont = infoPtr->hDefaultFont;
7658 LISTVIEW_SaveTextMetrics(infoPtr);
7661 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7662 WS_CHILD | HDS_HORZ | HDS_FULLDRAG | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7663 0, 0, 0, 0, hwnd, NULL,
7664 lpcs->hInstance, NULL);
7665 if (!infoPtr->hwndHeader) goto fail;
7667 /* set header unicode format */
7668 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7670 /* set header font */
7671 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7673 /* allocate memory for the data structure */
7674 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7675 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7676 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7677 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7678 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7680 /* initialize the icon sizes */
7681 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7682 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7684 /* init item size to avoid division by 0 */
7685 LISTVIEW_UpdateItemSize (infoPtr);
7687 if (uView == LVS_REPORT)
7689 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7691 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7695 /* set HDS_HIDDEN flag to hide the header bar */
7696 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7697 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7701 OpenThemeData(hwnd, themeClass);
7706 DestroyWindow(infoPtr->hwndHeader);
7707 ranges_destroy(infoPtr->selectionRanges);
7708 DPA_Destroy(infoPtr->hdpaItems);
7709 DPA_Destroy(infoPtr->hdpaPosX);
7710 DPA_Destroy(infoPtr->hdpaPosY);
7711 DPA_Destroy(infoPtr->hdpaColumns);
7718 * Destroys the listview control.
7721 * [I] infoPtr : valid pointer to the listview structure
7727 static LRESULT LISTVIEW_Destroy(LISTVIEW_INFO *infoPtr)
7729 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7730 CloseThemeData(theme);
7736 * Enables the listview control.
7739 * [I] infoPtr : valid pointer to the listview structure
7740 * [I] bEnable : specifies whether to enable or disable the window
7746 static BOOL LISTVIEW_Enable(LISTVIEW_INFO *infoPtr, BOOL bEnable)
7748 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
7749 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7755 * Erases the background of the listview control.
7758 * [I] infoPtr : valid pointer to the listview structure
7759 * [I] hdc : device context handle
7765 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7769 TRACE("(hdc=%p)\n", hdc);
7771 if (!GetClipBox(hdc, &rc)) return FALSE;
7773 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7779 * Helper function for LISTVIEW_[HV]Scroll *only*.
7780 * Performs vertical/horizontal scrolling by a give amount.
7783 * [I] infoPtr : valid pointer to the listview structure
7784 * [I] dx : amount of horizontal scroll
7785 * [I] dy : amount of vertical scroll
7787 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7789 /* now we can scroll the list */
7790 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7791 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7792 /* if we have focus, adjust rect */
7793 OffsetRect(&infoPtr->rcFocus, dx, dy);
7794 UpdateWindow(infoPtr->hwndSelf);
7799 * Performs vertical scrolling.
7802 * [I] infoPtr : valid pointer to the listview structure
7803 * [I] nScrollCode : scroll code
7804 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7805 * [I] hScrollWnd : scrollbar control window handle
7811 * SB_LINEUP/SB_LINEDOWN:
7812 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7813 * for LVS_REPORT is 1 line
7814 * for LVS_LIST cannot occur
7817 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7818 INT nScrollDiff, HWND hScrollWnd)
7820 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7821 INT nOldScrollPos, nNewScrollPos;
7822 SCROLLINFO scrollInfo;
7825 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7826 debugscrollcode(nScrollCode), nScrollDiff);
7828 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7830 scrollInfo.cbSize = sizeof(SCROLLINFO);
7831 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7833 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7835 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7837 nOldScrollPos = scrollInfo.nPos;
7838 switch (nScrollCode)
7844 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7848 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7852 nScrollDiff = -scrollInfo.nPage;
7856 nScrollDiff = scrollInfo.nPage;
7859 case SB_THUMBPOSITION:
7861 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7868 /* quit right away if pos isn't changing */
7869 if (nScrollDiff == 0) return 0;
7871 /* calculate new position, and handle overflows */
7872 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7873 if (nScrollDiff > 0) {
7874 if (nNewScrollPos < nOldScrollPos ||
7875 nNewScrollPos > scrollInfo.nMax)
7876 nNewScrollPos = scrollInfo.nMax;
7878 if (nNewScrollPos > nOldScrollPos ||
7879 nNewScrollPos < scrollInfo.nMin)
7880 nNewScrollPos = scrollInfo.nMin;
7883 /* set the new position, and reread in case it changed */
7884 scrollInfo.fMask = SIF_POS;
7885 scrollInfo.nPos = nNewScrollPos;
7886 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7888 /* carry on only if it really changed */
7889 if (nNewScrollPos == nOldScrollPos) return 0;
7891 /* now adjust to client coordinates */
7892 nScrollDiff = nOldScrollPos - nNewScrollPos;
7893 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7895 /* and scroll the window */
7896 scroll_list(infoPtr, 0, nScrollDiff);
7903 * Performs horizontal scrolling.
7906 * [I] infoPtr : valid pointer to the listview structure
7907 * [I] nScrollCode : scroll code
7908 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7909 * [I] hScrollWnd : scrollbar control window handle
7915 * SB_LINELEFT/SB_LINERIGHT:
7916 * for LVS_ICON, LVS_SMALLICON 1 pixel
7917 * for LVS_REPORT is 1 pixel
7918 * for LVS_LIST is 1 column --> which is a 1 because the
7919 * scroll is based on columns not pixels
7922 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7923 INT nScrollDiff, HWND hScrollWnd)
7925 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7926 INT nOldScrollPos, nNewScrollPos;
7927 SCROLLINFO scrollInfo;
7929 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7930 debugscrollcode(nScrollCode), nScrollDiff);
7932 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7934 scrollInfo.cbSize = sizeof(SCROLLINFO);
7935 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7937 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7939 nOldScrollPos = scrollInfo.nPos;
7941 switch (nScrollCode)
7955 nScrollDiff = -scrollInfo.nPage;
7959 nScrollDiff = scrollInfo.nPage;
7962 case SB_THUMBPOSITION:
7964 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7971 /* quit right away if pos isn't changing */
7972 if (nScrollDiff == 0) return 0;
7974 /* calculate new position, and handle overflows */
7975 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7976 if (nScrollDiff > 0) {
7977 if (nNewScrollPos < nOldScrollPos ||
7978 nNewScrollPos > scrollInfo.nMax)
7979 nNewScrollPos = scrollInfo.nMax;
7981 if (nNewScrollPos > nOldScrollPos ||
7982 nNewScrollPos < scrollInfo.nMin)
7983 nNewScrollPos = scrollInfo.nMin;
7986 /* set the new position, and reread in case it changed */
7987 scrollInfo.fMask = SIF_POS;
7988 scrollInfo.nPos = nNewScrollPos;
7989 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7991 /* carry on only if it really changed */
7992 if (nNewScrollPos == nOldScrollPos) return 0;
7994 if(uView == LVS_REPORT)
7995 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7997 /* now adjust to client coordinates */
7998 nScrollDiff = nOldScrollPos - nNewScrollPos;
7999 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
8001 /* and scroll the window */
8002 scroll_list(infoPtr, nScrollDiff, 0);
8007 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
8009 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8010 INT gcWheelDelta = 0;
8011 INT pulScrollLines = 3;
8012 SCROLLINFO scrollInfo;
8014 TRACE("(wheelDelta=%d)\n", wheelDelta);
8016 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
8017 gcWheelDelta -= wheelDelta;
8019 scrollInfo.cbSize = sizeof(SCROLLINFO);
8020 scrollInfo.fMask = SIF_POS;
8027 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
8028 * should be fixed in the future.
8030 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
8031 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
8035 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
8037 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
8038 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
8039 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
8044 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8055 * [I] infoPtr : valid pointer to the listview structure
8056 * [I] nVirtualKey : virtual key
8057 * [I] lKeyData : key data
8062 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8064 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8065 HWND hwndSelf = infoPtr->hwndSelf;
8067 NMLVKEYDOWN nmKeyDown;
8069 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
8071 /* send LVN_KEYDOWN notification */
8072 nmKeyDown.wVKey = nVirtualKey;
8073 nmKeyDown.flags = 0;
8074 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8075 if (!IsWindow(hwndSelf))
8078 switch (nVirtualKey)
8081 nItem = infoPtr->nFocusedItem;
8085 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8087 if (!notify(infoPtr, NM_RETURN)) return 0;
8088 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8093 if (infoPtr->nItemCount > 0)
8098 if (infoPtr->nItemCount > 0)
8099 nItem = infoPtr->nItemCount - 1;
8103 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8107 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8111 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8115 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8119 if (uView == LVS_REPORT)
8121 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8122 if (infoPtr->nFocusedItem == topidx)
8123 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8128 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8129 * LISTVIEW_GetCountPerRow(infoPtr);
8130 if(nItem < 0) nItem = 0;
8134 if (uView == LVS_REPORT)
8136 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8137 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8138 if (infoPtr->nFocusedItem == topidx + cnt - 1)
8139 nItem = infoPtr->nFocusedItem + cnt - 1;
8141 nItem = topidx + cnt - 1;
8144 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8145 * LISTVIEW_GetCountPerRow(infoPtr);
8146 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8150 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8151 LISTVIEW_KeySelection(infoPtr, nItem);
8161 * [I] infoPtr : valid pointer to the listview structure
8166 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8170 /* if we did not have the focus, there's nothing to do */
8171 if (!infoPtr->bFocus) return 0;
8173 /* send NM_KILLFOCUS notification */
8174 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8176 /* if we have a focus rectagle, get rid of it */
8177 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8179 /* set window focus flag */
8180 infoPtr->bFocus = FALSE;
8182 /* invalidate the selected items before reseting focus flag */
8183 LISTVIEW_InvalidateSelectedItems(infoPtr);
8190 * Processes double click messages (left mouse button).
8193 * [I] infoPtr : valid pointer to the listview structure
8194 * [I] wKey : key flag
8195 * [I] x,y : mouse coordinate
8200 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8202 LVHITTESTINFO htInfo;
8204 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8206 /* send NM_RELEASEDCAPTURE notification */
8207 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8212 /* send NM_DBLCLK notification */
8213 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8214 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8216 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8217 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8224 * Processes mouse down messages (left mouse button).
8227 * infoPtr [I ] valid pointer to the listview structure
8228 * wKey [I ] key flag
8229 * x,y [I ] mouse coordinate
8234 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8236 LVHITTESTINFO lvHitTestInfo;
8237 static BOOL bGroupSelect = TRUE;
8238 POINT pt = { x, y };
8241 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8243 /* send NM_RELEASEDCAPTURE notification */
8244 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8246 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8248 /* set left button down flag and record the click position */
8249 infoPtr->bLButtonDown = TRUE;
8250 infoPtr->ptClickPos = pt;
8252 lvHitTestInfo.pt.x = x;
8253 lvHitTestInfo.pt.y = y;
8255 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8256 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
8257 infoPtr->nEditLabelItem = -1;
8258 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8260 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8262 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
8263 if(state == 1 || state == 2)
8267 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
8268 lvitem.stateMask = LVIS_STATEIMAGEMASK;
8269 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
8274 if (infoPtr->dwStyle & LVS_SINGLESEL)
8276 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8277 infoPtr->nEditLabelItem = nItem;
8279 LISTVIEW_SetSelection(infoPtr, nItem);
8283 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8287 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8288 LISTVIEW_SetItemFocus(infoPtr, nItem);
8289 infoPtr->nSelectionMark = nItem;
8295 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8296 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8298 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8299 infoPtr->nSelectionMark = nItem;
8302 else if (wKey & MK_CONTROL)
8306 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8308 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8309 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8310 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8311 infoPtr->nSelectionMark = nItem;
8313 else if (wKey & MK_SHIFT)
8315 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8319 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8320 infoPtr->nEditLabelItem = nItem;
8322 /* set selection (clears other pre-existing selections) */
8323 LISTVIEW_SetSelection(infoPtr, nItem);
8329 /* remove all selections */
8330 LISTVIEW_DeselectAll(infoPtr);
8339 * Processes mouse up messages (left mouse button).
8342 * infoPtr [I ] valid pointer to the listview structure
8343 * wKey [I ] key flag
8344 * x,y [I ] mouse coordinate
8349 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8351 LVHITTESTINFO lvHitTestInfo;
8353 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8355 if (!infoPtr->bLButtonDown) return 0;
8357 lvHitTestInfo.pt.x = x;
8358 lvHitTestInfo.pt.y = y;
8360 /* send NM_CLICK notification */
8361 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8362 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8364 /* set left button flag */
8365 infoPtr->bLButtonDown = FALSE;
8367 /* if we clicked on a selected item, edit the label */
8368 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8369 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
8376 * Destroys the listview control (called after WM_DESTROY).
8379 * [I] infoPtr : valid pointer to the listview structure
8384 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8388 /* delete all items */
8389 LISTVIEW_DeleteAllItems(infoPtr);
8391 /* destroy data structure */
8392 DPA_Destroy(infoPtr->hdpaItems);
8393 DPA_Destroy(infoPtr->hdpaPosX);
8394 DPA_Destroy(infoPtr->hdpaPosY);
8395 DPA_Destroy(infoPtr->hdpaColumns);
8396 ranges_destroy(infoPtr->selectionRanges);
8398 /* destroy image lists */
8399 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8401 if (infoPtr->himlNormal)
8402 ImageList_Destroy(infoPtr->himlNormal);
8403 if (infoPtr->himlSmall)
8404 ImageList_Destroy(infoPtr->himlSmall);
8405 if (infoPtr->himlState)
8406 ImageList_Destroy(infoPtr->himlState);
8409 /* destroy font, bkgnd brush */
8411 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8412 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8414 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8416 /* free listview info pointer*/
8424 * Handles notifications from header.
8427 * [I] infoPtr : valid pointer to the listview structure
8428 * [I] nCtrlId : control identifier
8429 * [I] lpnmh : notification information
8434 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8436 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8437 HWND hwndSelf = infoPtr->hwndSelf;
8439 TRACE("(lpnmh=%p)\n", lpnmh);
8441 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8443 switch (lpnmh->hdr.code)
8448 COLUMN_INFO *lpColumnInfo;
8452 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8455 /* remove the old line (if any) */
8456 LISTVIEW_DrawTrackLine(infoPtr);
8458 /* compute & draw the new line */
8459 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8460 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
8461 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8462 infoPtr->xTrackLine = x + ptOrigin.x;
8463 LISTVIEW_DrawTrackLine(infoPtr);
8469 /* remove the track line (if any) */
8470 LISTVIEW_DrawTrackLine(infoPtr);
8471 infoPtr->xTrackLine = -1;
8475 FIXME("Changing column order not implemented\n");
8478 case HDN_ITEMCHANGINGW:
8479 case HDN_ITEMCHANGINGA:
8480 return notify_forward_header(infoPtr, lpnmh);
8482 case HDN_ITEMCHANGEDW:
8483 case HDN_ITEMCHANGEDA:
8485 COLUMN_INFO *lpColumnInfo;
8488 notify_forward_header(infoPtr, lpnmh);
8489 if (!IsWindow(hwndSelf))
8492 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8496 hdi.mask = HDI_WIDTH;
8497 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, &hdi)) return 0;
8501 cxy = lpnmh->pitem->cxy;
8503 /* determine how much we change since the last know position */
8504 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8505 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8508 lpColumnInfo->rcHeader.right += dx;
8509 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8510 LISTVIEW_UpdateItemSize(infoPtr);
8511 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8514 RECT rcCol = lpColumnInfo->rcHeader;
8516 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8517 OffsetRect(&rcCol, ptOrigin.x, 0);
8519 rcCol.top = infoPtr->rcList.top;
8520 rcCol.bottom = infoPtr->rcList.bottom;
8522 /* resizing left-aligned columns leaves most of the left side untouched */
8523 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8525 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth + dx;
8526 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
8529 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8535 case HDN_ITEMCLICKW:
8536 case HDN_ITEMCLICKA:
8538 /* Handle sorting by Header Column */
8541 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8543 nmlv.iSubItem = lpnmh->iItem;
8544 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8548 case HDN_DIVIDERDBLCLICKW:
8549 case HDN_DIVIDERDBLCLICKA:
8550 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
8559 * Paint non-client area of control.
8562 * [I] infoPtr : valid pointer to the listview structureof the sender
8563 * [I] region : update region
8566 * TRUE - frame was painted
8567 * FALSE - call default window proc
8569 static BOOL LISTVIEW_NCPaint(LISTVIEW_INFO *infoPtr, HRGN region)
8571 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
8575 int cxEdge = GetSystemMetrics (SM_CXEDGE),
8576 cyEdge = GetSystemMetrics (SM_CYEDGE);
8578 if (!theme) return FALSE;
8580 GetWindowRect(infoPtr->hwndSelf, &r);
8582 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
8583 r.right - cxEdge, r.bottom - cyEdge);
8584 if (region != (HRGN)1)
8585 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
8586 OffsetRect(&r, -r.left, -r.top);
8588 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
8589 OffsetRect(&r, -r.left, -r.top);
8591 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
8592 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
8593 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
8594 ReleaseDC(infoPtr->hwndSelf, dc);
8596 /* Call default proc to get the scrollbars etc. painted */
8597 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
8604 * Determines the type of structure to use.
8607 * [I] infoPtr : valid pointer to the listview structureof the sender
8608 * [I] hwndFrom : listview window handle
8609 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8614 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8616 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8618 if (nCommand != NF_REQUERY) return 0;
8620 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8627 * Paints/Repaints the listview control.
8630 * [I] infoPtr : valid pointer to the listview structure
8631 * [I] hdc : device context handle
8636 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8638 TRACE("(hdc=%p)\n", hdc);
8640 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8642 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8644 infoPtr->bNoItemMetrics = FALSE;
8645 LISTVIEW_UpdateItemSize(infoPtr);
8646 if (uView == LVS_ICON || uView == LVS_SMALLICON)
8647 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8648 LISTVIEW_UpdateScroll(infoPtr);
8651 LISTVIEW_Refresh(infoPtr, hdc);
8656 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8658 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8659 LISTVIEW_Refresh(infoPtr, hdc);
8660 EndPaint(infoPtr->hwndSelf, &ps);
8669 * Paints/Repaints the listview control.
8672 * [I] infoPtr : valid pointer to the listview structure
8673 * [I] hdc : device context handle
8674 * [I] options : drawing options
8679 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
8681 FIXME("Partial Stub: (hdc=%p options=0x%08lx)\n", hdc, options);
8683 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
8686 if (options & PRF_ERASEBKGND)
8687 LISTVIEW_EraseBkgnd(infoPtr, hdc);
8689 if (options & PRF_CLIENT)
8690 LISTVIEW_Paint(infoPtr, hdc);
8698 * Processes double click messages (right mouse button).
8701 * [I] infoPtr : valid pointer to the listview structure
8702 * [I] wKey : key flag
8703 * [I] x,y : mouse coordinate
8708 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8710 LVHITTESTINFO lvHitTestInfo;
8712 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8714 /* send NM_RELEASEDCAPTURE notification */
8715 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8717 /* send NM_RDBLCLK notification */
8718 lvHitTestInfo.pt.x = x;
8719 lvHitTestInfo.pt.y = y;
8720 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8721 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8728 * Processes mouse down messages (right mouse button).
8731 * [I] infoPtr : valid pointer to the listview structure
8732 * [I] wKey : key flag
8733 * [I] x,y : mouse coordinate
8738 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8740 LVHITTESTINFO lvHitTestInfo;
8743 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8745 /* send NM_RELEASEDCAPTURE notification */
8746 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8748 /* make sure the listview control window has the focus */
8749 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8751 /* set right button down flag */
8752 infoPtr->bRButtonDown = TRUE;
8754 /* determine the index of the selected item */
8755 lvHitTestInfo.pt.x = x;
8756 lvHitTestInfo.pt.y = y;
8757 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8759 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8761 LISTVIEW_SetItemFocus(infoPtr, nItem);
8762 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8763 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8764 LISTVIEW_SetSelection(infoPtr, nItem);
8768 LISTVIEW_DeselectAll(infoPtr);
8776 * Processes mouse up messages (right mouse button).
8779 * [I] infoPtr : valid pointer to the listview structure
8780 * [I] wKey : key flag
8781 * [I] x,y : mouse coordinate
8786 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8788 LVHITTESTINFO lvHitTestInfo;
8791 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8793 if (!infoPtr->bRButtonDown) return 0;
8795 /* set button flag */
8796 infoPtr->bRButtonDown = FALSE;
8798 /* Send NM_RClICK notification */
8799 lvHitTestInfo.pt.x = x;
8800 lvHitTestInfo.pt.y = y;
8801 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8802 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
8804 /* Change to screen coordinate for WM_CONTEXTMENU */
8805 pt = lvHitTestInfo.pt;
8806 ClientToScreen(infoPtr->hwndSelf, &pt);
8808 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8809 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8810 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8821 * [I] infoPtr : valid pointer to the listview structure
8822 * [I] hwnd : window handle of window containing the cursor
8823 * [I] nHittest : hit-test code
8824 * [I] wMouseMsg : ideintifier of the mouse message
8827 * TRUE if cursor is set
8830 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8832 LVHITTESTINFO lvHitTestInfo;
8834 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8836 if(!infoPtr->hHotCursor) return FALSE;
8838 GetCursorPos(&lvHitTestInfo.pt);
8839 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8841 SetCursor(infoPtr->hHotCursor);
8851 * [I] infoPtr : valid pointer to the listview structure
8852 * [I] hwndLoseFocus : handle of previously focused window
8857 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8859 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8861 /* if we have the focus already, there's nothing to do */
8862 if (infoPtr->bFocus) return 0;
8864 /* send NM_SETFOCUS notification */
8865 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
8867 /* set window focus flag */
8868 infoPtr->bFocus = TRUE;
8870 /* put the focus rect back on */
8871 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8873 /* redraw all visible selected items */
8874 LISTVIEW_InvalidateSelectedItems(infoPtr);
8884 * [I] infoPtr : valid pointer to the listview structure
8885 * [I] fRedraw : font handle
8886 * [I] fRedraw : redraw flag
8891 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8893 HFONT oldFont = infoPtr->hFont;
8895 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8897 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8898 if (infoPtr->hFont == oldFont) return 0;
8900 LISTVIEW_SaveTextMetrics(infoPtr);
8902 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8903 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8905 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8912 * Message handling for WM_SETREDRAW.
8913 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8916 * [I] infoPtr : valid pointer to the listview structure
8917 * [I] bRedraw: state of redraw flag
8920 * DefWinProc return value
8922 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8924 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8926 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
8927 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8929 infoPtr->bRedraw = bRedraw;
8931 if(!bRedraw) return 0;
8933 if (is_autoarrange(infoPtr))
8934 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8935 LISTVIEW_UpdateScroll(infoPtr);
8937 /* despite what the WM_SETREDRAW docs says, apps expect us
8938 * to invalidate the listview here... stupid! */
8939 LISTVIEW_InvalidateList(infoPtr);
8946 * Resizes the listview control. This function processes WM_SIZE
8947 * messages. At this time, the width and height are not used.
8950 * [I] infoPtr : valid pointer to the listview structure
8951 * [I] Width : new width
8952 * [I] Height : new height
8957 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8959 RECT rcOld = infoPtr->rcList;
8961 TRACE("(width=%d, height=%d)\n", Width, Height);
8963 LISTVIEW_UpdateSize(infoPtr);
8964 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8966 /* do not bother with display related stuff if we're not redrawing */
8967 if (!is_redrawing(infoPtr)) return 0;
8969 if (is_autoarrange(infoPtr))
8970 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8972 LISTVIEW_UpdateScroll(infoPtr);
8974 /* refresh all only for lists whose height changed significantly */
8975 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
8976 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8977 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8978 LISTVIEW_InvalidateList(infoPtr);
8985 * Sets the size information.
8988 * [I] infoPtr : valid pointer to the listview structure
8993 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8995 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8997 TRACE("uView=%d, rcList(old)=%s\n", uView, wine_dbgstr_rect(&infoPtr->rcList));
8999 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
9001 if (uView == LVS_LIST)
9003 /* Apparently the "LIST" style is supposed to have the same
9004 * number of items in a column even if there is no scroll bar.
9005 * Since if a scroll bar already exists then the bottom is already
9006 * reduced, only reduce if the scroll bar does not currently exist.
9007 * The "2" is there to mimic the native control. I think it may be
9008 * related to either padding or edges. (GLA 7/2002)
9010 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
9011 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
9012 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
9014 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
9019 hl.prc = &infoPtr->rcList;
9021 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9023 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9025 infoPtr->rcList.top = max(wp.cy, 0);
9028 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
9033 * Processes WM_STYLECHANGED messages.
9036 * [I] infoPtr : valid pointer to the listview structure
9037 * [I] wStyleType : window style type (normal or extended)
9038 * [I] lpss : window style information
9043 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
9044 const STYLESTRUCT *lpss)
9046 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9047 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9049 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
9050 wStyleType, lpss->styleOld, lpss->styleNew);
9052 if (wStyleType != GWL_STYLE) return 0;
9054 /* FIXME: if LVS_NOSORTHEADER changed, update header */
9055 /* what if LVS_OWNERDATA changed? */
9056 /* or LVS_SINGLESEL */
9057 /* or LVS_SORT{AS,DES}CENDING */
9059 infoPtr->dwStyle = lpss->styleNew;
9061 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9062 ((lpss->styleNew & WS_HSCROLL) == 0))
9063 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9065 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9066 ((lpss->styleNew & WS_VSCROLL) == 0))
9067 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9069 if (uNewView != uOldView)
9071 SIZE oldIconSize = infoPtr->iconSize;
9074 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9075 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9077 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9078 SetRectEmpty(&infoPtr->rcFocus);
9080 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9081 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
9083 if (uNewView == LVS_ICON)
9085 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
9087 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
9088 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9089 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
9092 else if (uNewView == LVS_REPORT)
9097 hl.prc = &infoPtr->rcList;
9099 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9100 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9103 LISTVIEW_UpdateItemSize(infoPtr);
9106 if (uNewView == LVS_REPORT)
9107 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
9109 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9110 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9111 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9113 /* update the size of the client area */
9114 LISTVIEW_UpdateSize(infoPtr);
9116 /* add scrollbars if needed */
9117 LISTVIEW_UpdateScroll(infoPtr);
9119 /* invalidate client area + erase background */
9120 LISTVIEW_InvalidateList(infoPtr);
9127 * Window procedure of the listview control.
9130 static LRESULT WINAPI
9131 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9133 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9135 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
9137 if (!infoPtr && (uMsg != WM_CREATE))
9138 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9142 case LVM_APPROXIMATEVIEWRECT:
9143 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9144 LOWORD(lParam), HIWORD(lParam));
9146 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9148 /* case LVM_CANCELEDITLABEL: */
9150 case LVM_CREATEDRAGIMAGE:
9151 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9153 case LVM_DELETEALLITEMS:
9154 return LISTVIEW_DeleteAllItems(infoPtr);
9156 case LVM_DELETECOLUMN:
9157 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9159 case LVM_DELETEITEM:
9160 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9162 case LVM_EDITLABELW:
9163 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9165 case LVM_EDITLABELA:
9166 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9168 /* case LVM_ENABLEGROUPVIEW: */
9170 case LVM_ENSUREVISIBLE:
9171 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9174 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9177 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9179 case LVM_GETBKCOLOR:
9180 return infoPtr->clrBk;
9182 /* case LVM_GETBKIMAGE: */
9184 case LVM_GETCALLBACKMASK:
9185 return infoPtr->uCallbackMask;
9187 case LVM_GETCOLUMNA:
9188 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9190 case LVM_GETCOLUMNW:
9191 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9193 case LVM_GETCOLUMNORDERARRAY:
9194 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9196 case LVM_GETCOLUMNWIDTH:
9197 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9199 case LVM_GETCOUNTPERPAGE:
9200 return LISTVIEW_GetCountPerPage(infoPtr);
9202 case LVM_GETEDITCONTROL:
9203 return (LRESULT)infoPtr->hwndEdit;
9205 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9206 return infoPtr->dwLvExStyle;
9208 /* case LVM_GETGROUPINFO: */
9210 /* case LVM_GETGROUPMETRICS: */
9213 return (LRESULT)infoPtr->hwndHeader;
9215 case LVM_GETHOTCURSOR:
9216 return (LRESULT)infoPtr->hHotCursor;
9218 case LVM_GETHOTITEM:
9219 return infoPtr->nHotItem;
9221 case LVM_GETHOVERTIME:
9222 return infoPtr->dwHoverTime;
9224 case LVM_GETIMAGELIST:
9225 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9227 /* case LVM_GETINSERTMARK: */
9229 /* case LVM_GETINSERTMARKCOLOR: */
9231 /* case LVM_GETINSERTMARKRECT: */
9233 case LVM_GETISEARCHSTRINGA:
9234 case LVM_GETISEARCHSTRINGW:
9235 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9239 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9242 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9244 case LVM_GETITEMCOUNT:
9245 return infoPtr->nItemCount;
9247 case LVM_GETITEMPOSITION:
9248 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9250 case LVM_GETITEMRECT:
9251 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9253 case LVM_GETITEMSPACING:
9254 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9256 case LVM_GETITEMSTATE:
9257 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9259 case LVM_GETITEMTEXTA:
9260 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9262 case LVM_GETITEMTEXTW:
9263 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9265 case LVM_GETNEXTITEM:
9266 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9268 case LVM_GETNUMBEROFWORKAREAS:
9269 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9273 if (!lParam) return FALSE;
9274 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9277 /* case LVM_GETOUTLINECOLOR: */
9279 /* case LVM_GETSELECTEDCOLUMN: */
9281 case LVM_GETSELECTEDCOUNT:
9282 return LISTVIEW_GetSelectedCount(infoPtr);
9284 case LVM_GETSELECTIONMARK:
9285 return infoPtr->nSelectionMark;
9287 case LVM_GETSTRINGWIDTHA:
9288 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9290 case LVM_GETSTRINGWIDTHW:
9291 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9293 case LVM_GETSUBITEMRECT:
9294 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
9296 case LVM_GETTEXTBKCOLOR:
9297 return infoPtr->clrTextBk;
9299 case LVM_GETTEXTCOLOR:
9300 return infoPtr->clrText;
9302 /* case LVM_GETTILEINFO: */
9304 /* case LVM_GETTILEVIEWINFO: */
9306 case LVM_GETTOOLTIPS:
9307 if( !infoPtr->hwndToolTip )
9308 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9309 return (LRESULT)infoPtr->hwndToolTip;
9311 case LVM_GETTOPINDEX:
9312 return LISTVIEW_GetTopIndex(infoPtr);
9314 /*case LVM_GETUNICODEFORMAT:
9315 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9318 /* case LVM_GETVIEW: */
9320 case LVM_GETVIEWRECT:
9321 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9323 case LVM_GETWORKAREAS:
9324 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9327 /* case LVM_HASGROUP: */
9330 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9332 case LVM_INSERTCOLUMNA:
9333 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9335 case LVM_INSERTCOLUMNW:
9336 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9338 /* case LVM_INSERTGROUP: */
9340 /* case LVM_INSERTGROUPSORTED: */
9342 case LVM_INSERTITEMA:
9343 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9345 case LVM_INSERTITEMW:
9346 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9348 /* case LVM_INSERTMARKHITTEST: */
9350 /* case LVM_ISGROUPVIEWENABLED: */
9352 /* case LVM_MAPIDTOINDEX: */
9354 /* case LVM_MAPINDEXTOID: */
9356 /* case LVM_MOVEGROUP: */
9358 /* case LVM_MOVEITEMTOGROUP: */
9360 case LVM_REDRAWITEMS:
9361 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9363 /* case LVM_REMOVEALLGROUPS: */
9365 /* case LVM_REMOVEGROUP: */
9368 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9370 case LVM_SETBKCOLOR:
9371 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9373 /* case LVM_SETBKIMAGE: */
9375 case LVM_SETCALLBACKMASK:
9376 infoPtr->uCallbackMask = (UINT)wParam;
9379 case LVM_SETCOLUMNA:
9380 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9382 case LVM_SETCOLUMNW:
9383 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9385 case LVM_SETCOLUMNORDERARRAY:
9386 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9388 case LVM_SETCOLUMNWIDTH:
9389 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9391 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9392 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9394 /* case LVM_SETGROUPINFO: */
9396 /* case LVM_SETGROUPMETRICS: */
9398 case LVM_SETHOTCURSOR:
9399 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9401 case LVM_SETHOTITEM:
9402 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9404 case LVM_SETHOVERTIME:
9405 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9407 case LVM_SETICONSPACING:
9408 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9410 case LVM_SETIMAGELIST:
9411 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9413 /* case LVM_SETINFOTIP: */
9415 /* case LVM_SETINSERTMARK: */
9417 /* case LVM_SETINSERTMARKCOLOR: */
9420 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9423 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9425 case LVM_SETITEMCOUNT:
9426 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9428 case LVM_SETITEMPOSITION:
9431 pt.x = (short)LOWORD(lParam);
9432 pt.y = (short)HIWORD(lParam);
9433 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9436 case LVM_SETITEMPOSITION32:
9437 if (lParam == 0) return FALSE;
9438 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9440 case LVM_SETITEMSTATE:
9441 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9443 case LVM_SETITEMTEXTA:
9444 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9446 case LVM_SETITEMTEXTW:
9447 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9449 /* case LVM_SETOUTLINECOLOR: */
9451 /* case LVM_SETSELECTEDCOLUMN: */
9453 case LVM_SETSELECTIONMARK:
9454 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9456 case LVM_SETTEXTBKCOLOR:
9457 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9459 case LVM_SETTEXTCOLOR:
9460 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9462 /* case LVM_SETTILEINFO: */
9464 /* case LVM_SETTILEVIEWINFO: */
9466 /* case LVM_SETTILEWIDTH: */
9468 case LVM_SETTOOLTIPS:
9469 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9471 /* case LVM_SETUNICODEFORMAT: */
9473 /* case LVM_SETVIEW: */
9475 /* case LVM_SETWORKAREAS: */
9477 /* case LVM_SORTGROUPS: */
9480 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9482 /* LVM_SORTITEMSEX: */
9484 case LVM_SUBITEMHITTEST:
9485 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9488 return LISTVIEW_Update(infoPtr, (INT)wParam);
9491 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9494 return LISTVIEW_Command(infoPtr, wParam, lParam);
9497 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9500 return LISTVIEW_Destroy(infoPtr);
9503 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
9506 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9509 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9512 return (LRESULT)infoPtr->hFont;
9515 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9518 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9521 return LISTVIEW_KillFocus(infoPtr);
9523 case WM_LBUTTONDBLCLK:
9524 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9526 case WM_LBUTTONDOWN:
9527 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9530 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9533 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9536 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9539 return LISTVIEW_NCDestroy(infoPtr);
9542 if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
9547 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9548 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9551 case WM_NOTIFYFORMAT:
9552 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9554 case WM_PRINTCLIENT:
9555 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
9558 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9560 case WM_RBUTTONDBLCLK:
9561 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9563 case WM_RBUTTONDOWN:
9564 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9567 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9570 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9575 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9578 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9581 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9584 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9586 case WM_STYLECHANGED:
9587 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9589 case WM_SYSCOLORCHANGE:
9590 COMCTL32_RefreshSysColors();
9593 /* case WM_TIMER: */
9594 case WM_THEMECHANGED:
9595 return LISTVIEW_ThemeChanged(infoPtr);
9598 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9601 if (wParam & (MK_SHIFT | MK_CONTROL))
9602 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9603 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9605 case WM_WINDOWPOSCHANGED:
9606 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
9608 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9609 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9610 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9612 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
9614 MEASUREITEMSTRUCT mis;
9615 mis.CtlType = ODT_LISTVIEW;
9616 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
9620 mis.itemHeight= infoPtr->nItemHeight;
9621 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
9622 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
9623 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
9626 LISTVIEW_UpdateSize(infoPtr);
9627 LISTVIEW_UpdateScroll(infoPtr);
9629 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9631 /* case WM_WININICHANGE: */
9634 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9635 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9638 /* call default window procedure */
9639 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9646 * Registers the window class.
9654 void LISTVIEW_Register(void)
9658 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9659 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9660 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
9661 wndClass.cbClsExtra = 0;
9662 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9663 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9664 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9665 wndClass.lpszClassName = WC_LISTVIEWW;
9666 RegisterClassW(&wndClass);
9671 * Unregisters the window class.
9679 void LISTVIEW_Unregister(void)
9681 UnregisterClassW(WC_LISTVIEWW, NULL);
9686 * Handle any WM_COMMAND messages
9689 * [I] infoPtr : valid pointer to the listview structure
9690 * [I] wParam : the first message parameter
9691 * [I] lParam : the second message parameter
9696 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9698 switch (HIWORD(wParam))
9703 * Adjust the edit window size
9706 HDC hdc = GetDC(infoPtr->hwndEdit);
9707 HFONT hFont, hOldFont = 0;
9712 if (!infoPtr->hwndEdit || !hdc) return 0;
9713 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9714 GetWindowRect(infoPtr->hwndEdit, &rect);
9716 /* Select font to get the right dimension of the string */
9717 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9720 hOldFont = SelectObject(hdc, hFont);
9723 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9725 TEXTMETRICW textMetric;
9727 /* Add Extra spacing for the next character */
9728 GetTextMetricsW(hdc, &textMetric);
9729 sz.cx += (textMetric.tmMaxCharWidth * 2);
9737 rect.bottom - rect.top,
9738 SWP_DRAWFRAME|SWP_NOMOVE);
9741 SelectObject(hdc, hOldFont);
9743 ReleaseDC(infoPtr->hwndEdit, hdc);
9749 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
9758 * Subclassed edit control windproc function
9761 * [I] hwnd : the edit window handle
9762 * [I] uMsg : the message that is to be processed
9763 * [I] wParam : first message parameter
9764 * [I] lParam : second message parameter
9765 * [I] isW : TRUE if input is Unicode
9770 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9772 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
9773 BOOL cancel = FALSE;
9775 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9776 hwnd, uMsg, wParam, lParam, isW);
9781 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9788 WNDPROC editProc = infoPtr->EditWndProc;
9789 infoPtr->EditWndProc = 0;
9790 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
9791 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9795 if (VK_ESCAPE == (INT)wParam)
9800 else if (VK_RETURN == (INT)wParam)
9804 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9808 if (infoPtr->hwndEdit)
9810 LPWSTR buffer = NULL;
9812 infoPtr->hwndEdit = 0;
9815 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9819 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9821 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9822 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9826 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9828 if (buffer) Free(buffer);
9832 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9838 * Subclassed edit control Unicode windproc function
9841 * [I] hwnd : the edit window handle
9842 * [I] uMsg : the message that is to be processed
9843 * [I] wParam : first message parameter
9844 * [I] lParam : second message parameter
9848 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9850 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9855 * Subclassed edit control ANSI windproc function
9858 * [I] hwnd : the edit window handle
9859 * [I] uMsg : the message that is to be processed
9860 * [I] wParam : first message parameter
9861 * [I] lParam : second message parameter
9865 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9867 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9872 * Creates a subclassed edit cotrol
9875 * [I] infoPtr : valid pointer to the listview structure
9876 * [I] text : initial text for the edit
9877 * [I] style : the window style
9878 * [I] isW : TRUE if input is Unicode
9882 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9883 INT x, INT y, INT width, INT height, BOOL isW)
9885 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9890 TEXTMETRICW textMetric;
9891 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
9893 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9895 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
9896 hdc = GetDC(infoPtr->hwndSelf);
9898 /* Select the font to get appropriate metric dimensions */
9899 if(infoPtr->hFont != 0)
9900 hOldFont = SelectObject(hdc, infoPtr->hFont);
9902 /*Get String Length in pixels */
9903 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9905 /*Add Extra spacing for the next character */
9906 GetTextMetricsW(hdc, &textMetric);
9907 sz.cx += (textMetric.tmMaxCharWidth * 2);
9909 if(infoPtr->hFont != 0)
9910 SelectObject(hdc, hOldFont);
9912 ReleaseDC(infoPtr->hwndSelf, hdc);
9914 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9916 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9918 if (!hedit) return 0;
9920 infoPtr->EditWndProc = (WNDPROC)
9921 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
9922 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
9924 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);