4 * Copyright 1998, 1999 Eric Kohl
5 * Copyright 1999 Luc Tourangeau
6 * Copyright 2000 Jason Mawdsley
7 * Copyright 2001 CodeWeavers Inc.
8 * Copyright 2002 Dimitrie O. Paun
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 * This code was audited for completeness against the documented features
27 * of Comctl32.dll version 6.0 on May. 20, 2005, by James Hawkins.
29 * Unless otherwise noted, we believe this code to be complete, as per
30 * the specification mentioned above.
31 * If you discover missing features, or bugs, please note them below.
35 * Default Message Processing
36 * -- EN_KILLFOCUS should be handled in WM_COMMAND
37 * -- WM_CREATE: create the icon and small icon image lists at this point only if
38 * the LVS_SHAREIMAGELISTS style is not specified.
39 * -- WM_ERASEBKGND: forward this message to the parent window if the bkgnd
41 * -- WM_WINDOWPOSCHANGED: arrange the list items if the current view is icon
42 * or small icon and the LVS_AUTOARRANGE style is specified.
47 * -- Hot item handling, mouse hovering
48 * -- Workareas support
53 * -- Expand large item in ICON mode when the cursor is flying over the icon or text.
54 * -- Support CustomDraw options for _WIN32_IE >= 0x560 (see NMLVCUSTOMDRAW docs).
55 * -- LVA_SNAPTOGRID not implemented
56 * -- LISTVIEW_ApproximateViewRect partially implemented
57 * -- LISTVIEW_[GS]etColumnOrderArray stubs
58 * -- LISTVIEW_SetColumnWidth ignores header images & bitmap
59 * -- LISTVIEW_SetIconSpacing is incomplete
60 * -- LISTVIEW_SortItems is broken
61 * -- LISTVIEW_StyleChanged doesn't handle some changes too well
64 * -- LISTVIEW_GetNextItem needs to be rewritten. It is currently
65 * linear in the number of items in the list, and this is
66 * unacceptable for large lists.
67 * -- in sorted mode, LISTVIEW_InsertItemT sorts the array,
68 * instead of inserting in the right spot
69 * -- we should keep an ordered array of coordinates in iconic mode
70 * this would allow to frame items (iterator_frameditems),
71 * and find nearest item (LVFI_NEARESTXY) a lot more efficiently
79 * -- LVIS_ACTIVATING (not currently supported by comctl32.dll version 6.0)
86 * -- LVS_NOSCROLL (see Q137520)
87 * -- LVS_SORTASCENDING, LVS_SORTDESCENDING
89 * -- LVS_TYPESTYLEMASK
92 * -- LVS_EX_BORDERSELECT
95 * -- LVS_EX_HEADERDRAGDROP
98 * -- LVS_EX_MULTIWORKAREAS
99 * -- LVS_EX_ONECLICKACTIVATE
101 * -- LVS_EX_SIMPLESELECT
102 * -- LVS_EX_TRACKSELECT
103 * -- LVS_EX_TWOCLICKACTIVATE
104 * -- LVS_EX_UNDERLINECOLD
105 * -- LVS_EX_UNDERLINEHOT
108 * -- LVN_BEGINSCROLL, LVN_ENDSCROLL
111 * -- LVN_MARQUEEBEGIN
118 * -- LVM_CANCELEDITLABEL
119 * -- LVM_ENABLEGROUPVIEW
120 * -- LVM_GETBKIMAGE, LVM_SETBKIMAGE
121 * -- LVM_GETGROUPINFO, LVM_SETGROUPINFO
122 * -- LVM_GETGROUPMETRICS, LVM_SETGROUPMETRICS
123 * -- LVM_GETINSERTMARK, LVM_SETINSERTMARK
124 * -- LVM_GETINSERTMARKCOLOR, LVM_SETINSERTMARKCOLOR
125 * -- LVM_GETINSERTMARKRECT
126 * -- LVM_GETNUMBEROFWORKAREAS
127 * -- LVM_GETOUTLINECOLOR, LVM_SETOUTLINECOLOR
128 * -- LVM_GETSELECTEDCOLUMN, LVM_SETSELECTEDCOLUMN
129 * -- LVM_GETISEARCHSTRINGW, LVM_GETISEARCHSTRINGA
130 * -- LVM_GETTILEINFO, LVM_SETTILEINFO
131 * -- LVM_GETTILEVIEWINFO, LVM_SETTILEVIEWINFO
132 * -- LVM_GETUNICODEFORMAT, LVM_SETUNICODEFORMAT
133 * -- LVM_GETVIEW, LVM_SETVIEW
134 * -- LVM_GETWORKAREAS, LVM_SETWORKAREAS
135 * -- LVM_HASGROUP, LVM_INSERTGROUP, LVM_REMOVEGROUP, LVM_REMOVEALLGROUPS
136 * -- LVM_INSERTGROUPSORTED
137 * -- LVM_INSERTMARKHITTEST
138 * -- LVM_ISGROUPVIEWENABLED
139 * -- LVM_MAPIDTOINDEX, LVM_MAPINDEXTOID
141 * -- LVM_MOVEITEMTOGROUP
143 * -- LVM_SETTILEWIDTH
148 * -- ListView_GetCheckSate, ListView_SetCheckState
149 * -- ListView_GetHoverTime, ListView_SetHoverTime
150 * -- ListView_GetISearchString
151 * -- ListView_GetNumberOfWorkAreas
152 * -- ListView_GetOrigin
153 * -- ListView_GetTextBkColor
154 * -- ListView_GetUnicodeFormat, ListView_SetUnicodeFormat
155 * -- ListView_GetWorkAreas, ListView_SetWorkAreas
156 * -- ListView_SortItemsEx
161 * Known differences in message stream from native control (not known if
162 * these differences cause problems):
163 * LVM_INSERTITEM issues LVM_SETITEMSTATE and LVM_SETITEM in certain cases.
164 * LVM_SETITEM does not always issue LVN_ITEMCHANGING/LVN_ITEMCHANGED.
165 * WM_CREATE does not issue WM_QUERYUISTATE and associated registry
166 * processing for "USEDOUBLECLICKTIME".
170 #include "wine/port.h"
185 #include "commctrl.h"
186 #include "comctl32.h"
189 #include "wine/debug.h"
190 #include "wine/unicode.h"
192 WINE_DEFAULT_DEBUG_CHANNEL(listview);
194 /* make sure you set this to 0 for production use! */
195 #define DEBUG_RANGES 1
197 typedef struct tagCOLUMN_INFO
199 RECT rcHeader; /* tracks the header's rectangle */
200 int fmt; /* same as LVCOLUMN.fmt */
203 typedef struct tagITEMHDR
207 } ITEMHDR, *LPITEMHDR;
209 typedef struct tagSUBITEM_INFO
215 typedef struct tagITEM_INFO
223 typedef struct tagRANGE
229 typedef struct tagRANGES
234 typedef struct tagITERATOR
243 typedef struct tagLISTVIEW_INFO
250 COLORREF clrTextBkDefault;
251 HIMAGELIST himlNormal;
252 HIMAGELIST himlSmall;
253 HIMAGELIST himlState;
256 POINT ptClickPos; /* point where the user clicked */
257 BOOL bNoItemMetrics; /* flags if item metrics are not yet computed */
260 RANGES selectionRanges;
265 RECT rcList; /* This rectangle is really the window
266 * client rectangle possibly reduced by the
267 * horizontal scroll bar and/or header - see
268 * LISTVIEW_UpdateSize. This rectangle offset
269 * by the LISTVIEW_GetOrigin value is in
270 * client coordinates */
279 INT ntmHeight; /* Some cached metrics of the font used */
280 INT ntmMaxCharWidth; /* by the listview to draw items */
282 BOOL bRedraw; /* Turns on/off repaints & invalidations */
283 BOOL bAutoarrange; /* Autoarrange flag when NOT in LVS_AUTOARRANGE */
285 BOOL bDoChangeNotify; /* send change notification messages? */
288 DWORD dwStyle; /* the cached window GWL_STYLE */
289 DWORD dwLvExStyle; /* extended listview style */
290 INT nItemCount; /* the number of items in the list */
291 HDPA hdpaItems; /* array ITEM_INFO pointers */
292 HDPA hdpaPosX; /* maintains the (X, Y) coordinates of the */
293 HDPA hdpaPosY; /* items in LVS_ICON, and LVS_SMALLICON modes */
294 HDPA hdpaColumns; /* array of COLUMN_INFO pointers */
295 POINT currIconPos; /* this is the position next icon will be placed */
296 PFNLVCOMPARE pfnCompare;
304 DWORD cditemmode; /* Keep the custom draw flags for an item/row */
306 DWORD lastKeyPressTimestamp;
308 INT nSearchParamLength;
309 WCHAR szSearchParam[ MAX_PATH ];
311 INT nMeasureItemHeight;
312 INT xTrackLine; /* The x coefficient of the track line or -1 if none */
318 /* How many we debug buffer to allocate */
319 #define DEBUG_BUFFERS 20
320 /* The size of a single debug bbuffer */
321 #define DEBUG_BUFFER_SIZE 256
323 /* Internal interface to LISTVIEW_HScroll and LISTVIEW_VScroll */
324 #define SB_INTERNAL -1
326 /* maximum size of a label */
327 #define DISP_TEXT_SIZE 512
329 /* padding for items in list and small icon display modes */
330 #define WIDTH_PADDING 12
332 /* padding for items in list, report and small icon display modes */
333 #define HEIGHT_PADDING 1
335 /* offset of items in report display mode */
336 #define REPORT_MARGINX 2
338 /* padding for icon in large icon display mode
339 * ICON_TOP_PADDING_NOTHITABLE - space between top of box and area
340 * that HITTEST will see.
341 * ICON_TOP_PADDING_HITABLE - spacing between above and icon.
342 * ICON_TOP_PADDING - sum of the two above.
343 * ICON_BOTTOM_PADDING - between bottom of icon and top of text
344 * LABEL_HOR_PADDING - between text and sides of box
345 * LABEL_VERT_PADDING - between bottom of text and end of box
347 * ICON_LR_PADDING - additional width above icon size.
348 * ICON_LR_HALF - half of the above value
350 #define ICON_TOP_PADDING_NOTHITABLE 2
351 #define ICON_TOP_PADDING_HITABLE 2
352 #define ICON_TOP_PADDING (ICON_TOP_PADDING_NOTHITABLE + ICON_TOP_PADDING_HITABLE)
353 #define ICON_BOTTOM_PADDING 4
354 #define LABEL_HOR_PADDING 5
355 #define LABEL_VERT_PADDING 7
356 #define ICON_LR_PADDING 16
357 #define ICON_LR_HALF (ICON_LR_PADDING/2)
359 /* default label width for items in list and small icon display modes */
360 #define DEFAULT_LABEL_WIDTH 40
362 /* default column width for items in list display mode */
363 #define DEFAULT_COLUMN_WIDTH 128
365 /* Size of "line" scroll for V & H scrolls */
366 #define LISTVIEW_SCROLL_ICON_LINE_SIZE 37
368 /* Padding betwen image and label */
369 #define IMAGE_PADDING 2
371 /* Padding behind the label */
372 #define TRAILING_LABEL_PADDING 12
373 #define TRAILING_HEADER_PADDING 11
375 /* Border for the icon caption */
376 #define CAPTION_BORDER 2
378 /* Standard DrawText flags */
379 #define LV_ML_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
380 #define LV_FL_DT_FLAGS (DT_TOP | DT_NOPREFIX | DT_EDITCONTROL | DT_CENTER | DT_WORDBREAK | DT_NOCLIP)
381 #define LV_SL_DT_FLAGS (DT_VCENTER | DT_NOPREFIX | DT_EDITCONTROL | DT_SINGLELINE | DT_WORD_ELLIPSIS | DT_END_ELLIPSIS)
383 /* Image index from state */
384 #define STATEIMAGEINDEX(x) (((x) & LVIS_STATEIMAGEMASK) >> 12)
386 /* The time in milliseconds to reset the search in the list */
387 #define KEY_DELAY 450
389 /* Dump the LISTVIEW_INFO structure to the debug channel */
390 #define LISTVIEW_DUMP(iP) do { \
391 TRACE("hwndSelf=%p, clrBk=0x%06lx, clrText=0x%06lx, clrTextBk=0x%06lx, ItemHeight=%d, ItemWidth=%d, Style=0x%08lx\n", \
392 iP->hwndSelf, iP->clrBk, iP->clrText, iP->clrTextBk, \
393 iP->nItemHeight, iP->nItemWidth, iP->dwStyle); \
394 TRACE("hwndSelf=%p, himlNor=%p, himlSml=%p, himlState=%p, Focused=%d, Hot=%d, exStyle=0x%08lx, Focus=%d\n", \
395 iP->hwndSelf, iP->himlNormal, iP->himlSmall, iP->himlState, \
396 iP->nFocusedItem, iP->nHotItem, iP->dwLvExStyle, iP->bFocus ); \
397 TRACE("hwndSelf=%p, ntmH=%d, icSz.cx=%ld, icSz.cy=%ld, icSp.cx=%ld, icSp.cy=%ld, notifyFmt=%d\n", \
398 iP->hwndSelf, iP->ntmHeight, iP->iconSize.cx, iP->iconSize.cy, \
399 iP->iconSpacing.cx, iP->iconSpacing.cy, iP->notifyFormat); \
400 TRACE("hwndSelf=%p, rcList=%s\n", iP->hwndSelf, wine_dbgstr_rect(&iP->rcList)); \
403 static const WCHAR themeClass[] = {'L','i','s','t','V','i','e','w',0};
406 * forward declarations
408 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *, LPLVITEMW, BOOL);
409 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *, INT, LPRECT);
410 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *, INT, LPPOINT);
411 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *, INT, LPPOINT);
412 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *, INT, LPRECT);
413 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *, INT);
414 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *, LPPOINT);
415 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *, LPRECT);
416 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *, INT);
417 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *, const LVITEMW *, BOOL);
418 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *);
419 static void LISTVIEW_SetSelection(LISTVIEW_INFO *, INT);
420 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *);
421 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *, INT, BOOL);
422 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *, WPARAM, LPARAM);
423 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *, PFNLVCOMPARE, LPARAM);
424 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *, LPCWSTR, BOOL);
425 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *, INT);
426 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *, INT, UINT);
427 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *, INT, const LVITEMW *);
428 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *, INT, INT, HWND);
429 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *, INT, INT, HWND);
430 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *);
431 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *, INT, BOOL);
432 static HWND CreateEditLabelT(LISTVIEW_INFO *, LPCWSTR, DWORD, INT, INT, INT, INT, BOOL);
433 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *, INT, HIMAGELIST);
434 static INT LISTVIEW_HitTest(LISTVIEW_INFO *, LPLVHITTESTINFO, BOOL, BOOL);
436 /******** Text handling functions *************************************/
438 /* A text pointer is either NULL, LPSTR_TEXTCALLBACK, or points to a
439 * text string. The string may be ANSI or Unicode, in which case
440 * the boolean isW tells us the type of the string.
442 * The name of the function tell what type of strings it expects:
443 * W: Unicode, T: ANSI/Unicode - function of isW
446 static inline BOOL is_textW(LPCWSTR text)
448 return text != NULL && text != LPSTR_TEXTCALLBACKW;
451 static inline BOOL is_textT(LPCWSTR text, BOOL isW)
453 /* we can ignore isW since LPSTR_TEXTCALLBACKW == LPSTR_TEXTCALLBACKA */
454 return is_textW(text);
457 static inline int textlenT(LPCWSTR text, BOOL isW)
459 return !is_textT(text, isW) ? 0 :
460 isW ? lstrlenW(text) : lstrlenA((LPCSTR)text);
463 static inline void textcpynT(LPWSTR dest, BOOL isDestW, LPCWSTR src, BOOL isSrcW, INT max)
466 if (isSrcW) lstrcpynW(dest, src, max);
467 else MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, dest, max);
469 if (isSrcW) WideCharToMultiByte(CP_ACP, 0, src, -1, (LPSTR)dest, max, NULL, NULL);
470 else lstrcpynA((LPSTR)dest, (LPCSTR)src, max);
473 static inline LPWSTR textdupTtoW(LPCWSTR text, BOOL isW)
475 LPWSTR wstr = (LPWSTR)text;
477 if (!isW && is_textT(text, isW))
479 INT len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, NULL, 0);
480 wstr = Alloc(len * sizeof(WCHAR));
481 if (wstr) MultiByteToWideChar(CP_ACP, 0, (LPCSTR)text, -1, wstr, len);
483 TRACE(" wstr=%s\n", text == LPSTR_TEXTCALLBACKW ? "(callback)" : debugstr_w(wstr));
487 static inline void textfreeT(LPWSTR wstr, BOOL isW)
489 if (!isW && is_textT(wstr, isW)) Free (wstr);
493 * dest is a pointer to a Unicode string
494 * src is a pointer to a string (Unicode if isW, ANSI if !isW)
496 static BOOL textsetptrT(LPWSTR *dest, LPWSTR src, BOOL isW)
500 if (src == LPSTR_TEXTCALLBACKW)
502 if (is_textW(*dest)) Free(*dest);
503 *dest = LPSTR_TEXTCALLBACKW;
507 LPWSTR pszText = textdupTtoW(src, isW);
508 if (*dest == LPSTR_TEXTCALLBACKW) *dest = NULL;
509 bResult = Str_SetPtrW(dest, pszText);
510 textfreeT(pszText, isW);
516 * compares a Unicode to a Unicode/ANSI text string
518 static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
520 if (!aw) return bt ? -1 : 0;
521 if (!bt) return aw ? 1 : 0;
522 if (aw == LPSTR_TEXTCALLBACKW)
523 return bt == LPSTR_TEXTCALLBACKW ? 0 : -1;
524 if (bt != LPSTR_TEXTCALLBACKW)
526 LPWSTR bw = textdupTtoW(bt, isW);
527 int r = bw ? lstrcmpW(aw, bw) : 1;
535 static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
539 n = min(min(n, strlenW(s1)), strlenW(s2));
540 res = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n);
541 return res ? res - sizeof(WCHAR) : res;
544 /******** Debugging functions *****************************************/
546 static inline LPCSTR debugtext_t(LPCWSTR text, BOOL isW)
548 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
549 return isW ? debugstr_w(text) : debugstr_a((LPCSTR)text);
552 static inline LPCSTR debugtext_tn(LPCWSTR text, BOOL isW, INT n)
554 if (text == LPSTR_TEXTCALLBACKW) return "(callback)";
555 n = min(textlenT(text, isW), n);
556 return isW ? debugstr_wn(text, n) : debugstr_an((LPCSTR)text, n);
559 static char* debug_getbuf(void)
561 static int index = 0;
562 static char buffers[DEBUG_BUFFERS][DEBUG_BUFFER_SIZE];
563 return buffers[index++ % DEBUG_BUFFERS];
566 static inline const char* debugrange(const RANGE *lprng)
568 if (!lprng) return "(null)";
569 return wine_dbg_sprintf("[%d, %d)", lprng->lower, lprng->upper);
572 static const char* debugscrollinfo(const SCROLLINFO *pScrollInfo)
574 char* buf = debug_getbuf(), *text = buf;
575 int len, size = DEBUG_BUFFER_SIZE;
577 if (pScrollInfo == NULL) return "(null)";
578 len = snprintf(buf, size, "{cbSize=%d, ", pScrollInfo->cbSize);
579 if (len == -1) goto end; buf += len; size -= len;
580 if (pScrollInfo->fMask & SIF_RANGE)
581 len = snprintf(buf, size, "nMin=%d, nMax=%d, ", pScrollInfo->nMin, pScrollInfo->nMax);
583 if (len == -1) goto end; buf += len; size -= len;
584 if (pScrollInfo->fMask & SIF_PAGE)
585 len = snprintf(buf, size, "nPage=%u, ", pScrollInfo->nPage);
587 if (len == -1) goto end; buf += len; size -= len;
588 if (pScrollInfo->fMask & SIF_POS)
589 len = snprintf(buf, size, "nPos=%d, ", pScrollInfo->nPos);
591 if (len == -1) goto end; buf += len; size -= len;
592 if (pScrollInfo->fMask & SIF_TRACKPOS)
593 len = snprintf(buf, size, "nTrackPos=%d, ", pScrollInfo->nTrackPos);
595 if (len == -1) goto end; buf += len; size -= len;
598 buf = text + strlen(text);
600 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
604 static const char* debugnmlistview(const NMLISTVIEW *plvnm)
606 if (!plvnm) return "(null)";
607 return wine_dbg_sprintf("iItem=%d, iSubItem=%d, uNewState=0x%x,"
608 " uOldState=0x%x, uChanged=0x%x, ptAction=%s, lParam=%ld\n",
609 plvnm->iItem, plvnm->iSubItem, plvnm->uNewState, plvnm->uOldState,
610 plvnm->uChanged, wine_dbgstr_point(&plvnm->ptAction), plvnm->lParam);
613 static const char* debuglvitem_t(const LVITEMW *lpLVItem, BOOL isW)
615 char* buf = debug_getbuf(), *text = buf;
616 int len, size = DEBUG_BUFFER_SIZE;
618 if (lpLVItem == NULL) return "(null)";
619 len = snprintf(buf, size, "{iItem=%d, iSubItem=%d, ", lpLVItem->iItem, lpLVItem->iSubItem);
620 if (len == -1) goto end; buf += len; size -= len;
621 if (lpLVItem->mask & LVIF_STATE)
622 len = snprintf(buf, size, "state=%x, stateMask=%x, ", lpLVItem->state, lpLVItem->stateMask);
624 if (len == -1) goto end; buf += len; size -= len;
625 if (lpLVItem->mask & LVIF_TEXT)
626 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpLVItem->pszText, isW, 80), lpLVItem->cchTextMax);
628 if (len == -1) goto end; buf += len; size -= len;
629 if (lpLVItem->mask & LVIF_IMAGE)
630 len = snprintf(buf, size, "iImage=%d, ", lpLVItem->iImage);
632 if (len == -1) goto end; buf += len; size -= len;
633 if (lpLVItem->mask & LVIF_PARAM)
634 len = snprintf(buf, size, "lParam=%lx, ", lpLVItem->lParam);
636 if (len == -1) goto end; buf += len; size -= len;
637 if (lpLVItem->mask & LVIF_INDENT)
638 len = snprintf(buf, size, "iIndent=%d, ", lpLVItem->iIndent);
640 if (len == -1) goto end; buf += len; size -= len;
643 buf = text + strlen(text);
645 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
649 static const char* debuglvcolumn_t(const LVCOLUMNW *lpColumn, BOOL isW)
651 char* buf = debug_getbuf(), *text = buf;
652 int len, size = DEBUG_BUFFER_SIZE;
654 if (lpColumn == NULL) return "(null)";
655 len = snprintf(buf, size, "{");
656 if (len == -1) goto end; buf += len; size -= len;
657 if (lpColumn->mask & LVCF_SUBITEM)
658 len = snprintf(buf, size, "iSubItem=%d, ", lpColumn->iSubItem);
660 if (len == -1) goto end; buf += len; size -= len;
661 if (lpColumn->mask & LVCF_FMT)
662 len = snprintf(buf, size, "fmt=%x, ", lpColumn->fmt);
664 if (len == -1) goto end; buf += len; size -= len;
665 if (lpColumn->mask & LVCF_WIDTH)
666 len = snprintf(buf, size, "cx=%d, ", lpColumn->cx);
668 if (len == -1) goto end; buf += len; size -= len;
669 if (lpColumn->mask & LVCF_TEXT)
670 len = snprintf(buf, size, "pszText=%s, cchTextMax=%d, ", debugtext_tn(lpColumn->pszText, isW, 80), lpColumn->cchTextMax);
672 if (len == -1) goto end; buf += len; size -= len;
673 if (lpColumn->mask & LVCF_IMAGE)
674 len = snprintf(buf, size, "iImage=%d, ", lpColumn->iImage);
676 if (len == -1) goto end; buf += len; size -= len;
677 if (lpColumn->mask & LVCF_ORDER)
678 len = snprintf(buf, size, "iOrder=%d, ", lpColumn->iOrder);
680 if (len == -1) goto end; buf += len; size -= len;
683 buf = text + strlen(text);
685 if (buf - text > 2) { buf[-2] = '}'; buf[-1] = 0; }
689 static const char* debuglvhittestinfo(const LVHITTESTINFO *lpht)
691 if (!lpht) return "(null)";
693 return wine_dbg_sprintf("{pt=%s, flags=0x%x, iItem=%d, iSubItem=%d}",
694 wine_dbgstr_point(&lpht->pt), lpht->flags, lpht->iItem, lpht->iSubItem);
697 /* Return the corresponding text for a given scroll value */
698 static inline LPCSTR debugscrollcode(int nScrollCode)
702 case SB_LINELEFT: return "SB_LINELEFT";
703 case SB_LINERIGHT: return "SB_LINERIGHT";
704 case SB_PAGELEFT: return "SB_PAGELEFT";
705 case SB_PAGERIGHT: return "SB_PAGERIGHT";
706 case SB_THUMBPOSITION: return "SB_THUMBPOSITION";
707 case SB_THUMBTRACK: return "SB_THUMBTRACK";
708 case SB_ENDSCROLL: return "SB_ENDSCROLL";
709 case SB_INTERNAL: return "SB_INTERNAL";
710 default: return "unknown";
715 /******** Notification functions i************************************/
717 static LRESULT notify_forward_header(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
719 return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
720 (WPARAM)lpnmh->hdr.idFrom, (LPARAM)lpnmh);
723 static LRESULT notify_hdr(LISTVIEW_INFO *infoPtr, INT code, LPNMHDR pnmh)
727 TRACE("(code=%d)\n", code);
729 pnmh->hwndFrom = infoPtr->hwndSelf;
730 pnmh->idFrom = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
732 result = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY,
733 (WPARAM)pnmh->idFrom, (LPARAM)pnmh);
735 TRACE(" <= %ld\n", result);
740 static inline BOOL notify(LISTVIEW_INFO *infoPtr, INT code)
743 HWND hwnd = infoPtr->hwndSelf;
744 notify_hdr(infoPtr, code, &nmh);
745 return IsWindow(hwnd);
748 static inline void notify_itemactivate(LISTVIEW_INFO *infoPtr, LVHITTESTINFO *htInfo)
759 item.mask = LVIF_PARAM|LVIF_STATE;
760 item.iItem = htInfo->iItem;
762 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) {
763 nmia.lParam = item.lParam;
764 nmia.uOldState = item.state;
765 nmia.uNewState = item.state | LVIS_ACTIVATING;
766 nmia.uChanged = LVIF_STATE;
769 nmia.iItem = htInfo->iItem;
770 nmia.iSubItem = htInfo->iSubItem;
771 nmia.ptAction = htInfo->pt;
773 if (GetKeyState(VK_SHIFT) & 0x8000) nmia.uKeyFlags |= LVKF_SHIFT;
774 if (GetKeyState(VK_CONTROL) & 0x8000) nmia.uKeyFlags |= LVKF_CONTROL;
775 if (GetKeyState(VK_MENU) & 0x8000) nmia.uKeyFlags |= LVKF_ALT;
777 notify_hdr(infoPtr, LVN_ITEMACTIVATE, (LPNMHDR)&nmia);
780 static inline LRESULT notify_listview(LISTVIEW_INFO *infoPtr, INT code, LPNMLISTVIEW plvnm)
782 TRACE("(code=%d, plvnm=%s)\n", code, debugnmlistview(plvnm));
783 return notify_hdr(infoPtr, code, (LPNMHDR)plvnm);
786 static BOOL notify_click(LISTVIEW_INFO *infoPtr, INT code, LVHITTESTINFO *lvht)
790 HWND hwnd = infoPtr->hwndSelf;
792 TRACE("code=%d, lvht=%s\n", code, debuglvhittestinfo(lvht));
793 ZeroMemory(&nmlv, sizeof(nmlv));
794 nmlv.iItem = lvht->iItem;
795 nmlv.iSubItem = lvht->iSubItem;
796 nmlv.ptAction = lvht->pt;
797 item.mask = LVIF_PARAM;
798 item.iItem = lvht->iItem;
800 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
801 notify_listview(infoPtr, code, &nmlv);
802 return IsWindow(hwnd);
805 static BOOL notify_deleteitem(LISTVIEW_INFO *infoPtr, INT nItem)
809 HWND hwnd = infoPtr->hwndSelf;
811 ZeroMemory(&nmlv, sizeof (NMLISTVIEW));
813 item.mask = LVIF_PARAM;
816 if (LISTVIEW_GetItemT(infoPtr, &item, TRUE)) nmlv.lParam = item.lParam;
817 notify_listview(infoPtr, LVN_DELETEITEM, &nmlv);
818 return IsWindow(hwnd);
821 static int get_ansi_notification(INT unicodeNotificationCode)
823 switch (unicodeNotificationCode)
825 case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
826 case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
827 case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
828 case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
829 case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
830 case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
832 ERR("unknown notification %x\n", unicodeNotificationCode);
838 Send notification. depends on dispinfoW having same
839 structure as dispinfoA.
840 infoPtr : listview struct
841 notificationCode : *Unicode* notification code
842 pdi : dispinfo structure (can be unicode or ansi)
843 isW : TRUE if dispinfo is Unicode
845 static BOOL notify_dispinfoT(LISTVIEW_INFO *infoPtr, INT notificationCode, LPNMLVDISPINFOW pdi, BOOL isW)
847 BOOL bResult = FALSE;
848 BOOL convertToAnsi = FALSE, convertToUnicode = FALSE;
849 INT cchTempBufMax = 0, savCchTextMax = 0, realNotifCode;
850 LPWSTR pszTempBuf = NULL, savPszText = NULL;
852 if ((pdi->item.mask & LVIF_TEXT) && is_textT(pdi->item.pszText, isW))
854 convertToAnsi = (isW && infoPtr->notifyFormat == NFR_ANSI);
855 convertToUnicode = (!isW && infoPtr->notifyFormat == NFR_UNICODE);
858 if (convertToAnsi || convertToUnicode)
860 if (notificationCode != LVN_GETDISPINFOW)
862 cchTempBufMax = convertToUnicode ?
863 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1, NULL, 0):
864 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, NULL, 0, NULL, NULL);
868 cchTempBufMax = pdi->item.cchTextMax;
869 *pdi->item.pszText = 0; /* make sure we don't process garbage */
872 pszTempBuf = Alloc( (convertToUnicode ? sizeof(WCHAR) : sizeof(CHAR)) * cchTempBufMax);
873 if (!pszTempBuf) return FALSE;
875 if (convertToUnicode)
876 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pdi->item.pszText, -1,
877 pszTempBuf, cchTempBufMax);
879 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) pszTempBuf,
880 cchTempBufMax, NULL, NULL);
882 savCchTextMax = pdi->item.cchTextMax;
883 savPszText = pdi->item.pszText;
884 pdi->item.pszText = pszTempBuf;
885 pdi->item.cchTextMax = cchTempBufMax;
888 if (infoPtr->notifyFormat == NFR_ANSI)
889 realNotifCode = get_ansi_notification(notificationCode);
891 realNotifCode = notificationCode;
892 TRACE(" pdi->item=%s\n", debuglvitem_t(&pdi->item, infoPtr->notifyFormat != NFR_ANSI));
893 bResult = notify_hdr(infoPtr, realNotifCode, &pdi->hdr);
895 if (convertToUnicode || convertToAnsi)
897 if (convertToUnicode) /* note : pointer can be changed by app ! */
898 WideCharToMultiByte(CP_ACP, 0, pdi->item.pszText, -1, (LPSTR) savPszText,
899 savCchTextMax, NULL, NULL);
901 MultiByteToWideChar(CP_ACP, 0, (LPSTR) pdi->item.pszText, -1,
902 savPszText, savCchTextMax);
903 pdi->item.pszText = savPszText; /* restores our buffer */
904 pdi->item.cchTextMax = savCchTextMax;
910 static void customdraw_fill(NMLVCUSTOMDRAW *lpnmlvcd, LISTVIEW_INFO *infoPtr, HDC hdc,
911 const RECT *rcBounds, const LVITEMW *lplvItem)
913 ZeroMemory(lpnmlvcd, sizeof(NMLVCUSTOMDRAW));
914 lpnmlvcd->nmcd.hdc = hdc;
915 lpnmlvcd->nmcd.rc = *rcBounds;
916 lpnmlvcd->clrTextBk = infoPtr->clrTextBk;
917 lpnmlvcd->clrText = infoPtr->clrText;
918 if (!lplvItem) return;
919 lpnmlvcd->nmcd.dwItemSpec = lplvItem->iItem + 1;
920 lpnmlvcd->iSubItem = lplvItem->iSubItem;
921 if (lplvItem->state & LVIS_SELECTED) lpnmlvcd->nmcd.uItemState |= CDIS_SELECTED;
922 if (lplvItem->state & LVIS_FOCUSED) lpnmlvcd->nmcd.uItemState |= CDIS_FOCUS;
923 if (lplvItem->iItem == infoPtr->nHotItem) lpnmlvcd->nmcd.uItemState |= CDIS_HOT;
924 lpnmlvcd->nmcd.lItemlParam = lplvItem->lParam;
927 static inline DWORD notify_customdraw (LISTVIEW_INFO *infoPtr, DWORD dwDrawStage, NMLVCUSTOMDRAW *lpnmlvcd)
929 BOOL isForItem = (lpnmlvcd->nmcd.dwItemSpec != 0);
932 lpnmlvcd->nmcd.dwDrawStage = dwDrawStage;
933 if (isForItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_ITEM;
934 if (lpnmlvcd->iSubItem) lpnmlvcd->nmcd.dwDrawStage |= CDDS_SUBITEM;
935 if (isForItem) lpnmlvcd->nmcd.dwItemSpec--;
936 result = notify_hdr(infoPtr, NM_CUSTOMDRAW, &lpnmlvcd->nmcd.hdr);
937 if (isForItem) lpnmlvcd->nmcd.dwItemSpec++;
941 static void prepaint_setup (LISTVIEW_INFO *infoPtr, HDC hdc, NMLVCUSTOMDRAW *lpnmlvcd)
943 /* apprently, for selected items, we have to override the returned values */
944 if (lpnmlvcd->nmcd.uItemState & CDIS_SELECTED)
948 lpnmlvcd->clrTextBk = comctl32_color.clrHighlight;
949 lpnmlvcd->clrText = comctl32_color.clrHighlightText;
951 else if (infoPtr->dwStyle & LVS_SHOWSELALWAYS)
953 lpnmlvcd->clrTextBk = comctl32_color.clr3dFace;
954 lpnmlvcd->clrText = comctl32_color.clrBtnText;
958 /* Set the text attributes */
959 if (lpnmlvcd->clrTextBk != CLR_NONE)
961 SetBkMode(hdc, OPAQUE);
962 if (lpnmlvcd->clrTextBk == CLR_DEFAULT)
963 SetBkColor(hdc, infoPtr->clrTextBkDefault);
965 SetBkColor(hdc,lpnmlvcd->clrTextBk);
968 SetBkMode(hdc, TRANSPARENT);
969 SetTextColor(hdc, lpnmlvcd->clrText);
972 static inline DWORD notify_postpaint (LISTVIEW_INFO *infoPtr, NMLVCUSTOMDRAW *lpnmlvcd)
974 return notify_customdraw(infoPtr, CDDS_POSTPAINT, lpnmlvcd);
977 /******** Item iterator functions **********************************/
979 static RANGES ranges_create(int count);
980 static void ranges_destroy(RANGES ranges);
981 static BOOL ranges_add(RANGES ranges, RANGE range);
982 static BOOL ranges_del(RANGES ranges, RANGE range);
983 static void ranges_dump(RANGES ranges);
985 static inline BOOL ranges_additem(RANGES ranges, INT nItem)
987 RANGE range = { nItem, nItem + 1 };
989 return ranges_add(ranges, range);
992 static inline BOOL ranges_delitem(RANGES ranges, INT nItem)
994 RANGE range = { nItem, nItem + 1 };
996 return ranges_del(ranges, range);
1000 * ITERATOR DOCUMENTATION
1002 * The iterator functions allow for easy, and convenient iteration
1003 * over items of iterest in the list. Typically, you create a
1004 * iterator, use it, and destroy it, as such:
1007 * iterator_xxxitems(&i, ...);
1008 * while (iterator_{prev,next}(&i)
1010 * //code which uses i.nItem
1012 * iterator_destroy(&i);
1014 * where xxx is either: framed, or visible.
1015 * Note that it is important that the code destroys the iterator
1016 * after it's done with it, as the creation of the iterator may
1017 * allocate memory, which thus needs to be freed.
1019 * You can iterate both forwards, and backwards through the list,
1020 * by using iterator_next or iterator_prev respectively.
1022 * Lower numbered items are draw on top of higher number items in
1023 * LVS_ICON, and LVS_SMALLICON (which are the only modes where
1024 * items may overlap). So, to test items, you should use
1026 * which lists the items top to bottom (in Z-order).
1027 * For drawing items, you should use
1029 * which lists the items bottom to top (in Z-order).
1030 * If you keep iterating over the items after the end-of-items
1031 * marker (-1) is returned, the iterator will start from the
1032 * beginning. Typically, you don't need to test for -1,
1033 * because iterator_{next,prev} will return TRUE if more items
1034 * are to be iterated over, or FALSE otherwise.
1036 * Note: the iterator is defined to be bidirectional. That is,
1037 * any number of prev followed by any number of next, or
1038 * five versa, should leave the iterator at the same item:
1039 * prev * n, next * n = next * n, prev * n
1041 * The iterator has a notion of an out-of-order, special item,
1042 * which sits at the start of the list. This is used in
1043 * LVS_ICON, and LVS_SMALLICON mode to handle the focused item,
1044 * which needs to be first, as it may overlap other items.
1046 * The code is a bit messy because we have:
1047 * - a special item to deal with
1048 * - simple range, or composite range
1050 * If you find bugs, or want to add features, please make sure you
1051 * always check/modify *both* iterator_prev, and iterator_next.
1055 * This function iterates through the items in increasing order,
1056 * but prefixed by the special item, then -1. That is:
1057 * special, 1, 2, 3, ..., n, -1.
1058 * Each item is listed only once.
1060 static inline BOOL iterator_next(ITERATOR* i)
1064 i->nItem = i->nSpecial;
1065 if (i->nItem != -1) return TRUE;
1067 if (i->nItem == i->nSpecial)
1069 if (i->ranges) i->index = 0;
1075 if (i->nItem == i->nSpecial) i->nItem++;
1076 if (i->nItem < i->range.upper) return TRUE;
1081 if (i->index < DPA_GetPtrCount(i->ranges->hdpa))
1082 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, i->index++);
1085 else if (i->nItem >= i->range.upper) goto end;
1087 i->nItem = i->range.lower;
1088 if (i->nItem >= 0) goto testitem;
1095 * This function iterates through the items in decreasing order,
1096 * followed by the special item, then -1. That is:
1097 * n, n-1, ..., 3, 2, 1, special, -1.
1098 * Each item is listed only once.
1100 static inline BOOL iterator_prev(ITERATOR* i)
1107 if (i->ranges) i->index = DPA_GetPtrCount(i->ranges->hdpa);
1110 if (i->nItem == i->nSpecial)
1118 if (i->nItem == i->nSpecial) i->nItem--;
1119 if (i->nItem >= i->range.lower) return TRUE;
1125 i->range = *(RANGE*)DPA_GetPtr(i->ranges->hdpa, --i->index);
1128 else if (!start && i->nItem < i->range.lower) goto end;
1130 i->nItem = i->range.upper;
1131 if (i->nItem > 0) goto testitem;
1133 return (i->nItem = i->nSpecial) != -1;
1136 static RANGE iterator_range(ITERATOR* i)
1140 if (!i->ranges) return i->range;
1142 if (DPA_GetPtrCount(i->ranges->hdpa) > 0)
1144 range.lower = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, 0)).lower;
1145 range.upper = (*(RANGE*)DPA_GetPtr(i->ranges->hdpa, DPA_GetPtrCount(i->ranges->hdpa) - 1)).upper;
1147 else range.lower = range.upper = 0;
1153 * Releases resources associated with this ierator.
1155 static inline void iterator_destroy(ITERATOR* i)
1157 ranges_destroy(i->ranges);
1161 * Create an empty iterator.
1163 static inline BOOL iterator_empty(ITERATOR* i)
1165 ZeroMemory(i, sizeof(*i));
1166 i->nItem = i->nSpecial = i->range.lower = i->range.upper = -1;
1171 * Create an iterator over a range.
1173 static inline BOOL iterator_rangeitems(ITERATOR* i, RANGE range)
1181 * Create an iterator over a bunch of ranges.
1182 * Please note that the iterator will take ownership of the ranges,
1183 * and will free them upon destruction.
1185 static inline BOOL iterator_rangesitems(ITERATOR* i, RANGES ranges)
1193 * Creates an iterator over the items which intersect lprc.
1195 static BOOL iterator_frameditems(ITERATOR* i, LISTVIEW_INFO* infoPtr, const RECT *lprc)
1197 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1198 RECT frame = *lprc, rcItem, rcTemp;
1201 /* in case we fail, we want to return an empty iterator */
1202 if (!iterator_empty(i)) return FALSE;
1204 LISTVIEW_GetOrigin(infoPtr, &Origin);
1206 TRACE("(lprc=%s)\n", wine_dbgstr_rect(lprc));
1207 OffsetRect(&frame, -Origin.x, -Origin.y);
1209 if (uView == LVS_ICON || uView == LVS_SMALLICON)
1213 if (uView == LVS_ICON && infoPtr->nFocusedItem != -1)
1215 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcItem);
1216 if (IntersectRect(&rcTemp, &rcItem, lprc))
1217 i->nSpecial = infoPtr->nFocusedItem;
1219 if (!(iterator_rangesitems(i, ranges_create(50)))) return FALSE;
1220 /* to do better here, we need to have PosX, and PosY sorted */
1221 TRACE("building icon ranges:\n");
1222 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
1224 rcItem.left = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1225 rcItem.top = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1226 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1227 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1228 if (IntersectRect(&rcTemp, &rcItem, &frame))
1229 ranges_additem(i->ranges, nItem);
1233 else if (uView == LVS_REPORT)
1237 if (frame.left >= infoPtr->nItemWidth) return TRUE;
1238 if (frame.top >= infoPtr->nItemHeight * infoPtr->nItemCount) return TRUE;
1240 range.lower = max(frame.top / infoPtr->nItemHeight, 0);
1241 range.upper = min((frame.bottom - 1) / infoPtr->nItemHeight, infoPtr->nItemCount - 1) + 1;
1242 if (range.upper <= range.lower) return TRUE;
1243 if (!iterator_rangeitems(i, range)) return FALSE;
1244 TRACE(" report=%s\n", debugrange(&i->range));
1248 INT nPerCol = max((infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight, 1);
1249 INT nFirstRow = max(frame.top / infoPtr->nItemHeight, 0);
1250 INT nLastRow = min((frame.bottom - 1) / infoPtr->nItemHeight, nPerCol - 1);
1251 INT nFirstCol = max(frame.left / infoPtr->nItemWidth, 0);
1252 INT nLastCol = min((frame.right - 1) / infoPtr->nItemWidth, (infoPtr->nItemCount + nPerCol - 1) / nPerCol);
1253 INT lower = nFirstCol * nPerCol + nFirstRow;
1257 TRACE("nPerCol=%d, nFirstRow=%d, nLastRow=%d, nFirstCol=%d, nLastCol=%d, lower=%d\n",
1258 nPerCol, nFirstRow, nLastRow, nFirstCol, nLastCol, lower);
1260 if (nLastCol < nFirstCol || nLastRow < nFirstRow) return TRUE;
1262 if (!(iterator_rangesitems(i, ranges_create(nLastCol - nFirstCol + 1)))) return FALSE;
1263 TRACE("building list ranges:\n");
1264 for (nCol = nFirstCol; nCol <= nLastCol; nCol++)
1266 item_range.lower = nCol * nPerCol + nFirstRow;
1267 if(item_range.lower >= infoPtr->nItemCount) break;
1268 item_range.upper = min(nCol * nPerCol + nLastRow + 1, infoPtr->nItemCount);
1269 TRACE(" list=%s\n", debugrange(&item_range));
1270 ranges_add(i->ranges, item_range);
1278 * Creates an iterator over the items which intersect the visible region of hdc.
1280 static BOOL iterator_visibleitems(ITERATOR *i, LISTVIEW_INFO *infoPtr, HDC hdc)
1282 POINT Origin, Position;
1283 RECT rcItem, rcClip;
1286 rgntype = GetClipBox(hdc, &rcClip);
1287 if (rgntype == NULLREGION) return iterator_empty(i);
1288 if (!iterator_frameditems(i, infoPtr, &rcClip)) return FALSE;
1289 if (rgntype == SIMPLEREGION) return TRUE;
1291 /* first deal with the special item */
1292 if (i->nSpecial != -1)
1294 LISTVIEW_GetItemBox(infoPtr, i->nSpecial, &rcItem);
1295 if (!RectVisible(hdc, &rcItem)) i->nSpecial = -1;
1298 /* if we can't deal with the region, we'll just go with the simple range */
1299 LISTVIEW_GetOrigin(infoPtr, &Origin);
1300 TRACE("building visible range:\n");
1301 if (!i->ranges && i->range.lower < i->range.upper)
1303 if (!(i->ranges = ranges_create(50))) return TRUE;
1304 if (!ranges_add(i->ranges, i->range))
1306 ranges_destroy(i->ranges);
1312 /* now delete the invisible items from the list */
1313 while(iterator_next(i))
1315 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
1316 rcItem.left = Position.x + Origin.x;
1317 rcItem.top = Position.y + Origin.y;
1318 rcItem.right = rcItem.left + infoPtr->nItemWidth;
1319 rcItem.bottom = rcItem.top + infoPtr->nItemHeight;
1320 if (!RectVisible(hdc, &rcItem))
1321 ranges_delitem(i->ranges, i->nItem);
1323 /* the iterator should restart on the next iterator_next */
1329 /******** Misc helper functions ************************************/
1331 static inline LRESULT CallWindowProcT(WNDPROC proc, HWND hwnd, UINT uMsg,
1332 WPARAM wParam, LPARAM lParam, BOOL isW)
1334 if (isW) return CallWindowProcW(proc, hwnd, uMsg, wParam, lParam);
1335 else return CallWindowProcA(proc, hwnd, uMsg, wParam, lParam);
1338 static inline BOOL is_autoarrange(LISTVIEW_INFO *infoPtr)
1340 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1342 return ((infoPtr->dwStyle & LVS_AUTOARRANGE) || infoPtr->bAutoarrange) &&
1343 (uView == LVS_ICON || uView == LVS_SMALLICON);
1346 /******** Internal API functions ************************************/
1348 static inline COLUMN_INFO * LISTVIEW_GetColumnInfo(LISTVIEW_INFO *infoPtr, INT nSubItem)
1350 static COLUMN_INFO mainItem;
1352 if (nSubItem == 0 && DPA_GetPtrCount(infoPtr->hdpaColumns) == 0) return &mainItem;
1353 assert (nSubItem >= 0 && nSubItem < DPA_GetPtrCount(infoPtr->hdpaColumns));
1354 return (COLUMN_INFO *)DPA_GetPtr(infoPtr->hdpaColumns, nSubItem);
1357 static inline void LISTVIEW_GetHeaderRect(LISTVIEW_INFO *infoPtr, INT nSubItem, RECT *lprc)
1359 *lprc = LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->rcHeader;
1362 static inline BOOL LISTVIEW_GetItemW(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem)
1364 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
1367 /* Listview invalidation functions: use _only_ these functions to invalidate */
1369 static inline BOOL is_redrawing(LISTVIEW_INFO *infoPtr)
1371 return infoPtr->bRedraw;
1374 static inline void LISTVIEW_InvalidateRect(LISTVIEW_INFO *infoPtr, const RECT* rect)
1376 if(!is_redrawing(infoPtr)) return;
1377 TRACE(" invalidating rect=%s\n", wine_dbgstr_rect(rect));
1378 InvalidateRect(infoPtr->hwndSelf, rect, TRUE);
1381 static inline void LISTVIEW_InvalidateItem(LISTVIEW_INFO *infoPtr, INT nItem)
1385 if(!is_redrawing(infoPtr)) return;
1386 LISTVIEW_GetItemBox(infoPtr, nItem, &rcBox);
1387 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1390 static inline void LISTVIEW_InvalidateSubItem(LISTVIEW_INFO *infoPtr, INT nItem, INT nSubItem)
1392 POINT Origin, Position;
1395 if(!is_redrawing(infoPtr)) return;
1396 assert ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT);
1397 LISTVIEW_GetOrigin(infoPtr, &Origin);
1398 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
1399 LISTVIEW_GetHeaderRect(infoPtr, nSubItem, &rcBox);
1401 rcBox.bottom = infoPtr->nItemHeight;
1402 OffsetRect(&rcBox, Origin.x + Position.x, Origin.y + Position.y);
1403 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1406 static inline void LISTVIEW_InvalidateList(LISTVIEW_INFO *infoPtr)
1408 LISTVIEW_InvalidateRect(infoPtr, NULL);
1411 static inline void LISTVIEW_InvalidateColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
1415 if(!is_redrawing(infoPtr)) return;
1416 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
1417 rcCol.top = infoPtr->rcList.top;
1418 rcCol.bottom = infoPtr->rcList.bottom;
1419 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
1424 * Retrieves the number of items that can fit vertically in the client area.
1427 * [I] infoPtr : valid pointer to the listview structure
1430 * Number of items per row.
1432 static inline INT LISTVIEW_GetCountPerRow(LISTVIEW_INFO *infoPtr)
1434 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
1436 return max(nListWidth/infoPtr->nItemWidth, 1);
1441 * Retrieves the number of items that can fit horizontally in the client
1445 * [I] infoPtr : valid pointer to the listview structure
1448 * Number of items per column.
1450 static inline INT LISTVIEW_GetCountPerColumn(LISTVIEW_INFO *infoPtr)
1452 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
1454 return max(nListHeight / infoPtr->nItemHeight, 1);
1458 /*************************************************************************
1459 * LISTVIEW_ProcessLetterKeys
1461 * Processes keyboard messages generated by pressing the letter keys
1463 * What this does is perform a case insensitive search from the
1464 * current position with the following quirks:
1465 * - If two chars or more are pressed in quick succession we search
1466 * for the corresponding string (e.g. 'abc').
1467 * - If there is a delay we wipe away the current search string and
1468 * restart with just that char.
1469 * - If the user keeps pressing the same character, whether slowly or
1470 * fast, so that the search string is entirely composed of this
1471 * character ('aaaaa' for instance), then we search for first item
1472 * that starting with that character.
1473 * - If the user types the above character in quick succession, then
1474 * we must also search for the corresponding string ('aaaaa'), and
1475 * go to that string if there is a match.
1478 * [I] hwnd : handle to the window
1479 * [I] charCode : the character code, the actual character
1480 * [I] keyData : key data
1488 * - The current implementation has a list of characters it will
1489 * accept and it ignores averything else. In particular it will
1490 * ignore accentuated characters which seems to match what
1491 * Windows does. But I'm not sure it makes sense to follow
1493 * - We don't sound a beep when the search fails.
1497 * TREEVIEW_ProcessLetterKeys
1499 static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, LPARAM keyData)
1504 WCHAR buffer[MAX_PATH];
1505 DWORD lastKeyPressTimestamp = infoPtr->lastKeyPressTimestamp;
1507 /* simple parameter checking */
1508 if (!charCode || !keyData) return 0;
1510 /* only allow the valid WM_CHARs through */
1511 if (!isalnum(charCode) &&
1512 charCode != '.' && charCode != '`' && charCode != '!' &&
1513 charCode != '@' && charCode != '#' && charCode != '$' &&
1514 charCode != '%' && charCode != '^' && charCode != '&' &&
1515 charCode != '*' && charCode != '(' && charCode != ')' &&
1516 charCode != '-' && charCode != '_' && charCode != '+' &&
1517 charCode != '=' && charCode != '\\'&& charCode != ']' &&
1518 charCode != '}' && charCode != '[' && charCode != '{' &&
1519 charCode != '/' && charCode != '?' && charCode != '>' &&
1520 charCode != '<' && charCode != ',' && charCode != '~')
1523 /* if there's one item or less, there is no where to go */
1524 if (infoPtr->nItemCount <= 1) return 0;
1526 /* update the search parameters */
1527 infoPtr->lastKeyPressTimestamp = GetTickCount();
1528 if (infoPtr->lastKeyPressTimestamp - lastKeyPressTimestamp < KEY_DELAY) {
1529 if (infoPtr->nSearchParamLength < MAX_PATH)
1530 infoPtr->szSearchParam[infoPtr->nSearchParamLength++]=charCode;
1531 if (infoPtr->charCode != charCode)
1532 infoPtr->charCode = charCode = 0;
1534 infoPtr->charCode=charCode;
1535 infoPtr->szSearchParam[0]=charCode;
1536 infoPtr->nSearchParamLength=1;
1537 /* Redundant with the 1 char string */
1541 /* and search from the current position */
1543 if (infoPtr->nFocusedItem >= 0) {
1544 endidx=infoPtr->nFocusedItem;
1546 /* if looking for single character match,
1547 * then we must always move forward
1549 if (infoPtr->nSearchParamLength == 1)
1552 endidx=infoPtr->nItemCount;
1556 if (idx == infoPtr->nItemCount) {
1557 if (endidx == infoPtr->nItemCount || endidx == 0)
1563 item.mask = LVIF_TEXT;
1566 item.pszText = buffer;
1567 item.cchTextMax = MAX_PATH;
1568 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
1570 /* check for a match */
1571 if (lstrncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
1574 } else if ( (charCode != 0) && (nItem == -1) && (nItem != infoPtr->nFocusedItem) &&
1575 (lstrncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
1576 /* This would work but we must keep looking for a longer match */
1580 } while (idx != endidx);
1583 LISTVIEW_KeySelection(infoPtr, nItem);
1588 /*************************************************************************
1589 * LISTVIEW_UpdateHeaderSize [Internal]
1591 * Function to resize the header control
1594 * [I] hwnd : handle to a window
1595 * [I] nNewScrollPos : scroll pos to set
1600 static void LISTVIEW_UpdateHeaderSize(LISTVIEW_INFO *infoPtr, INT nNewScrollPos)
1605 TRACE("nNewScrollPos=%d\n", nNewScrollPos);
1607 GetWindowRect(infoPtr->hwndHeader, &winRect);
1608 point[0].x = winRect.left;
1609 point[0].y = winRect.top;
1610 point[1].x = winRect.right;
1611 point[1].y = winRect.bottom;
1613 MapWindowPoints(HWND_DESKTOP, infoPtr->hwndSelf, point, 2);
1614 point[0].x = -nNewScrollPos;
1615 point[1].x += nNewScrollPos;
1617 SetWindowPos(infoPtr->hwndHeader,0,
1618 point[0].x,point[0].y,point[1].x,point[1].y,
1619 SWP_NOZORDER | SWP_NOACTIVATE);
1624 * Update the scrollbars. This functions should be called whenever
1625 * the content, size or view changes.
1628 * [I] infoPtr : valid pointer to the listview structure
1633 static void LISTVIEW_UpdateScroll(LISTVIEW_INFO *infoPtr)
1635 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1636 SCROLLINFO horzInfo, vertInfo;
1638 if ((infoPtr->dwStyle & LVS_NOSCROLL) || !is_redrawing(infoPtr)) return;
1640 ZeroMemory(&horzInfo, sizeof(SCROLLINFO));
1641 horzInfo.cbSize = sizeof(SCROLLINFO);
1642 horzInfo.nPage = infoPtr->rcList.right - infoPtr->rcList.left;
1644 /* for now, we'll set info.nMax to the _count_, and adjust it later */
1645 if (uView == LVS_LIST)
1647 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
1648 horzInfo.nMax = (infoPtr->nItemCount + nPerCol - 1) / nPerCol;
1650 /* scroll by at least one column per page */
1651 if(horzInfo.nPage < infoPtr->nItemWidth)
1652 horzInfo.nPage = infoPtr->nItemWidth;
1654 horzInfo.nPage /= infoPtr->nItemWidth;
1656 else if (uView == LVS_REPORT)
1658 horzInfo.nMax = infoPtr->nItemWidth;
1660 else /* LVS_ICON, or LVS_SMALLICON */
1664 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) horzInfo.nMax = rcView.right - rcView.left;
1667 horzInfo.fMask = SIF_RANGE | SIF_PAGE;
1668 horzInfo.nMax = max(horzInfo.nMax - 1, 0);
1669 SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo, TRUE);
1670 TRACE("horzInfo=%s\n", debugscrollinfo(&horzInfo));
1672 /* Setting the horizontal scroll can change the listview size
1673 * (and potentially everything else) so we need to recompute
1674 * everything again for the vertical scroll
1677 ZeroMemory(&vertInfo, sizeof(SCROLLINFO));
1678 vertInfo.cbSize = sizeof(SCROLLINFO);
1679 vertInfo.nPage = infoPtr->rcList.bottom - infoPtr->rcList.top;
1681 if (uView == LVS_REPORT)
1683 vertInfo.nMax = infoPtr->nItemCount;
1685 /* scroll by at least one page */
1686 if(vertInfo.nPage < infoPtr->nItemHeight)
1687 vertInfo.nPage = infoPtr->nItemHeight;
1689 vertInfo.nPage /= infoPtr->nItemHeight;
1691 else if (uView != LVS_LIST) /* LVS_ICON, or LVS_SMALLICON */
1695 if (LISTVIEW_GetViewRect(infoPtr, &rcView)) vertInfo.nMax = rcView.bottom - rcView.top;
1698 vertInfo.fMask = SIF_RANGE | SIF_PAGE;
1699 vertInfo.nMax = max(vertInfo.nMax - 1, 0);
1700 SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &vertInfo, TRUE);
1701 TRACE("vertInfo=%s\n", debugscrollinfo(&vertInfo));
1703 /* Update the Header Control */
1704 if (uView == LVS_REPORT)
1706 horzInfo.fMask = SIF_POS;
1707 GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &horzInfo);
1708 LISTVIEW_UpdateHeaderSize(infoPtr, horzInfo.nPos);
1715 * Shows/hides the focus rectangle.
1718 * [I] infoPtr : valid pointer to the listview structure
1719 * [I] fShow : TRUE to show the focus, FALSE to hide it.
1724 static void LISTVIEW_ShowFocusRect(LISTVIEW_INFO *infoPtr, BOOL fShow)
1726 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1729 TRACE("fShow=%d, nItem=%d\n", fShow, infoPtr->nFocusedItem);
1731 if (infoPtr->nFocusedItem < 0) return;
1733 /* we need some gymnastics in ICON mode to handle large items */
1734 if ( (infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON )
1738 LISTVIEW_GetItemBox(infoPtr, infoPtr->nFocusedItem, &rcBox);
1739 if ((rcBox.bottom - rcBox.top) > infoPtr->nItemHeight)
1741 LISTVIEW_InvalidateRect(infoPtr, &rcBox);
1746 if (!(hdc = GetDC(infoPtr->hwndSelf))) return;
1748 /* for some reason, owner draw should work only in report mode */
1749 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
1754 item.iItem = infoPtr->nFocusedItem;
1756 item.mask = LVIF_PARAM;
1757 if (!LISTVIEW_GetItemW(infoPtr, &item)) goto done;
1759 ZeroMemory(&dis, sizeof(dis));
1760 dis.CtlType = ODT_LISTVIEW;
1761 dis.CtlID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
1762 dis.itemID = item.iItem;
1763 dis.itemAction = ODA_FOCUS;
1764 if (fShow) dis.itemState |= ODS_FOCUS;
1765 dis.hwndItem = infoPtr->hwndSelf;
1767 LISTVIEW_GetItemBox(infoPtr, dis.itemID, &dis.rcItem);
1768 dis.itemData = item.lParam;
1770 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
1774 DrawFocusRect(hdc, &infoPtr->rcFocus);
1777 ReleaseDC(infoPtr->hwndSelf, hdc);
1781 * Invalidates all visible selected items.
1783 static void LISTVIEW_InvalidateSelectedItems(LISTVIEW_INFO *infoPtr)
1787 iterator_frameditems(&i, infoPtr, &infoPtr->rcList);
1788 while(iterator_next(&i))
1790 if (LISTVIEW_GetItemState(infoPtr, i.nItem, LVIS_SELECTED))
1791 LISTVIEW_InvalidateItem(infoPtr, i.nItem);
1793 iterator_destroy(&i);
1798 * DESCRIPTION: [INTERNAL]
1799 * Computes an item's (left,top) corner, relative to rcView.
1800 * That is, the position has NOT been made relative to the Origin.
1801 * This is deliberate, to avoid computing the Origin over, and
1802 * over again, when this function is call in a loop. Instead,
1803 * one ca factor the computation of the Origin before the loop,
1804 * and offset the value retured by this function, on every iteration.
1807 * [I] infoPtr : valid pointer to the listview structure
1808 * [I] nItem : item number
1809 * [O] lpptOrig : item top, left corner
1814 static void LISTVIEW_GetItemOrigin(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
1816 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1818 assert(nItem >= 0 && nItem < infoPtr->nItemCount);
1820 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
1822 lpptPosition->x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
1823 lpptPosition->y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
1825 else if (uView == LVS_LIST)
1827 INT nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
1828 lpptPosition->x = nItem / nCountPerColumn * infoPtr->nItemWidth;
1829 lpptPosition->y = nItem % nCountPerColumn * infoPtr->nItemHeight;
1831 else /* LVS_REPORT */
1833 lpptPosition->x = 0;
1834 lpptPosition->y = nItem * infoPtr->nItemHeight;
1839 * DESCRIPTION: [INTERNAL]
1840 * Compute the rectangles of an item. This is to localize all
1841 * the computations in one place. If you are not interested in some
1842 * of these values, simply pass in a NULL -- the fucntion is smart
1843 * enough to compute only what's necessary. The function computes
1844 * the standard rectangles (BOUNDS, ICON, LABEL) plus a non-standard
1845 * one, the BOX rectangle. This rectangle is very cheap to compute,
1846 * and is guaranteed to contain all the other rectangles. Computing
1847 * the ICON rect is also cheap, but all the others are potentaily
1848 * expensive. This gives an easy and effective optimization when
1849 * searching (like point inclusion, or rectangle intersection):
1850 * first test against the BOX, and if TRUE, test agains the desired
1852 * If the function does not have all the necessary information
1853 * to computed the requested rectangles, will crash with a
1854 * failed assertion. This is done so we catch all programming
1855 * errors, given that the function is called only from our code.
1857 * We have the following 'special' meanings for a few fields:
1858 * * If LVIS_FOCUSED is set, we assume the item has the focus
1859 * This is important in ICON mode, where it might get a larger
1860 * then usual rectange
1862 * Please note that subitem support works only in REPORT mode.
1865 * [I] infoPtr : valid pointer to the listview structure
1866 * [I] lpLVItem : item to compute the measures for
1867 * [O] lprcBox : ptr to Box rectangle
1868 * The internal LVIR_BOX rectangle
1869 * [0] lprcState : ptr to State icon rectangle
1870 * The internal LVIR_STATE rectangle
1871 * [O] lprcIcon : ptr to Icon rectangle
1872 * Same as LVM_GETITEMRECT with LVIR_ICON
1873 * [O] lprcLabel : ptr to Label rectangle
1874 * Same as LVM_GETITEMRECT with LVIR_LABEL
1879 static void LISTVIEW_GetItemMetrics(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem,
1880 LPRECT lprcBox, LPRECT lprcState,
1881 LPRECT lprcIcon, LPRECT lprcLabel)
1883 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
1884 BOOL doState = FALSE, doIcon = FALSE, doLabel = FALSE, oversizedBox = FALSE;
1885 RECT Box, State, Icon, Label;
1886 COLUMN_INFO *lpColumnInfo = NULL;
1888 TRACE("(lpLVItem=%s)\n", debuglvitem_t(lpLVItem, TRUE));
1890 /* Be smart and try to figure out the minimum we have to do */
1891 if (lpLVItem->iSubItem) assert(uView == LVS_REPORT);
1892 if (uView == LVS_ICON && (lprcBox || lprcLabel))
1894 assert((lpLVItem->mask & LVIF_STATE) && (lpLVItem->stateMask & LVIS_FOCUSED));
1895 if (lpLVItem->state & LVIS_FOCUSED) oversizedBox = doLabel = TRUE;
1897 if (lprcLabel) doLabel = TRUE;
1898 if (doLabel || lprcIcon) doIcon = TRUE;
1899 if (doIcon || lprcState) doState = TRUE;
1901 /************************************************************/
1902 /* compute the box rectangle (it should be cheap to do) */
1903 /************************************************************/
1904 if (lpLVItem->iSubItem || uView == LVS_REPORT)
1905 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpLVItem->iSubItem);
1907 if (lpLVItem->iSubItem)
1909 Box = lpColumnInfo->rcHeader;
1914 Box.right = infoPtr->nItemWidth;
1917 Box.bottom = infoPtr->nItemHeight;
1919 /************************************************************/
1920 /* compute STATEICON bounding box */
1921 /************************************************************/
1924 if (uView == LVS_ICON)
1926 State.left = Box.left - infoPtr->iconStateSize.cx - 2;
1927 if (infoPtr->himlNormal)
1928 State.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1929 State.top = Box.top + infoPtr->iconSize.cy - infoPtr->iconStateSize.cy + 4;
1933 /* we need the ident in report mode, if we don't have it, we fail */
1934 State.left = Box.left;
1935 if (uView == LVS_REPORT)
1937 if (lpLVItem->iSubItem == 0)
1939 State.left += REPORT_MARGINX;
1940 assert(lpLVItem->mask & LVIF_INDENT);
1941 State.left += infoPtr->iconSize.cx * lpLVItem->iIndent;
1944 State.top = Box.top;
1946 State.right = State.left;
1947 State.bottom = State.top;
1948 if (infoPtr->himlState && lpLVItem->iSubItem == 0)
1950 State.right += infoPtr->iconStateSize.cx;
1951 State.bottom += infoPtr->iconStateSize.cy;
1953 if (lprcState) *lprcState = State;
1954 TRACE(" - state=%s\n", wine_dbgstr_rect(&State));
1956 else State.right = 0;
1958 /************************************************************/
1959 /* compute ICON bounding box (ala LVM_GETITEMRECT) */
1960 /************************************************************/
1963 if (uView == LVS_ICON)
1965 Icon.left = Box.left;
1966 if (infoPtr->himlNormal)
1967 Icon.left += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
1968 Icon.top = Box.top + ICON_TOP_PADDING;
1969 Icon.right = Icon.left;
1970 Icon.bottom = Icon.top;
1971 if (infoPtr->himlNormal)
1973 Icon.right += infoPtr->iconSize.cx;
1974 Icon.bottom += infoPtr->iconSize.cy;
1977 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
1979 Icon.left = State.right;
1981 Icon.right = Icon.left;
1982 if (infoPtr->himlSmall &&
1983 (!lpColumnInfo || lpLVItem->iSubItem == 0 || (lpColumnInfo->fmt & LVCFMT_IMAGE) ||
1984 ((infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES) && lpLVItem->iImage != I_IMAGECALLBACK)))
1985 Icon.right += infoPtr->iconSize.cx;
1986 Icon.bottom = Icon.top + infoPtr->nItemHeight;
1988 if(lprcIcon) *lprcIcon = Icon;
1989 TRACE(" - icon=%s\n", wine_dbgstr_rect(&Icon));
1991 else Icon.right = 0;
1993 /************************************************************/
1994 /* compute LABEL bounding box (ala LVM_GETITEMRECT) */
1995 /************************************************************/
1998 SIZE labelSize = { 0, 0 };
2000 /* calculate how far to the right can the label strech */
2001 Label.right = Box.right;
2002 if (uView == LVS_REPORT)
2004 if (lpLVItem->iSubItem == 0) Label = lpColumnInfo->rcHeader;
2007 if (lpLVItem->iSubItem || ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && uView == LVS_REPORT))
2009 labelSize.cx = infoPtr->nItemWidth;
2010 labelSize.cy = infoPtr->nItemHeight;
2014 /* we need the text in non owner draw mode */
2015 assert(lpLVItem->mask & LVIF_TEXT);
2016 if (is_textT(lpLVItem->pszText, TRUE))
2018 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2019 HDC hdc = GetDC(infoPtr->hwndSelf);
2020 HFONT hOldFont = SelectObject(hdc, hFont);
2024 /* compute rough rectangle where the label will go */
2025 SetRectEmpty(&rcText);
2026 rcText.right = infoPtr->nItemWidth - TRAILING_LABEL_PADDING;
2027 rcText.bottom = infoPtr->nItemHeight;
2028 if (uView == LVS_ICON)
2029 rcText.bottom -= ICON_TOP_PADDING + infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2031 /* now figure out the flags */
2032 if (uView == LVS_ICON)
2033 uFormat = oversizedBox ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS;
2035 uFormat = LV_SL_DT_FLAGS;
2037 DrawTextW (hdc, lpLVItem->pszText, -1, &rcText, uFormat | DT_CALCRECT);
2039 labelSize.cx = min(rcText.right - rcText.left + TRAILING_LABEL_PADDING, infoPtr->nItemWidth);
2040 labelSize.cy = rcText.bottom - rcText.top;
2042 SelectObject(hdc, hOldFont);
2043 ReleaseDC(infoPtr->hwndSelf, hdc);
2047 if (uView == LVS_ICON)
2049 Label.left = Box.left + (infoPtr->nItemWidth - labelSize.cx) / 2;
2050 Label.top = Box.top + ICON_TOP_PADDING_HITABLE +
2051 infoPtr->iconSize.cy + ICON_BOTTOM_PADDING;
2052 Label.right = Label.left + labelSize.cx;
2053 Label.bottom = Label.top + infoPtr->nItemHeight;
2054 if (!oversizedBox && labelSize.cy > infoPtr->ntmHeight)
2056 labelSize.cy = min(Box.bottom - Label.top, labelSize.cy);
2057 labelSize.cy /= infoPtr->ntmHeight;
2058 labelSize.cy = max(labelSize.cy, 1);
2059 labelSize.cy *= infoPtr->ntmHeight;
2061 Label.bottom = Label.top + labelSize.cy + HEIGHT_PADDING;
2063 else /* LVS_SMALLICON, LVS_LIST or LVS_REPORT */
2065 Label.left = Icon.right;
2066 Label.top = Box.top;
2067 Label.right = min(Label.left + labelSize.cx, Label.right);
2068 Label.bottom = Label.top + infoPtr->nItemHeight;
2071 if (lprcLabel) *lprcLabel = Label;
2072 TRACE(" - label=%s\n", wine_dbgstr_rect(&Label));
2075 /* Fix the Box if necessary */
2078 if (oversizedBox) UnionRect(lprcBox, &Box, &Label);
2079 else *lprcBox = Box;
2081 TRACE(" - box=%s\n", wine_dbgstr_rect(&Box));
2085 * DESCRIPTION: [INTERNAL]
2088 * [I] infoPtr : valid pointer to the listview structure
2089 * [I] nItem : item number
2090 * [O] lprcBox : ptr to Box rectangle
2095 static void LISTVIEW_GetItemBox(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprcBox)
2097 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2098 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
2099 POINT Position, Origin;
2102 LISTVIEW_GetOrigin(infoPtr, &Origin);
2103 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
2105 /* Be smart and try to figure out the minimum we have to do */
2107 if (uView == LVS_ICON && infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
2108 lvItem.mask |= LVIF_TEXT;
2109 lvItem.iItem = nItem;
2110 lvItem.iSubItem = 0;
2111 lvItem.pszText = szDispText;
2112 lvItem.cchTextMax = DISP_TEXT_SIZE;
2113 if (lvItem.mask) LISTVIEW_GetItemW(infoPtr, &lvItem);
2114 if (uView == LVS_ICON)
2116 lvItem.mask |= LVIF_STATE;
2117 lvItem.stateMask = LVIS_FOCUSED;
2118 lvItem.state = (lvItem.mask & LVIF_TEXT ? LVIS_FOCUSED : 0);
2120 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprcBox, 0, 0, 0);
2122 OffsetRect(lprcBox, Position.x + Origin.x, Position.y + Origin.y);
2128 * Returns the current icon position, and advances it along the top.
2129 * The returned position is not offset by Origin.
2132 * [I] infoPtr : valid pointer to the listview structure
2133 * [O] lpPos : will get the current icon position
2138 static void LISTVIEW_NextIconPosTop(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2140 INT nListWidth = infoPtr->rcList.right - infoPtr->rcList.left;
2142 *lpPos = infoPtr->currIconPos;
2144 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2145 if (infoPtr->currIconPos.x + infoPtr->nItemWidth <= nListWidth) return;
2147 infoPtr->currIconPos.x = 0;
2148 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2154 * Returns the current icon position, and advances it down the left edge.
2155 * The returned position is not offset by Origin.
2158 * [I] infoPtr : valid pointer to the listview structure
2159 * [O] lpPos : will get the current icon position
2164 static void LISTVIEW_NextIconPosLeft(LISTVIEW_INFO *infoPtr, LPPOINT lpPos)
2166 INT nListHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
2168 *lpPos = infoPtr->currIconPos;
2170 infoPtr->currIconPos.y += infoPtr->nItemHeight;
2171 if (infoPtr->currIconPos.y + infoPtr->nItemHeight <= nListHeight) return;
2173 infoPtr->currIconPos.x += infoPtr->nItemWidth;
2174 infoPtr->currIconPos.y = 0;
2180 * Moves an icon to the specified position.
2181 * It takes care of invalidating the item, etc.
2184 * [I] infoPtr : valid pointer to the listview structure
2185 * [I] nItem : the item to move
2186 * [I] lpPos : the new icon position
2187 * [I] isNew : flags the item as being new
2193 static BOOL LISTVIEW_MoveIconTo(LISTVIEW_INFO *infoPtr, INT nItem, const POINT *lppt, BOOL isNew)
2199 old.x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, nItem);
2200 old.y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, nItem);
2202 if (lppt->x == old.x && lppt->y == old.y) return TRUE;
2203 LISTVIEW_InvalidateItem(infoPtr, nItem);
2206 /* Allocating a POINTER for every item is too resource intensive,
2207 * so we'll keep the (x,y) in different arrays */
2208 if (!DPA_SetPtr(infoPtr->hdpaPosX, nItem, (void *)(LONG_PTR)lppt->x)) return FALSE;
2209 if (!DPA_SetPtr(infoPtr->hdpaPosY, nItem, (void *)(LONG_PTR)lppt->y)) return FALSE;
2211 LISTVIEW_InvalidateItem(infoPtr, nItem);
2218 * Arranges listview items in icon display mode.
2221 * [I] infoPtr : valid pointer to the listview structure
2222 * [I] nAlignCode : alignment code
2228 static BOOL LISTVIEW_Arrange(LISTVIEW_INFO *infoPtr, INT nAlignCode)
2230 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2231 void (*next_pos)(LISTVIEW_INFO *, LPPOINT);
2235 if (uView != LVS_ICON && uView != LVS_SMALLICON) return FALSE;
2237 TRACE("nAlignCode=%d\n", nAlignCode);
2239 if (nAlignCode == LVA_DEFAULT)
2241 if (infoPtr->dwStyle & LVS_ALIGNLEFT) nAlignCode = LVA_ALIGNLEFT;
2242 else nAlignCode = LVA_ALIGNTOP;
2247 case LVA_ALIGNLEFT: next_pos = LISTVIEW_NextIconPosLeft; break;
2248 case LVA_ALIGNTOP: next_pos = LISTVIEW_NextIconPosTop; break;
2249 case LVA_SNAPTOGRID: next_pos = LISTVIEW_NextIconPosTop; break; /* FIXME */
2250 default: return FALSE;
2253 infoPtr->bAutoarrange = TRUE;
2254 infoPtr->currIconPos.x = infoPtr->currIconPos.y = 0;
2255 for (i = 0; i < infoPtr->nItemCount; i++)
2257 next_pos(infoPtr, &pos);
2258 LISTVIEW_MoveIconTo(infoPtr, i, &pos, FALSE);
2266 * Retrieves the bounding rectangle of all the items, not offset by Origin.
2269 * [I] infoPtr : valid pointer to the listview structure
2270 * [O] lprcView : bounding rectangle
2276 static void LISTVIEW_GetAreaRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2280 SetRectEmpty(lprcView);
2282 switch (infoPtr->dwStyle & LVS_TYPEMASK)
2286 for (i = 0; i < infoPtr->nItemCount; i++)
2288 x = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosX, i);
2289 y = (LONG_PTR)DPA_GetPtr(infoPtr->hdpaPosY, i);
2290 lprcView->right = max(lprcView->right, x);
2291 lprcView->bottom = max(lprcView->bottom, y);
2293 if (infoPtr->nItemCount > 0)
2295 lprcView->right += infoPtr->nItemWidth;
2296 lprcView->bottom += infoPtr->nItemHeight;
2301 y = LISTVIEW_GetCountPerColumn(infoPtr);
2302 x = infoPtr->nItemCount / y;
2303 if (infoPtr->nItemCount % y) x++;
2304 lprcView->right = x * infoPtr->nItemWidth;
2305 lprcView->bottom = y * infoPtr->nItemHeight;
2309 lprcView->right = infoPtr->nItemWidth;
2310 lprcView->bottom = infoPtr->nItemCount * infoPtr->nItemHeight;
2317 * Retrieves the bounding rectangle of all the items.
2320 * [I] infoPtr : valid pointer to the listview structure
2321 * [O] lprcView : bounding rectangle
2327 static BOOL LISTVIEW_GetViewRect(LISTVIEW_INFO *infoPtr, LPRECT lprcView)
2331 TRACE("(lprcView=%p)\n", lprcView);
2333 if (!lprcView) return FALSE;
2335 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
2336 LISTVIEW_GetAreaRect(infoPtr, lprcView);
2337 OffsetRect(lprcView, ptOrigin.x, ptOrigin.y);
2339 TRACE("lprcView=%s\n", wine_dbgstr_rect(lprcView));
2346 * Retrieves the subitem pointer associated with the subitem index.
2349 * [I] hdpaSubItems : DPA handle for a specific item
2350 * [I] nSubItem : index of subitem
2353 * SUCCESS : subitem pointer
2356 static SUBITEM_INFO* LISTVIEW_GetSubItemPtr(HDPA hdpaSubItems, INT nSubItem)
2358 SUBITEM_INFO *lpSubItem;
2361 /* we should binary search here if need be */
2362 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
2364 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
2365 if (lpSubItem->iSubItem == nSubItem)
2375 * Caclulates the desired item width.
2378 * [I] infoPtr : valid pointer to the listview structure
2381 * The desired item width.
2383 static INT LISTVIEW_CalculateItemWidth(LISTVIEW_INFO *infoPtr)
2385 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2388 TRACE("uView=%d\n", uView);
2390 if (uView == LVS_ICON)
2391 nItemWidth = infoPtr->iconSpacing.cx;
2392 else if (uView == LVS_REPORT)
2396 if (DPA_GetPtrCount(infoPtr->hdpaColumns) > 0)
2398 LISTVIEW_GetHeaderRect(infoPtr, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1, &rcHeader);
2399 nItemWidth = rcHeader.right;
2402 else /* LVS_SMALLICON, or LVS_LIST */
2406 for (i = 0; i < infoPtr->nItemCount; i++)
2407 nItemWidth = max(LISTVIEW_GetLabelWidth(infoPtr, i), nItemWidth);
2409 if (infoPtr->himlSmall) nItemWidth += infoPtr->iconSize.cx;
2410 if (infoPtr->himlState) nItemWidth += infoPtr->iconStateSize.cx;
2412 nItemWidth = max(DEFAULT_COLUMN_WIDTH, nItemWidth + WIDTH_PADDING);
2415 return max(nItemWidth, 1);
2420 * Caclulates the desired item height.
2423 * [I] infoPtr : valid pointer to the listview structure
2426 * The desired item height.
2428 static INT LISTVIEW_CalculateItemHeight(LISTVIEW_INFO *infoPtr)
2430 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
2433 TRACE("uView=%d\n", uView);
2435 if (uView == LVS_ICON)
2436 nItemHeight = infoPtr->iconSpacing.cy;
2439 nItemHeight = infoPtr->ntmHeight;
2440 if (infoPtr->himlState)
2441 nItemHeight = max(nItemHeight, infoPtr->iconStateSize.cy);
2442 if (infoPtr->himlSmall)
2443 nItemHeight = max(nItemHeight, infoPtr->iconSize.cy);
2444 if (infoPtr->himlState || infoPtr->himlSmall)
2445 nItemHeight += HEIGHT_PADDING;
2446 if (infoPtr->nMeasureItemHeight > 0)
2447 nItemHeight = infoPtr->nMeasureItemHeight;
2450 return max(nItemHeight, 1);
2455 * Updates the width, and height of an item.
2458 * [I] infoPtr : valid pointer to the listview structure
2463 static inline void LISTVIEW_UpdateItemSize(LISTVIEW_INFO *infoPtr)
2465 infoPtr->nItemWidth = LISTVIEW_CalculateItemWidth(infoPtr);
2466 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
2472 * Retrieves and saves important text metrics info for the current
2476 * [I] infoPtr : valid pointer to the listview structure
2479 static void LISTVIEW_SaveTextMetrics(LISTVIEW_INFO *infoPtr)
2481 HDC hdc = GetDC(infoPtr->hwndSelf);
2482 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
2483 HFONT hOldFont = SelectObject(hdc, hFont);
2487 if (GetTextMetricsW(hdc, &tm))
2489 infoPtr->ntmHeight = tm.tmHeight;
2490 infoPtr->ntmMaxCharWidth = tm.tmMaxCharWidth;
2493 if (GetTextExtentPoint32A(hdc, "...", 3, &sz))
2494 infoPtr->nEllipsisWidth = sz.cx;
2496 SelectObject(hdc, hOldFont);
2497 ReleaseDC(infoPtr->hwndSelf, hdc);
2499 TRACE("tmHeight=%d\n", infoPtr->ntmHeight);
2504 * A compare function for ranges
2507 * [I] range1 : pointer to range 1;
2508 * [I] range2 : pointer to range 2;
2512 * > 0 : if range 1 > range 2
2513 * < 0 : if range 2 > range 1
2514 * = 0 : if range intersects range 2
2516 static INT CALLBACK ranges_cmp(LPVOID range1, LPVOID range2, LPARAM flags)
2520 if (((RANGE*)range1)->upper <= ((RANGE*)range2)->lower)
2522 else if (((RANGE*)range2)->upper <= ((RANGE*)range1)->lower)
2527 TRACE("range1=%s, range2=%s, cmp=%d\n", debugrange((RANGE*)range1), debugrange((RANGE*)range2), cmp);
2533 #define ranges_check(ranges, desc) ranges_assert(ranges, desc, __FUNCTION__, __LINE__)
2535 #define ranges_check(ranges, desc) do { } while(0)
2538 static void ranges_assert(RANGES ranges, LPCSTR desc, const char *func, int line)
2543 TRACE("*** Checking %s:%d:%s ***\n", func, line, desc);
2545 assert (DPA_GetPtrCount(ranges->hdpa) >= 0);
2546 ranges_dump(ranges);
2547 prev = (RANGE *)DPA_GetPtr(ranges->hdpa, 0);
2548 if (DPA_GetPtrCount(ranges->hdpa) > 0)
2549 assert (prev->lower >= 0 && prev->lower < prev->upper);
2550 for (i = 1; i < DPA_GetPtrCount(ranges->hdpa); i++)
2552 curr = (RANGE *)DPA_GetPtr(ranges->hdpa, i);
2553 assert (prev->upper <= curr->lower);
2554 assert (curr->lower < curr->upper);
2557 TRACE("--- Done checking---\n");
2560 static RANGES ranges_create(int count)
2562 RANGES ranges = (RANGES)Alloc(sizeof(struct tagRANGES));
2563 if (!ranges) return NULL;
2564 ranges->hdpa = DPA_Create(count);
2565 if (ranges->hdpa) return ranges;
2570 static void ranges_clear(RANGES ranges)
2574 for(i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2575 Free(DPA_GetPtr(ranges->hdpa, i));
2576 DPA_DeleteAllPtrs(ranges->hdpa);
2580 static void ranges_destroy(RANGES ranges)
2582 if (!ranges) return;
2583 ranges_clear(ranges);
2584 DPA_Destroy(ranges->hdpa);
2588 static RANGES ranges_clone(RANGES ranges)
2593 if (!(clone = ranges_create(DPA_GetPtrCount(ranges->hdpa)))) goto fail;
2595 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2597 RANGE *newrng = (RANGE *)Alloc(sizeof(RANGE));
2598 if (!newrng) goto fail;
2599 *newrng = *((RANGE*)DPA_GetPtr(ranges->hdpa, i));
2600 DPA_SetPtr(clone->hdpa, i, newrng);
2605 TRACE ("clone failed\n");
2606 ranges_destroy(clone);
2610 static RANGES ranges_diff(RANGES ranges, RANGES sub)
2614 for (i = 0; i < DPA_GetPtrCount(sub->hdpa); i++)
2615 ranges_del(ranges, *((RANGE *)DPA_GetPtr(sub->hdpa, i)));
2620 static void ranges_dump(RANGES ranges)
2624 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2625 TRACE(" %s\n", debugrange(DPA_GetPtr(ranges->hdpa, i)));
2628 static inline BOOL ranges_contain(RANGES ranges, INT nItem)
2630 RANGE srchrng = { nItem, nItem + 1 };
2632 TRACE("(nItem=%d)\n", nItem);
2633 ranges_check(ranges, "before contain");
2634 return DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED) != -1;
2637 static INT ranges_itemcount(RANGES ranges)
2641 for (i = 0; i < DPA_GetPtrCount(ranges->hdpa); i++)
2643 RANGE *sel = DPA_GetPtr(ranges->hdpa, i);
2644 count += sel->upper - sel->lower;
2650 static BOOL ranges_shift(RANGES ranges, INT nItem, INT delta, INT nUpper)
2652 RANGE srchrng = { nItem, nItem + 1 }, *chkrng;
2655 index = DPA_Search(ranges->hdpa, &srchrng, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2656 if (index == -1) return TRUE;
2658 for (; index < DPA_GetPtrCount(ranges->hdpa); index++)
2660 chkrng = DPA_GetPtr(ranges->hdpa, index);
2661 if (chkrng->lower >= nItem)
2662 chkrng->lower = max(min(chkrng->lower + delta, nUpper - 1), 0);
2663 if (chkrng->upper > nItem)
2664 chkrng->upper = max(min(chkrng->upper + delta, nUpper), 0);
2669 static BOOL ranges_add(RANGES ranges, RANGE range)
2674 TRACE("(%s)\n", debugrange(&range));
2675 ranges_check(ranges, "before add");
2677 /* try find overlapping regions first */
2678 srchrgn.lower = range.lower - 1;
2679 srchrgn.upper = range.upper + 1;
2680 index = DPA_Search(ranges->hdpa, &srchrgn, 0, ranges_cmp, 0, DPAS_SORTED);
2686 TRACE("Adding new range\n");
2688 /* create the brand new range to insert */
2689 newrgn = (RANGE *)Alloc(sizeof(RANGE));
2690 if(!newrgn) goto fail;
2693 /* figure out where to insert it */
2694 index = DPA_Search(ranges->hdpa, newrgn, 0, ranges_cmp, 0, DPAS_SORTED | DPAS_INSERTAFTER);
2695 TRACE("index=%d\n", index);
2696 if (index == -1) index = 0;
2698 /* and get it over with */
2699 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2707 RANGE *chkrgn, *mrgrgn;
2708 INT fromindex, mergeindex;
2710 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2711 TRACE("Merge with %s @%d\n", debugrange(chkrgn), index);
2713 chkrgn->lower = min(range.lower, chkrgn->lower);
2714 chkrgn->upper = max(range.upper, chkrgn->upper);
2716 TRACE("New range %s @%d\n", debugrange(chkrgn), index);
2718 /* merge now common anges */
2720 srchrgn.lower = chkrgn->lower - 1;
2721 srchrgn.upper = chkrgn->upper + 1;
2725 mergeindex = DPA_Search(ranges->hdpa, &srchrgn, fromindex, ranges_cmp, 0, 0);
2726 if (mergeindex == -1) break;
2727 if (mergeindex == index)
2729 fromindex = index + 1;
2733 TRACE("Merge with index %i\n", mergeindex);
2735 mrgrgn = DPA_GetPtr(ranges->hdpa, mergeindex);
2736 chkrgn->lower = min(chkrgn->lower, mrgrgn->lower);
2737 chkrgn->upper = max(chkrgn->upper, mrgrgn->upper);
2739 DPA_DeletePtr(ranges->hdpa, mergeindex);
2740 if (mergeindex < index) index --;
2744 ranges_check(ranges, "after add");
2748 ranges_check(ranges, "failed add");
2752 static BOOL ranges_del(RANGES ranges, RANGE range)
2757 TRACE("(%s)\n", debugrange(&range));
2758 ranges_check(ranges, "before del");
2760 /* we don't use DPAS_SORTED here, since we need *
2761 * to find the first overlapping range */
2762 index = DPA_Search(ranges->hdpa, &range, 0, ranges_cmp, 0, 0);
2765 chkrgn = DPA_GetPtr(ranges->hdpa, index);
2767 TRACE("Matches range %s @%d\n", debugrange(chkrgn), index);
2769 /* case 1: Same range */
2770 if ( (chkrgn->upper == range.upper) &&
2771 (chkrgn->lower == range.lower) )
2773 DPA_DeletePtr(ranges->hdpa, index);
2776 /* case 2: engulf */
2777 else if ( (chkrgn->upper <= range.upper) &&
2778 (chkrgn->lower >= range.lower) )
2780 DPA_DeletePtr(ranges->hdpa, index);
2782 /* case 3: overlap upper */
2783 else if ( (chkrgn->upper <= range.upper) &&
2784 (chkrgn->lower < range.lower) )
2786 chkrgn->upper = range.lower;
2788 /* case 4: overlap lower */
2789 else if ( (chkrgn->upper > range.upper) &&
2790 (chkrgn->lower >= range.lower) )
2792 chkrgn->lower = range.upper;
2795 /* case 5: fully internal */
2798 RANGE tmprgn = *chkrgn, *newrgn;
2800 if (!(newrgn = (RANGE *)Alloc(sizeof(RANGE)))) goto fail;
2801 newrgn->lower = chkrgn->lower;
2802 newrgn->upper = range.lower;
2803 chkrgn->lower = range.upper;
2804 if (DPA_InsertPtr(ranges->hdpa, index, newrgn) == -1)
2813 index = DPA_Search(ranges->hdpa, &range, index, ranges_cmp, 0, 0);
2816 ranges_check(ranges, "after del");
2820 ranges_check(ranges, "failed del");
2826 * Removes all selection ranges
2829 * [I] infoPtr : valid pointer to the listview structure
2830 * [I] toSkip : item range to skip removing the selection
2836 static BOOL LISTVIEW_DeselectAllSkipItems(LISTVIEW_INFO *infoPtr, RANGES toSkip)
2845 lvItem.stateMask = LVIS_SELECTED;
2847 /* need to clone the DPA because callbacks can change it */
2848 if (!(clone = ranges_clone(infoPtr->selectionRanges))) return FALSE;
2849 iterator_rangesitems(&i, ranges_diff(clone, toSkip));
2850 while(iterator_next(&i))
2851 LISTVIEW_SetItemState(infoPtr, i.nItem, &lvItem);
2852 /* note that the iterator destructor will free the cloned range */
2853 iterator_destroy(&i);
2858 static inline BOOL LISTVIEW_DeselectAllSkipItem(LISTVIEW_INFO *infoPtr, INT nItem)
2862 if (!(toSkip = ranges_create(1))) return FALSE;
2863 if (nItem != -1) ranges_additem(toSkip, nItem);
2864 LISTVIEW_DeselectAllSkipItems(infoPtr, toSkip);
2865 ranges_destroy(toSkip);
2869 static inline BOOL LISTVIEW_DeselectAll(LISTVIEW_INFO *infoPtr)
2871 return LISTVIEW_DeselectAllSkipItem(infoPtr, -1);
2876 * Retrieves the number of items that are marked as selected.
2879 * [I] infoPtr : valid pointer to the listview structure
2882 * Number of items selected.
2884 static INT LISTVIEW_GetSelectedCount(LISTVIEW_INFO *infoPtr)
2886 INT nSelectedCount = 0;
2888 if (infoPtr->uCallbackMask & LVIS_SELECTED)
2891 for (i = 0; i < infoPtr->nItemCount; i++)
2893 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
2898 nSelectedCount = ranges_itemcount(infoPtr->selectionRanges);
2900 TRACE("nSelectedCount=%d\n", nSelectedCount);
2901 return nSelectedCount;
2906 * Manages the item focus.
2909 * [I] infoPtr : valid pointer to the listview structure
2910 * [I] nItem : item index
2913 * TRUE : focused item changed
2914 * FALSE : focused item has NOT changed
2916 static inline BOOL LISTVIEW_SetItemFocus(LISTVIEW_INFO *infoPtr, INT nItem)
2918 INT oldFocus = infoPtr->nFocusedItem;
2921 if (nItem == infoPtr->nFocusedItem) return FALSE;
2923 lvItem.state = nItem == -1 ? 0 : LVIS_FOCUSED;
2924 lvItem.stateMask = LVIS_FOCUSED;
2925 LISTVIEW_SetItemState(infoPtr, nItem == -1 ? infoPtr->nFocusedItem : nItem, &lvItem);
2927 return oldFocus != infoPtr->nFocusedItem;
2930 /* Helper function for LISTVIEW_ShiftIndices *only* */
2931 static INT shift_item(LISTVIEW_INFO *infoPtr, INT nShiftItem, INT nItem, INT direction)
2933 if (nShiftItem < nItem) return nShiftItem;
2935 if (nShiftItem > nItem) return nShiftItem + direction;
2937 if (direction > 0) return nShiftItem + direction;
2939 return min(nShiftItem, infoPtr->nItemCount - 1);
2944 * Updates the various indices after an item has been inserted or deleted.
2947 * [I] infoPtr : valid pointer to the listview structure
2948 * [I] nItem : item index
2949 * [I] direction : Direction of shift, +1 or -1.
2954 static void LISTVIEW_ShiftIndices(LISTVIEW_INFO *infoPtr, INT nItem, INT direction)
2959 /* temporarily disable change notification while shifting items */
2960 bOldChange = infoPtr->bDoChangeNotify;
2961 infoPtr->bDoChangeNotify = FALSE;
2963 TRACE("Shifting %iu, %i steps\n", nItem, direction);
2965 ranges_shift(infoPtr->selectionRanges, nItem, direction, infoPtr->nItemCount);
2967 assert(abs(direction) == 1);
2969 infoPtr->nSelectionMark = shift_item(infoPtr, infoPtr->nSelectionMark, nItem, direction);
2971 nNewFocus = shift_item(infoPtr, infoPtr->nFocusedItem, nItem, direction);
2972 if (nNewFocus != infoPtr->nFocusedItem)
2973 LISTVIEW_SetItemFocus(infoPtr, nNewFocus);
2975 /* But we are not supposed to modify nHotItem! */
2977 infoPtr->bDoChangeNotify = bOldChange;
2983 * Adds a block of selections.
2986 * [I] infoPtr : valid pointer to the listview structure
2987 * [I] nItem : item index
2990 * Whether the window is still valid.
2992 static BOOL LISTVIEW_AddGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
2994 INT nFirst = min(infoPtr->nSelectionMark, nItem);
2995 INT nLast = max(infoPtr->nSelectionMark, nItem);
2996 HWND hwndSelf = infoPtr->hwndSelf;
2997 NMLVODSTATECHANGE nmlv;
3002 /* Temporarily disable change notification
3003 * If the control is LVS_OWNERDATA, we need to send
3004 * only one LVN_ODSTATECHANGED notification.
3005 * See MSDN documentation for LVN_ITEMCHANGED.
3007 bOldChange = infoPtr->bDoChangeNotify;
3008 if (infoPtr->dwStyle & LVS_OWNERDATA) infoPtr->bDoChangeNotify = FALSE;
3010 if (nFirst == -1) nFirst = nItem;
3012 item.state = LVIS_SELECTED;
3013 item.stateMask = LVIS_SELECTED;
3015 for (i = nFirst; i <= nLast; i++)
3016 LISTVIEW_SetItemState(infoPtr,i,&item);
3018 ZeroMemory(&nmlv, sizeof(nmlv));
3019 nmlv.iFrom = nFirst;
3022 nmlv.uOldState = item.state;
3024 notify_hdr(infoPtr, LVN_ODSTATECHANGED, (LPNMHDR)&nmlv);
3025 if (!IsWindow(hwndSelf))
3027 infoPtr->bDoChangeNotify = bOldChange;
3034 * Sets a single group selection.
3037 * [I] infoPtr : valid pointer to the listview structure
3038 * [I] nItem : item index
3043 static void LISTVIEW_SetGroupSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3045 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3050 if (!(selection = ranges_create(100))) return;
3052 item.state = LVIS_SELECTED;
3053 item.stateMask = LVIS_SELECTED;
3055 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
3057 if (infoPtr->nSelectionMark == -1)
3059 infoPtr->nSelectionMark = nItem;
3060 ranges_additem(selection, nItem);
3066 sel.lower = min(infoPtr->nSelectionMark, nItem);
3067 sel.upper = max(infoPtr->nSelectionMark, nItem) + 1;
3068 ranges_add(selection, sel);
3073 RECT rcItem, rcSel, rcSelMark;
3076 rcItem.left = LVIR_BOUNDS;
3077 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return;
3078 rcSelMark.left = LVIR_BOUNDS;
3079 if (!LISTVIEW_GetItemRect(infoPtr, infoPtr->nSelectionMark, &rcSelMark)) return;
3080 UnionRect(&rcSel, &rcItem, &rcSelMark);
3081 iterator_frameditems(&i, infoPtr, &rcSel);
3082 while(iterator_next(&i))
3084 LISTVIEW_GetItemPosition(infoPtr, i.nItem, &ptItem);
3085 if (PtInRect(&rcSel, ptItem)) ranges_additem(selection, i.nItem);
3087 iterator_destroy(&i);
3090 LISTVIEW_DeselectAllSkipItems(infoPtr, selection);
3091 iterator_rangesitems(&i, selection);
3092 while(iterator_next(&i))
3093 LISTVIEW_SetItemState(infoPtr, i.nItem, &item);
3094 /* this will also destroy the selection */
3095 iterator_destroy(&i);
3097 LISTVIEW_SetItemFocus(infoPtr, nItem);
3102 * Sets a single selection.
3105 * [I] infoPtr : valid pointer to the listview structure
3106 * [I] nItem : item index
3111 static void LISTVIEW_SetSelection(LISTVIEW_INFO *infoPtr, INT nItem)
3115 TRACE("nItem=%d\n", nItem);
3117 LISTVIEW_DeselectAllSkipItem(infoPtr, nItem);
3119 lvItem.state = LVIS_FOCUSED | LVIS_SELECTED;
3120 lvItem.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
3121 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3123 infoPtr->nSelectionMark = nItem;
3128 * Set selection(s) with keyboard.
3131 * [I] infoPtr : valid pointer to the listview structure
3132 * [I] nItem : item index
3135 * SUCCESS : TRUE (needs to be repainted)
3136 * FAILURE : FALSE (nothing has changed)
3138 static BOOL LISTVIEW_KeySelection(LISTVIEW_INFO *infoPtr, INT nItem)
3140 /* FIXME: pass in the state */
3141 WORD wShift = HIWORD(GetKeyState(VK_SHIFT));
3142 WORD wCtrl = HIWORD(GetKeyState(VK_CONTROL));
3143 BOOL bResult = FALSE;
3145 TRACE("nItem=%d, wShift=%d, wCtrl=%d\n", nItem, wShift, wCtrl);
3146 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
3148 if (infoPtr->dwStyle & LVS_SINGLESEL)
3151 LISTVIEW_SetSelection(infoPtr, nItem);
3158 LISTVIEW_SetGroupSelection(infoPtr, nItem);
3163 lvItem.state = ~LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3164 lvItem.stateMask = LVIS_SELECTED;
3165 LISTVIEW_SetItemState(infoPtr, nItem, &lvItem);
3167 if (lvItem.state & LVIS_SELECTED)
3168 infoPtr->nSelectionMark = nItem;
3170 bResult = LISTVIEW_SetItemFocus(infoPtr, nItem);
3175 LISTVIEW_SetSelection(infoPtr, nItem);
3178 LISTVIEW_EnsureVisible(infoPtr, nItem, FALSE);
3181 UpdateWindow(infoPtr->hwndSelf); /* update client area */
3185 static BOOL LISTVIEW_GetItemAtPt(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, POINT pt)
3187 LVHITTESTINFO lvHitTestInfo;
3189 ZeroMemory(&lvHitTestInfo, sizeof(lvHitTestInfo));
3190 lvHitTestInfo.pt.x = pt.x;
3191 lvHitTestInfo.pt.y = pt.y;
3193 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
3195 lpLVItem->mask = LVIF_PARAM;
3196 lpLVItem->iItem = lvHitTestInfo.iItem;
3197 lpLVItem->iSubItem = 0;
3199 return LISTVIEW_GetItemT(infoPtr, lpLVItem, TRUE);
3204 * Called when the mouse is being actively tracked and has hovered for a specified
3208 * [I] infoPtr : valid pointer to the listview structure
3209 * [I] fwKeys : key indicator
3210 * [I] x,y : mouse position
3213 * 0 if the message was processed, non-zero if there was an error
3216 * LVS_EX_TRACKSELECT: An item is automatically selected when the cursor remains
3217 * over the item for a certain period of time.
3220 static LRESULT LISTVIEW_MouseHover(LISTVIEW_INFO *infoPtr, WORD fwKyes, INT x, INT y)
3222 if (infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)
3230 if (LISTVIEW_GetItemAtPt(infoPtr, &item, pt))
3231 LISTVIEW_SetSelection(infoPtr, item.iItem);
3239 * Called whenever WM_MOUSEMOVE is received.
3242 * [I] infoPtr : valid pointer to the listview structure
3243 * [I] fwKeys : key indicator
3244 * [I] x,y : mouse position
3247 * 0 if the message is processed, non-zero if there was an error
3249 static LRESULT LISTVIEW_MouseMove(LISTVIEW_INFO *infoPtr, WORD fwKeys, INT x, INT y)
3251 TRACKMOUSEEVENT trackinfo;
3253 if (!(fwKeys & MK_LBUTTON))
3254 infoPtr->bLButtonDown = FALSE;
3256 if (infoPtr->bLButtonDown && DragDetect(infoPtr->hwndSelf, infoPtr->ptClickPos))
3258 LVHITTESTINFO lvHitTestInfo;
3261 lvHitTestInfo.pt = infoPtr->ptClickPos;
3262 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
3264 ZeroMemory(&nmlv, sizeof(nmlv));
3265 nmlv.iItem = lvHitTestInfo.iItem;
3266 nmlv.ptAction = infoPtr->ptClickPos;
3268 notify_listview(infoPtr, LVN_BEGINDRAG, &nmlv);
3273 infoPtr->bLButtonDown = FALSE;
3275 /* see if we are supposed to be tracking mouse hovering */
3276 if(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT) {
3277 /* fill in the trackinfo struct */
3278 trackinfo.cbSize = sizeof(TRACKMOUSEEVENT);
3279 trackinfo.dwFlags = TME_QUERY;
3280 trackinfo.hwndTrack = infoPtr->hwndSelf;
3281 trackinfo.dwHoverTime = infoPtr->dwHoverTime;
3283 /* see if we are already tracking this hwnd */
3284 _TrackMouseEvent(&trackinfo);
3286 if(!(trackinfo.dwFlags & TME_HOVER)) {
3287 trackinfo.dwFlags = TME_HOVER;
3289 /* call TRACKMOUSEEVENT so we receive WM_MOUSEHOVER messages */
3290 _TrackMouseEvent(&trackinfo);
3299 * Tests wheather the item is assignable to a list with style lStyle
3301 static inline BOOL is_assignable_item(const LVITEMW *lpLVItem, LONG lStyle)
3303 if ( (lpLVItem->mask & LVIF_TEXT) &&
3304 (lpLVItem->pszText == LPSTR_TEXTCALLBACKW) &&
3305 (lStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) ) return FALSE;
3313 * Helper for LISTVIEW_SetItemT *only*: sets item attributes.
3316 * [I] infoPtr : valid pointer to the listview structure
3317 * [I] lpLVItem : valid pointer to new item atttributes
3318 * [I] isNew : the item being set is being inserted
3319 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3320 * [O] bChanged : will be set to TRUE if the item really changed
3326 static BOOL set_main_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isNew, BOOL isW, BOOL *bChanged)
3328 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3336 assert(lpLVItem->iItem >= 0 && lpLVItem->iItem < infoPtr->nItemCount);
3338 if (lpLVItem->mask == 0) return TRUE;
3340 if (infoPtr->dwStyle & LVS_OWNERDATA)
3342 /* a virtual listview we stores only selection and focus */
3343 if (lpLVItem->mask & ~LVIF_STATE)
3349 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3350 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
3354 /* we need to get the lParam and state of the item */
3355 item.iItem = lpLVItem->iItem;
3356 item.iSubItem = lpLVItem->iSubItem;
3357 item.mask = LVIF_STATE | LVIF_PARAM;
3358 item.stateMask = ~0;
3361 if (!isNew && !LISTVIEW_GetItemW(infoPtr, &item)) return FALSE;
3363 TRACE("oldState=%x, newState=%x\n", item.state, lpLVItem->state);
3364 /* determine what fields will change */
3365 if ((lpLVItem->mask & LVIF_STATE) && ((item.state ^ lpLVItem->state) & lpLVItem->stateMask & ~infoPtr->uCallbackMask))
3366 uChanged |= LVIF_STATE;
3368 if ((lpLVItem->mask & LVIF_IMAGE) && (lpItem->hdr.iImage != lpLVItem->iImage))
3369 uChanged |= LVIF_IMAGE;
3371 if ((lpLVItem->mask & LVIF_PARAM) && (lpItem->lParam != lpLVItem->lParam))
3372 uChanged |= LVIF_PARAM;
3374 if ((lpLVItem->mask & LVIF_INDENT) && (lpItem->iIndent != lpLVItem->iIndent))
3375 uChanged |= LVIF_INDENT;
3377 if ((lpLVItem->mask & LVIF_TEXT) && textcmpWT(lpItem->hdr.pszText, lpLVItem->pszText, isW))
3378 uChanged |= LVIF_TEXT;
3380 TRACE("uChanged=0x%x\n", uChanged);
3381 if (!uChanged) return TRUE;
3384 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
3385 nmlv.iItem = lpLVItem->iItem;
3386 nmlv.uNewState = (item.state & ~lpLVItem->stateMask) | (lpLVItem->state & lpLVItem->stateMask);
3387 nmlv.uOldState = item.state;
3388 nmlv.uChanged = uChanged;
3389 nmlv.lParam = item.lParam;
3391 /* send LVN_ITEMCHANGING notification, if the item is not being inserted */
3392 /* and we are _NOT_ virtual (LVS_OWERNDATA), and change notifications */
3394 if(lpItem && !isNew && infoPtr->bDoChangeNotify)
3396 HWND hwndSelf = infoPtr->hwndSelf;
3398 if (notify_listview(infoPtr, LVN_ITEMCHANGING, &nmlv))
3400 if (!IsWindow(hwndSelf))
3404 /* copy information */
3405 if (lpLVItem->mask & LVIF_TEXT)
3406 textsetptrT(&lpItem->hdr.pszText, lpLVItem->pszText, isW);
3408 if (lpLVItem->mask & LVIF_IMAGE)
3409 lpItem->hdr.iImage = lpLVItem->iImage;
3411 if (lpLVItem->mask & LVIF_PARAM)
3412 lpItem->lParam = lpLVItem->lParam;
3414 if (lpLVItem->mask & LVIF_INDENT)
3415 lpItem->iIndent = lpLVItem->iIndent;
3417 if (uChanged & LVIF_STATE)
3419 if (lpItem && (lpLVItem->stateMask & ~infoPtr->uCallbackMask & ~(LVIS_FOCUSED | LVIS_SELECTED)))
3421 lpItem->state &= ~lpLVItem->stateMask;
3422 lpItem->state |= (lpLVItem->state & lpLVItem->stateMask);
3424 if (lpLVItem->state & lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED)
3426 if (infoPtr->dwStyle & LVS_SINGLESEL) LISTVIEW_DeselectAllSkipItem(infoPtr, lpLVItem->iItem);
3427 ranges_additem(infoPtr->selectionRanges, lpLVItem->iItem);
3429 else if (lpLVItem->stateMask & LVIS_SELECTED)
3430 ranges_delitem(infoPtr->selectionRanges, lpLVItem->iItem);
3432 /* if we are asked to change focus, and we manage it, do it */
3433 if (lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED)
3435 if (lpLVItem->state & LVIS_FOCUSED)
3437 LISTVIEW_SetItemFocus(infoPtr, -1);
3438 infoPtr->nFocusedItem = lpLVItem->iItem;
3439 LISTVIEW_EnsureVisible(infoPtr, lpLVItem->iItem, uView == LVS_LIST);
3441 else if (infoPtr->nFocusedItem == lpLVItem->iItem)
3442 infoPtr->nFocusedItem = -1;
3446 /* if we're inserting the item, we're done */
3447 if (isNew) return TRUE;
3449 /* send LVN_ITEMCHANGED notification */
3450 if (lpLVItem->mask & LVIF_PARAM) nmlv.lParam = lpLVItem->lParam;
3451 if (infoPtr->bDoChangeNotify) notify_listview(infoPtr, LVN_ITEMCHANGED, &nmlv);
3458 * Helper for LISTVIEW_{Set,Insert}ItemT *only*: sets subitem attributes.
3461 * [I] infoPtr : valid pointer to the listview structure
3462 * [I] lpLVItem : valid pointer to new subitem atttributes
3463 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3464 * [O] bChanged : will be set to TRUE if the item really changed
3470 static BOOL set_sub_item(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW, BOOL *bChanged)
3473 SUBITEM_INFO *lpSubItem;
3475 /* we do not support subitems for virtual listviews */
3476 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
3478 /* set subitem only if column is present */
3479 if (lpLVItem->iSubItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
3481 /* First do some sanity checks */
3482 if (lpLVItem->mask & ~(LVIF_TEXT | LVIF_IMAGE)) return FALSE;
3483 if (!(lpLVItem->mask & (LVIF_TEXT | LVIF_IMAGE))) return TRUE;
3485 /* get the subitem structure, and create it if not there */
3486 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
3487 assert (hdpaSubItems);
3489 lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
3492 SUBITEM_INFO *tmpSubItem;
3495 lpSubItem = (SUBITEM_INFO *)Alloc(sizeof(SUBITEM_INFO));
3496 if (!lpSubItem) return FALSE;
3497 /* we could binary search here, if need be...*/
3498 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
3500 tmpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
3501 if (tmpSubItem->iSubItem > lpLVItem->iSubItem) break;
3503 if (DPA_InsertPtr(hdpaSubItems, i, lpSubItem) == -1)
3508 lpSubItem->iSubItem = lpLVItem->iSubItem;
3509 lpSubItem->hdr.iImage = I_IMAGECALLBACK;
3513 if (lpLVItem->mask & LVIF_IMAGE)
3514 if (lpSubItem->hdr.iImage != lpLVItem->iImage)
3516 lpSubItem->hdr.iImage = lpLVItem->iImage;
3520 if (lpLVItem->mask & LVIF_TEXT)
3521 if (lpSubItem->hdr.pszText != lpLVItem->pszText)
3523 textsetptrT(&lpSubItem->hdr.pszText, lpLVItem->pszText, isW);
3532 * Sets item attributes.
3535 * [I] infoPtr : valid pointer to the listview structure
3536 * [I] lpLVItem : new item atttributes
3537 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
3543 static BOOL LISTVIEW_SetItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
3545 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3546 HWND hwndSelf = infoPtr->hwndSelf;
3547 LPWSTR pszText = NULL;
3548 BOOL bResult, bChanged = FALSE;
3550 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
3552 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
3555 /* For efficiency, we transform the lpLVItem->pszText to Unicode here */
3556 if ((lpLVItem->mask & LVIF_TEXT) && is_textW(lpLVItem->pszText))
3558 pszText = lpLVItem->pszText;
3559 ((LVITEMW *)lpLVItem)->pszText = textdupTtoW(lpLVItem->pszText, isW);
3562 /* actually set the fields */
3563 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return FALSE;
3565 if (lpLVItem->iSubItem)
3566 bResult = set_sub_item(infoPtr, lpLVItem, TRUE, &bChanged);
3568 bResult = set_main_item(infoPtr, lpLVItem, FALSE, TRUE, &bChanged);
3569 if (!IsWindow(hwndSelf))
3572 /* redraw item, if necessary */
3573 if (bChanged && !infoPtr->bIsDrawing)
3575 /* this little optimization eliminates some nasty flicker */
3576 if ( uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) &&
3577 (!(infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) || lpLVItem->iSubItem) )
3578 LISTVIEW_InvalidateSubItem(infoPtr, lpLVItem->iItem, lpLVItem->iSubItem);
3580 LISTVIEW_InvalidateItem(infoPtr, lpLVItem->iItem);
3585 textfreeT(lpLVItem->pszText, isW);
3586 ((LVITEMW *)lpLVItem)->pszText = pszText;
3594 * Retrieves the index of the item at coordinate (0, 0) of the client area.
3597 * [I] infoPtr : valid pointer to the listview structure
3602 static INT LISTVIEW_GetTopIndex(LISTVIEW_INFO *infoPtr)
3604 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3606 SCROLLINFO scrollInfo;
3608 scrollInfo.cbSize = sizeof(SCROLLINFO);
3609 scrollInfo.fMask = SIF_POS;
3611 if (uView == LVS_LIST)
3613 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
3614 nItem = scrollInfo.nPos * LISTVIEW_GetCountPerColumn(infoPtr);
3616 else if (uView == LVS_REPORT)
3618 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3619 nItem = scrollInfo.nPos;
3623 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
3624 nItem = LISTVIEW_GetCountPerRow(infoPtr) * (scrollInfo.nPos / infoPtr->nItemHeight);
3627 TRACE("nItem=%d\n", nItem);
3635 * Erases the background of the given rectangle
3638 * [I] infoPtr : valid pointer to the listview structure
3639 * [I] hdc : device context handle
3640 * [I] lprcBox : clipping rectangle
3646 static inline BOOL LISTVIEW_FillBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc, const RECT *lprcBox)
3648 if (!infoPtr->hBkBrush) return FALSE;
3650 TRACE("(hdc=%p, lprcBox=%s, hBkBrush=%p)\n", hdc, wine_dbgstr_rect(lprcBox), infoPtr->hBkBrush);
3652 return FillRect(hdc, lprcBox, infoPtr->hBkBrush);
3660 * [I] infoPtr : valid pointer to the listview structure
3661 * [I] hdc : device context handle
3662 * [I] nItem : item index
3663 * [I] nSubItem : subitem index
3664 * [I] pos : item position in client coordinates
3665 * [I] cdmode : custom draw mode
3671 static BOOL LISTVIEW_DrawItem(LISTVIEW_INFO *infoPtr, HDC hdc, INT nItem, INT nSubItem, POINT pos, DWORD cdmode)
3673 UINT uFormat, uView = infoPtr->dwStyle & LVS_TYPEMASK;
3674 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
3675 static const WCHAR szCallback[] = { '(', 'c', 'a', 'l', 'l', 'b', 'a', 'c', 'k', ')', 0 };
3676 DWORD cdsubitemmode = CDRF_DODEFAULT;
3677 RECT* lprcFocus, rcSelect, rcBox, rcState, rcIcon, rcLabel;
3678 NMLVCUSTOMDRAW nmlvcd;
3683 TRACE("(hdc=%p, nItem=%d, nSubItem=%d, pos=%s)\n", hdc, nItem, nSubItem, wine_dbgstr_point(&pos));
3685 /* get information needed for drawing the item */
3686 lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
3687 if (nSubItem == 0) lvItem.mask |= LVIF_STATE | LVIF_PARAM;
3688 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
3689 lvItem.stateMask = LVIS_SELECTED | LVIS_FOCUSED | LVIS_STATEIMAGEMASK;
3690 lvItem.iItem = nItem;
3691 lvItem.iSubItem = nSubItem;
3694 lvItem.cchTextMax = DISP_TEXT_SIZE;
3695 lvItem.pszText = szDispText;
3696 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
3697 if (nSubItem > 0 && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3698 lvItem.state = LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED);
3699 if (lvItem.pszText == LPSTR_TEXTCALLBACKW) lvItem.pszText = (LPWSTR)szCallback;
3700 TRACE(" lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
3702 /* now check if we need to update the focus rectangle */
3703 lprcFocus = infoPtr->bFocus && (lvItem.state & LVIS_FOCUSED) ? &infoPtr->rcFocus : 0;
3705 if (!lprcFocus) lvItem.state &= ~LVIS_FOCUSED;
3706 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
3707 OffsetRect(&rcBox, pos.x, pos.y);
3708 OffsetRect(&rcState, pos.x, pos.y);
3709 OffsetRect(&rcIcon, pos.x, pos.y);
3710 OffsetRect(&rcLabel, pos.x, pos.y);
3711 TRACE(" rcBox=%s, rcState=%s, rcIcon=%s. rcLabel=%s\n",
3712 wine_dbgstr_rect(&rcBox), wine_dbgstr_rect(&rcState),
3713 wine_dbgstr_rect(&rcIcon), wine_dbgstr_rect(&rcLabel));
3715 /* fill in the custom draw structure */
3716 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcBox, &lvItem);
3718 hOldFont = GetCurrentObject(hdc, OBJ_FONT);
3719 if (nSubItem > 0) cdmode = infoPtr->cditemmode;
3720 if (cdmode & CDRF_NOTIFYITEMDRAW)
3721 cdsubitemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3722 if (nSubItem == 0) infoPtr->cditemmode = cdsubitemmode;
3723 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3724 /* we have to send a CDDS_SUBITEM customdraw explicitly for subitem 0 */
3725 if (nSubItem == 0 && cdsubitemmode == CDRF_NOTIFYITEMDRAW)
3727 cdsubitemmode = notify_customdraw(infoPtr, CDDS_SUBITEM | CDDS_ITEMPREPAINT, &nmlvcd);
3728 if (cdsubitemmode & CDRF_SKIPDEFAULT) goto postpaint;
3730 if (nSubItem == 0 || (cdmode & CDRF_NOTIFYITEMDRAW))
3731 prepaint_setup(infoPtr, hdc, &nmlvcd);
3733 /* in full row select, subitems, will just use main item's colors */
3734 if (nSubItem && uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3735 nmlvcd.clrTextBk = CLR_NONE;
3738 if (infoPtr->himlState && !IsRectEmpty(&rcState))
3740 UINT uStateImage = STATEIMAGEINDEX(lvItem.state);
3743 TRACE("uStateImage=%d\n", uStateImage);
3744 ImageList_Draw(infoPtr->himlState, uStateImage - 1, hdc, rcState.left, rcState.top, ILD_NORMAL);
3749 himl = (uView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
3750 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon))
3752 TRACE("iImage=%d\n", lvItem.iImage);
3753 ImageList_Draw(himl, lvItem.iImage, hdc, rcIcon.left, rcIcon.top,
3754 (lvItem.state & LVIS_SELECTED) && (infoPtr->bFocus) ? ILD_SELECTED : ILD_NORMAL);
3757 /* Don't bother painting item being edited */
3758 if (infoPtr->hwndEdit && nItem == infoPtr->nEditLabelItem && nSubItem == 0) goto postpaint;
3760 /* draw the selection background, if we're drawing the main item */
3764 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT))
3765 rcSelect.right = rcBox.right;
3767 if (nmlvcd.clrTextBk != CLR_NONE)
3768 ExtTextOutW(hdc, rcSelect.left, rcSelect.top, ETO_OPAQUE, &rcSelect, 0, 0, 0);
3769 if(lprcFocus) *lprcFocus = rcSelect;
3772 /* figure out the text drawing flags */
3773 uFormat = (uView == LVS_ICON ? (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS) : LV_SL_DT_FLAGS);
3774 if (uView == LVS_ICON)
3775 uFormat = (lprcFocus ? LV_FL_DT_FLAGS : LV_ML_DT_FLAGS);
3778 switch (LISTVIEW_GetColumnInfo(infoPtr, nSubItem)->fmt & LVCFMT_JUSTIFYMASK)
3780 case LVCFMT_RIGHT: uFormat |= DT_RIGHT; break;
3781 case LVCFMT_CENTER: uFormat |= DT_CENTER; break;
3782 default: uFormat |= DT_LEFT;
3785 if (!(uFormat & (DT_RIGHT | DT_CENTER)))
3787 if (himl && lvItem.iImage >= 0 && !IsRectEmpty(&rcIcon)) rcLabel.left += IMAGE_PADDING;
3788 else rcLabel.left += LABEL_HOR_PADDING;
3790 else if (uFormat & DT_RIGHT) rcLabel.right -= LABEL_HOR_PADDING;
3791 DrawTextW(hdc, lvItem.pszText, -1, &rcLabel, uFormat);
3794 if (cdsubitemmode & CDRF_NOTIFYPOSTPAINT)
3795 notify_postpaint(infoPtr, &nmlvcd);
3796 if (cdsubitemmode & CDRF_NEWFONT)
3797 SelectObject(hdc, hOldFont);
3803 * Draws listview items when in owner draw mode.
3806 * [I] infoPtr : valid pointer to the listview structure
3807 * [I] hdc : device context handle
3812 static void LISTVIEW_RefreshOwnerDraw(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3814 UINT uID = (UINT)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
3815 DWORD cditemmode = CDRF_DODEFAULT;
3816 NMLVCUSTOMDRAW nmlvcd;
3817 POINT Origin, Position;
3823 ZeroMemory(&dis, sizeof(dis));
3825 /* Get scroll info once before loop */
3826 LISTVIEW_GetOrigin(infoPtr, &Origin);
3828 /* iterate through the invalidated rows */
3829 while(iterator_next(i))
3831 item.iItem = i->nItem;
3833 item.mask = LVIF_PARAM | LVIF_STATE;
3834 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
3835 if (!LISTVIEW_GetItemW(infoPtr, &item)) continue;
3837 dis.CtlType = ODT_LISTVIEW;
3839 dis.itemID = item.iItem;
3840 dis.itemAction = ODA_DRAWENTIRE;
3842 if (item.state & LVIS_SELECTED) dis.itemState |= ODS_SELECTED;
3843 if (infoPtr->bFocus && (item.state & LVIS_FOCUSED)) dis.itemState |= ODS_FOCUS;
3844 dis.hwndItem = infoPtr->hwndSelf;
3846 LISTVIEW_GetItemOrigin(infoPtr, dis.itemID, &Position);
3847 dis.rcItem.left = Position.x + Origin.x;
3848 dis.rcItem.right = dis.rcItem.left + infoPtr->nItemWidth;
3849 dis.rcItem.top = Position.y + Origin.y;
3850 dis.rcItem.bottom = dis.rcItem.top + infoPtr->nItemHeight;
3851 dis.itemData = item.lParam;
3853 TRACE("item=%s, rcItem=%s\n", debuglvitem_t(&item, TRUE), wine_dbgstr_rect(&dis.rcItem));
3856 * Even if we do not send the CDRF_NOTIFYITEMDRAW we need to fill the nmlvcd
3857 * structure for the rest. of the paint cycle
3859 customdraw_fill(&nmlvcd, infoPtr, hdc, &dis.rcItem, &item);
3860 if (cdmode & CDRF_NOTIFYITEMDRAW)
3861 cditemmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
3863 if (!(cditemmode & CDRF_SKIPDEFAULT))
3865 prepaint_setup (infoPtr, hdc, &nmlvcd);
3866 SendMessageW(infoPtr->hwndNotify, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
3869 if (cditemmode & CDRF_NOTIFYPOSTPAINT)
3870 notify_postpaint(infoPtr, &nmlvcd);
3876 * Draws listview items when in report display mode.
3879 * [I] infoPtr : valid pointer to the listview structure
3880 * [I] hdc : device context handle
3881 * [I] cdmode : custom draw mode
3886 static void LISTVIEW_RefreshReport(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3889 RECT rcClip, rcItem;
3890 POINT Origin, Position;
3896 /* figure out what to draw */
3897 rgntype = GetClipBox(hdc, &rcClip);
3898 if (rgntype == NULLREGION) return;
3900 /* Get scroll info once before loop */
3901 LISTVIEW_GetOrigin(infoPtr, &Origin);
3903 /* narrow down the columns we need to paint */
3904 for(colRange.lower = 0; colRange.lower < DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.lower++)
3906 LISTVIEW_GetHeaderRect(infoPtr, colRange.lower, &rcItem);
3907 if (rcItem.right + Origin.x >= rcClip.left) break;
3909 for(colRange.upper = DPA_GetPtrCount(infoPtr->hdpaColumns); colRange.upper > 0; colRange.upper--)
3911 LISTVIEW_GetHeaderRect(infoPtr, colRange.upper - 1, &rcItem);
3912 if (rcItem.left + Origin.x < rcClip.right) break;
3914 iterator_rangeitems(&j, colRange);
3916 /* in full row select, we _have_ to draw the main item */
3917 if (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT)
3920 /* iterate through the invalidated rows */
3921 while(iterator_next(i))
3923 /* iterate through the invalidated columns */
3924 while(iterator_next(&j))
3926 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3927 Position.x += Origin.x;
3928 Position.y += Origin.y;
3930 if (rgntype == COMPLEXREGION && !((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && j.nItem == 0))
3932 LISTVIEW_GetHeaderRect(infoPtr, j.nItem, &rcItem);
3934 rcItem.bottom = infoPtr->nItemHeight;
3935 OffsetRect(&rcItem, Position.x, Position.y);
3936 if (!RectVisible(hdc, &rcItem)) continue;
3939 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, j.nItem, Position, cdmode);
3942 iterator_destroy(&j);
3947 * Draws listview items when in list display mode.
3950 * [I] infoPtr : valid pointer to the listview structure
3951 * [I] hdc : device context handle
3952 * [I] cdmode : custom draw mode
3957 static void LISTVIEW_RefreshList(LISTVIEW_INFO *infoPtr, ITERATOR *i, HDC hdc, DWORD cdmode)
3959 POINT Origin, Position;
3961 /* Get scroll info once before loop */
3962 LISTVIEW_GetOrigin(infoPtr, &Origin);
3964 while(iterator_prev(i))
3966 LISTVIEW_GetItemOrigin(infoPtr, i->nItem, &Position);
3967 Position.x += Origin.x;
3968 Position.y += Origin.y;
3970 LISTVIEW_DrawItem(infoPtr, hdc, i->nItem, 0, Position, cdmode);
3977 * Draws listview items.
3980 * [I] infoPtr : valid pointer to the listview structure
3981 * [I] hdc : device context handle
3986 static void LISTVIEW_Refresh(LISTVIEW_INFO *infoPtr, HDC hdc)
3988 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
3989 COLORREF oldTextColor, oldClrTextBk, oldClrText;
3990 NMLVCUSTOMDRAW nmlvcd;
3997 LISTVIEW_DUMP(infoPtr);
3999 infoPtr->bIsDrawing = TRUE;
4001 /* save dc values we're gonna trash while drawing */
4002 hOldFont = SelectObject(hdc, infoPtr->hFont);
4003 oldBkMode = GetBkMode(hdc);
4004 infoPtr->clrTextBkDefault = GetBkColor(hdc);
4005 oldTextColor = GetTextColor(hdc);
4007 oldClrTextBk = infoPtr->clrTextBk;
4008 oldClrText = infoPtr->clrText;
4010 infoPtr->cditemmode = CDRF_DODEFAULT;
4012 GetClientRect(infoPtr->hwndSelf, &rcClient);
4013 customdraw_fill(&nmlvcd, infoPtr, hdc, &rcClient, 0);
4014 cdmode = notify_customdraw(infoPtr, CDDS_PREPAINT, &nmlvcd);
4015 if (cdmode & CDRF_SKIPDEFAULT) goto enddraw;
4016 prepaint_setup(infoPtr, hdc, &nmlvcd);
4018 /* Use these colors to draw the items */
4019 infoPtr->clrTextBk = nmlvcd.clrTextBk;
4020 infoPtr->clrText = nmlvcd.clrText;
4022 /* nothing to draw */
4023 if(infoPtr->nItemCount == 0) goto enddraw;
4025 /* figure out what we need to draw */
4026 iterator_visibleitems(&i, infoPtr, hdc);
4028 /* send cache hint notification */
4029 if (infoPtr->dwStyle & LVS_OWNERDATA)
4031 RANGE range = iterator_range(&i);
4034 ZeroMemory(&nmlv, sizeof(NMLVCACHEHINT));
4035 nmlv.iFrom = range.lower;
4036 nmlv.iTo = range.upper - 1;
4037 notify_hdr(infoPtr, LVN_ODCACHEHINT, &nmlv.hdr);
4040 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
4041 LISTVIEW_RefreshOwnerDraw(infoPtr, &i, hdc, cdmode);
4044 if (uView == LVS_REPORT)
4045 LISTVIEW_RefreshReport(infoPtr, &i, hdc, cdmode);
4046 else /* LVS_LIST, LVS_ICON or LVS_SMALLICON */
4047 LISTVIEW_RefreshList(infoPtr, &i, hdc, cdmode);
4049 /* if we have a focus rect, draw it */
4050 if (infoPtr->bFocus)
4051 DrawFocusRect(hdc, &infoPtr->rcFocus);
4053 iterator_destroy(&i);
4056 if (cdmode & CDRF_NOTIFYPOSTPAINT)
4057 notify_postpaint(infoPtr, &nmlvcd);
4059 infoPtr->clrTextBk = oldClrTextBk;
4060 infoPtr->clrText = oldClrText;
4062 SelectObject(hdc, hOldFont);
4063 SetBkMode(hdc, oldBkMode);
4064 SetBkColor(hdc, infoPtr->clrTextBkDefault);
4065 SetTextColor(hdc, oldTextColor);
4066 infoPtr->bIsDrawing = FALSE;
4072 * Calculates the approximate width and height of a given number of items.
4075 * [I] infoPtr : valid pointer to the listview structure
4076 * [I] nItemCount : number of items
4077 * [I] wWidth : width
4078 * [I] wHeight : height
4081 * Returns a DWORD. The width in the low word and the height in high word.
4083 static DWORD LISTVIEW_ApproximateViewRect(LISTVIEW_INFO *infoPtr, INT nItemCount,
4084 WORD wWidth, WORD wHeight)
4086 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4087 INT nItemCountPerColumn = 1;
4088 INT nColumnCount = 0;
4089 DWORD dwViewRect = 0;
4091 if (nItemCount == -1)
4092 nItemCount = infoPtr->nItemCount;
4094 if (uView == LVS_LIST)
4096 if (wHeight == 0xFFFF)
4098 /* use current height */
4099 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4102 if (wHeight < infoPtr->nItemHeight)
4103 wHeight = infoPtr->nItemHeight;
4107 if (infoPtr->nItemHeight > 0)
4109 nItemCountPerColumn = wHeight / infoPtr->nItemHeight;
4110 if (nItemCountPerColumn == 0)
4111 nItemCountPerColumn = 1;
4113 if (nItemCount % nItemCountPerColumn != 0)
4114 nColumnCount = nItemCount / nItemCountPerColumn;
4116 nColumnCount = nItemCount / nItemCountPerColumn + 1;
4120 /* Microsoft padding magic */
4121 wHeight = nItemCountPerColumn * infoPtr->nItemHeight + 2;
4122 wWidth = nColumnCount * infoPtr->nItemWidth + 2;
4124 dwViewRect = MAKELONG(wWidth, wHeight);
4126 else if (uView == LVS_REPORT)
4130 if (infoPtr->nItemCount > 0)
4132 LISTVIEW_GetItemBox(infoPtr, 0, &rcBox);
4133 wWidth = rcBox.right - rcBox.left;
4134 wHeight = (rcBox.bottom - rcBox.top) * nItemCount;
4138 /* use current height and width */
4139 if (wHeight == 0xffff)
4140 wHeight = infoPtr->rcList.bottom - infoPtr->rcList.top;
4141 if (wWidth == 0xffff)
4142 wWidth = infoPtr->rcList.right - infoPtr->rcList.left;
4145 dwViewRect = MAKELONG(wWidth, wHeight);
4147 else if (uView == LVS_SMALLICON)
4148 FIXME("uView == LVS_SMALLICON: not implemented\n");
4149 else if (uView == LVS_ICON)
4150 FIXME("uView == LVS_ICON: not implemented\n");
4158 * Create a drag image list for the specified item.
4161 * [I] infoPtr : valid pointer to the listview structure
4162 * [I] iItem : index of item
4163 * [O] lppt : Upperr-left corner of the image
4166 * Returns a handle to the image list if successful, NULL otherwise.
4168 static HIMAGELIST LISTVIEW_CreateDragImage(LISTVIEW_INFO *infoPtr, INT iItem, LPPOINT lppt)
4174 HBITMAP hbmp, hOldbmp;
4175 HIMAGELIST dragList = 0;
4176 TRACE("iItem=%d Count=%d\n", iItem, infoPtr->nItemCount);
4178 if (iItem < 0 || iItem >= infoPtr->nItemCount)
4181 rcItem.left = LVIR_BOUNDS;
4182 if (!LISTVIEW_GetItemRect(infoPtr, iItem, &rcItem))
4185 lppt->x = rcItem.left;
4186 lppt->y = rcItem.top;
4188 size.cx = rcItem.right - rcItem.left;
4189 size.cy = rcItem.bottom - rcItem.top;
4191 hdcOrig = GetDC(infoPtr->hwndSelf);
4192 hdc = CreateCompatibleDC(hdcOrig);
4193 hbmp = CreateCompatibleBitmap(hdcOrig, size.cx, size.cy);
4194 hOldbmp = SelectObject(hdc, hbmp);
4196 rcItem.left = rcItem.top = 0;
4197 rcItem.right = size.cx;
4198 rcItem.bottom = size.cy;
4199 FillRect(hdc, &rcItem, infoPtr->hBkBrush);
4202 if (LISTVIEW_DrawItem(infoPtr, hdc, iItem, 0, pos, infoPtr->cditemmode))
4204 dragList = ImageList_Create(size.cx, size.cy, ILC_COLOR, 10, 10);
4205 SelectObject(hdc, hOldbmp);
4206 ImageList_Add(dragList, hbmp, 0);
4209 SelectObject(hdc, hOldbmp);
4213 ReleaseDC(infoPtr->hwndSelf, hdcOrig);
4215 TRACE("ret=%p\n", dragList);
4223 * Removes all listview items and subitems.
4226 * [I] infoPtr : valid pointer to the listview structure
4232 static BOOL LISTVIEW_DeleteAllItems(LISTVIEW_INFO *infoPtr)
4235 HDPA hdpaSubItems = NULL;
4242 /* we do it directly, to avoid notifications */
4243 ranges_clear(infoPtr->selectionRanges);
4244 infoPtr->nSelectionMark = -1;
4245 infoPtr->nFocusedItem = -1;
4246 SetRectEmpty(&infoPtr->rcFocus);
4247 /* But we are supposed to leave nHotItem as is! */
4250 /* send LVN_DELETEALLITEMS notification */
4251 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
4253 bSuppress = notify_listview(infoPtr, LVN_DELETEALLITEMS, &nmlv);
4255 for (i = infoPtr->nItemCount - 1; i >= 0; i--)
4257 /* send LVN_DELETEITEM notification, if not suppressed */
4258 if (!bSuppress) notify_deleteitem(infoPtr, i);
4259 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4261 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
4262 for (j = 0; j < DPA_GetPtrCount(hdpaSubItems); j++)
4264 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, j);
4265 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4268 DPA_Destroy(hdpaSubItems);
4269 DPA_DeletePtr(infoPtr->hdpaItems, i);
4271 DPA_DeletePtr(infoPtr->hdpaPosX, i);
4272 DPA_DeletePtr(infoPtr->hdpaPosY, i);
4273 infoPtr->nItemCount --;
4276 LISTVIEW_UpdateScroll(infoPtr);
4278 LISTVIEW_InvalidateList(infoPtr);
4285 * Scrolls, and updates the columns, when a column is changing width.
4288 * [I] infoPtr : valid pointer to the listview structure
4289 * [I] nColumn : column to scroll
4290 * [I] dx : amount of scroll, in pixels
4295 static void LISTVIEW_ScrollColumns(LISTVIEW_INFO *infoPtr, INT nColumn, INT dx)
4297 COLUMN_INFO *lpColumnInfo;
4302 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) < 1) return;
4303 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns) - 1));
4304 rcCol = lpColumnInfo->rcHeader;
4305 if (nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns))
4306 rcCol.left = rcCol.right;
4308 /* ajust the other columns */
4309 for (nCol = nColumn; nCol < DPA_GetPtrCount(infoPtr->hdpaColumns); nCol++)
4311 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nCol);
4312 lpColumnInfo->rcHeader.left += dx;
4313 lpColumnInfo->rcHeader.right += dx;
4316 /* do not update screen if not in report mode */
4317 if (!is_redrawing(infoPtr) || (infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return;
4319 /* if we have a focus, must first erase the focus rect */
4320 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, FALSE);
4322 /* Need to reset the item width when inserting a new column */
4323 infoPtr->nItemWidth += dx;
4325 LISTVIEW_UpdateScroll(infoPtr);
4326 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
4328 /* scroll to cover the deleted column, and invalidate for redraw */
4329 rcOld = infoPtr->rcList;
4330 rcOld.left = ptOrigin.x + rcCol.left + dx;
4331 ScrollWindowEx(infoPtr->hwndSelf, dx, 0, &rcOld, &rcOld, 0, 0, SW_ERASE | SW_INVALIDATE);
4333 /* we can restore focus now */
4334 if (infoPtr->bFocus) LISTVIEW_ShowFocusRect(infoPtr, TRUE);
4339 * Removes a column from the listview control.
4342 * [I] infoPtr : valid pointer to the listview structure
4343 * [I] nColumn : column index
4349 static BOOL LISTVIEW_DeleteColumn(LISTVIEW_INFO *infoPtr, INT nColumn)
4353 TRACE("nColumn=%d\n", nColumn);
4355 if (nColumn < 0 || DPA_GetPtrCount(infoPtr->hdpaColumns) == 0
4356 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4358 /* While the MSDN specifically says that column zero should not be deleted,
4359 what actually happens is that the column itself is deleted but no items or subitems
4363 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcCol);
4365 if (!Header_DeleteItem(infoPtr->hwndHeader, nColumn))
4368 Free(DPA_GetPtr(infoPtr->hdpaColumns, nColumn));
4369 DPA_DeletePtr(infoPtr->hdpaColumns, nColumn);
4371 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && nColumn)
4373 SUBITEM_INFO *lpSubItem, *lpDelItem;
4375 INT nItem, nSubItem, i;
4377 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
4379 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
4382 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
4384 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
4385 if (lpSubItem->iSubItem == nColumn)
4388 lpDelItem = lpSubItem;
4390 else if (lpSubItem->iSubItem > nColumn)
4392 lpSubItem->iSubItem--;
4396 /* if we found our subitem, zapp it */
4400 if (is_textW(lpDelItem->hdr.pszText))
4401 Free(lpDelItem->hdr.pszText);
4406 /* free dpa memory */
4407 DPA_DeletePtr(hdpaSubItems, nSubItem);
4412 /* update the other column info */
4413 if(DPA_GetPtrCount(infoPtr->hdpaColumns) == 0)
4414 LISTVIEW_InvalidateList(infoPtr);
4416 LISTVIEW_ScrollColumns(infoPtr, nColumn, -(rcCol.right - rcCol.left));
4423 * Invalidates the listview after an item's insertion or deletion.
4426 * [I] infoPtr : valid pointer to the listview structure
4427 * [I] nItem : item index
4428 * [I] dir : -1 if deleting, 1 if inserting
4433 static void LISTVIEW_ScrollOnInsert(LISTVIEW_INFO *infoPtr, INT nItem, INT dir)
4435 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4436 INT nPerCol, nItemCol, nItemRow;
4440 /* if we don't refresh, what's the point of scrolling? */
4441 if (!is_redrawing(infoPtr)) return;
4443 assert (abs(dir) == 1);
4445 /* arrange icons if autoarrange is on */
4446 if (is_autoarrange(infoPtr))
4448 BOOL arrange = TRUE;
4449 if (dir < 0 && nItem >= infoPtr->nItemCount) arrange = FALSE;
4450 if (dir > 0 && nItem == infoPtr->nItemCount - 1) arrange = FALSE;
4451 if (arrange) LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
4454 /* scrollbars need updating */
4455 LISTVIEW_UpdateScroll(infoPtr);
4457 /* figure out the item's position */
4458 if (uView == LVS_REPORT)
4459 nPerCol = infoPtr->nItemCount + 1;
4460 else if (uView == LVS_LIST)
4461 nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
4462 else /* LVS_ICON, or LVS_SMALLICON */
4465 nItemCol = nItem / nPerCol;
4466 nItemRow = nItem % nPerCol;
4467 LISTVIEW_GetOrigin(infoPtr, &Origin);
4469 /* move the items below up a slot */
4470 rcScroll.left = nItemCol * infoPtr->nItemWidth;
4471 rcScroll.top = nItemRow * infoPtr->nItemHeight;
4472 rcScroll.right = rcScroll.left + infoPtr->nItemWidth;
4473 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4474 OffsetRect(&rcScroll, Origin.x, Origin.y);
4475 TRACE("rcScroll=%s, dx=%d\n", wine_dbgstr_rect(&rcScroll), dir * infoPtr->nItemHeight);
4476 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4478 TRACE("Scrolling rcScroll=%s, rcList=%s\n", wine_dbgstr_rect(&rcScroll), wine_dbgstr_rect(&infoPtr->rcList));
4479 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4480 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4483 /* report has only that column, so we're done */
4484 if (uView == LVS_REPORT) return;
4486 /* now for LISTs, we have to deal with the columns to the right */
4487 rcScroll.left = (nItemCol + 1) * infoPtr->nItemWidth;
4489 rcScroll.right = (infoPtr->nItemCount / nPerCol + 1) * infoPtr->nItemWidth;
4490 rcScroll.bottom = nPerCol * infoPtr->nItemHeight;
4491 OffsetRect(&rcScroll, Origin.x, Origin.y);
4492 if (IntersectRect(&rcScroll, &rcScroll, &infoPtr->rcList))
4493 ScrollWindowEx(infoPtr->hwndSelf, 0, dir * infoPtr->nItemHeight,
4494 &rcScroll, &rcScroll, 0, 0, SW_ERASE | SW_INVALIDATE);
4499 * Removes an item from the listview control.
4502 * [I] infoPtr : valid pointer to the listview structure
4503 * [I] nItem : item index
4509 static BOOL LISTVIEW_DeleteItem(LISTVIEW_INFO *infoPtr, INT nItem)
4511 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4514 TRACE("(nItem=%d)\n", nItem);
4516 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
4518 /* remove selection, and focus */
4520 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
4521 LISTVIEW_SetItemState(infoPtr, nItem, &item);
4523 /* send LVN_DELETEITEM notification. */
4524 if (!notify_deleteitem(infoPtr, nItem)) return FALSE;
4526 /* we need to do this here, because we'll be deleting stuff */
4527 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4528 LISTVIEW_InvalidateItem(infoPtr, nItem);
4530 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4536 hdpaSubItems = (HDPA)DPA_DeletePtr(infoPtr->hdpaItems, nItem);
4537 for (i = 0; i < DPA_GetPtrCount(hdpaSubItems); i++)
4539 hdrItem = (ITEMHDR *)DPA_GetPtr(hdpaSubItems, i);
4540 if (is_textW(hdrItem->pszText)) Free(hdrItem->pszText);
4543 DPA_Destroy(hdpaSubItems);
4546 if (uView == LVS_SMALLICON || uView == LVS_ICON)
4548 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
4549 DPA_DeletePtr(infoPtr->hdpaPosY, nItem);
4552 infoPtr->nItemCount--;
4553 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
4555 /* now is the invalidation fun */
4556 LISTVIEW_ScrollOnInsert(infoPtr, nItem, -1);
4563 * Callback implementation for editlabel control
4566 * [I] infoPtr : valid pointer to the listview structure
4567 * [I] pszText : modified text
4568 * [I] isW : TRUE if psxText is Unicode, FALSE if it's ANSI
4574 static BOOL LISTVIEW_EndEditLabelT(LISTVIEW_INFO *infoPtr, LPWSTR pszText, BOOL isW)
4576 HWND hwndSelf = infoPtr->hwndSelf;
4577 NMLVDISPINFOW dispInfo;
4579 TRACE("(pszText=%s, isW=%d)\n", debugtext_t(pszText, isW), isW);
4581 ZeroMemory(&dispInfo, sizeof(dispInfo));
4582 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE;
4583 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4584 dispInfo.item.iSubItem = 0;
4585 dispInfo.item.stateMask = ~0;
4586 if (!LISTVIEW_GetItemW(infoPtr, &dispInfo.item)) return FALSE;
4587 /* add the text from the edit in */
4588 dispInfo.item.mask |= LVIF_TEXT;
4589 dispInfo.item.pszText = pszText;
4590 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4592 /* Do we need to update the Item Text */
4593 if (!notify_dispinfoT(infoPtr, LVN_ENDLABELEDITW, &dispInfo, isW)) return FALSE;
4594 if (!IsWindow(hwndSelf))
4596 if (!pszText) return TRUE;
4598 if (!(infoPtr->dwStyle & LVS_OWNERDATA))
4600 HDPA hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nEditLabelItem);
4601 ITEM_INFO* lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
4602 if (lpItem && lpItem->hdr.pszText == LPSTR_TEXTCALLBACKW)
4604 LISTVIEW_InvalidateItem(infoPtr, infoPtr->nEditLabelItem);
4609 ZeroMemory(&dispInfo, sizeof(dispInfo));
4610 dispInfo.item.mask = LVIF_TEXT;
4611 dispInfo.item.iItem = infoPtr->nEditLabelItem;
4612 dispInfo.item.iSubItem = 0;
4613 dispInfo.item.pszText = pszText;
4614 dispInfo.item.cchTextMax = textlenT(pszText, isW);
4615 return LISTVIEW_SetItemT(infoPtr, &dispInfo.item, isW);
4620 * Begin in place editing of specified list view item
4623 * [I] infoPtr : valid pointer to the listview structure
4624 * [I] nItem : item index
4625 * [I] isW : TRUE if it's a Unicode req, FALSE if ASCII
4631 static HWND LISTVIEW_EditLabelT(LISTVIEW_INFO *infoPtr, INT nItem, BOOL isW)
4633 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
4634 NMLVDISPINFOW dispInfo;
4636 HWND hwndSelf = infoPtr->hwndSelf;
4638 TRACE("(nItem=%d, isW=%d)\n", nItem, isW);
4640 if (~infoPtr->dwStyle & LVS_EDITLABELS) return 0;
4641 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
4643 infoPtr->nEditLabelItem = nItem;
4645 /* Is the EditBox still there, if so remove it */
4646 if(infoPtr->hwndEdit != 0)
4648 SetFocus(infoPtr->hwndSelf);
4649 infoPtr->hwndEdit = 0;
4652 LISTVIEW_SetSelection(infoPtr, nItem);
4653 LISTVIEW_SetItemFocus(infoPtr, nItem);
4654 LISTVIEW_InvalidateItem(infoPtr, nItem);
4656 rect.left = LVIR_LABEL;
4657 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rect)) return 0;
4659 ZeroMemory(&dispInfo, sizeof(dispInfo));
4660 dispInfo.item.mask = LVIF_PARAM | LVIF_STATE | LVIF_TEXT;
4661 dispInfo.item.iItem = nItem;
4662 dispInfo.item.iSubItem = 0;
4663 dispInfo.item.stateMask = ~0;
4664 dispInfo.item.pszText = szDispText;
4665 dispInfo.item.cchTextMax = DISP_TEXT_SIZE;
4666 if (!LISTVIEW_GetItemT(infoPtr, &dispInfo.item, isW)) return 0;
4668 infoPtr->hwndEdit = CreateEditLabelT(infoPtr, dispInfo.item.pszText, WS_VISIBLE,
4669 rect.left-2, rect.top-1, 0, rect.bottom - rect.top+2, isW);
4670 if (!infoPtr->hwndEdit) return 0;
4672 if (notify_dispinfoT(infoPtr, LVN_BEGINLABELEDITW, &dispInfo, isW))
4674 if (!IsWindow(hwndSelf))
4676 SendMessageW(infoPtr->hwndEdit, WM_CLOSE, 0, 0);
4677 infoPtr->hwndEdit = 0;
4681 ShowWindow(infoPtr->hwndEdit, SW_NORMAL);
4682 SetFocus(infoPtr->hwndEdit);
4683 SendMessageW(infoPtr->hwndEdit, EM_SETSEL, 0, -1);
4684 return infoPtr->hwndEdit;
4690 * Ensures the specified item is visible, scrolling into view if necessary.
4693 * [I] infoPtr : valid pointer to the listview structure
4694 * [I] nItem : item index
4695 * [I] bPartial : partially or entirely visible
4701 static BOOL LISTVIEW_EnsureVisible(LISTVIEW_INFO *infoPtr, INT nItem, BOOL bPartial)
4703 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4704 INT nScrollPosHeight = 0;
4705 INT nScrollPosWidth = 0;
4706 INT nHorzAdjust = 0;
4707 INT nVertAdjust = 0;
4710 RECT rcItem, rcTemp;
4712 rcItem.left = LVIR_BOUNDS;
4713 if (!LISTVIEW_GetItemRect(infoPtr, nItem, &rcItem)) return FALSE;
4715 if (bPartial && IntersectRect(&rcTemp, &infoPtr->rcList, &rcItem)) return TRUE;
4717 if (rcItem.left < infoPtr->rcList.left || rcItem.right > infoPtr->rcList.right)
4719 /* scroll left/right, but in LVS_REPORT mode */
4720 if (uView == LVS_LIST)
4721 nScrollPosWidth = infoPtr->nItemWidth;
4722 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
4723 nScrollPosWidth = 1;
4725 if (rcItem.left < infoPtr->rcList.left)
4728 if (uView != LVS_REPORT) nHorzDiff = rcItem.left - infoPtr->rcList.left;
4733 if (uView != LVS_REPORT) nHorzDiff = rcItem.right - infoPtr->rcList.right;
4737 if (rcItem.top < infoPtr->rcList.top || rcItem.bottom > infoPtr->rcList.bottom)
4739 /* scroll up/down, but not in LVS_LIST mode */
4740 if (uView == LVS_REPORT)
4741 nScrollPosHeight = infoPtr->nItemHeight;
4742 else if ((uView == LVS_ICON) || (uView == LVS_SMALLICON))
4743 nScrollPosHeight = 1;
4745 if (rcItem.top < infoPtr->rcList.top)
4748 if (uView != LVS_LIST) nVertDiff = rcItem.top - infoPtr->rcList.top;
4753 if (uView != LVS_LIST) nVertDiff = rcItem.bottom - infoPtr->rcList.bottom;
4757 if (!nScrollPosWidth && !nScrollPosHeight) return TRUE;
4759 if (nScrollPosWidth)
4761 INT diff = nHorzDiff / nScrollPosWidth;
4762 if (nHorzDiff % nScrollPosWidth) diff += nHorzAdjust;
4763 LISTVIEW_HScroll(infoPtr, SB_INTERNAL, diff, 0);
4766 if (nScrollPosHeight)
4768 INT diff = nVertDiff / nScrollPosHeight;
4769 if (nVertDiff % nScrollPosHeight) diff += nVertAdjust;
4770 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, diff, 0);
4778 * Searches for an item with specific characteristics.
4781 * [I] hwnd : window handle
4782 * [I] nStart : base item index
4783 * [I] lpFindInfo : item information to look for
4786 * SUCCESS : index of item
4789 static INT LISTVIEW_FindItemW(LISTVIEW_INFO *infoPtr, INT nStart,
4790 const LVFINDINFOW *lpFindInfo)
4792 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
4793 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
4794 BOOL bWrap = FALSE, bNearest = FALSE;
4795 INT nItem = nStart + 1, nLast = infoPtr->nItemCount, nNearestItem = -1;
4796 ULONG xdist, ydist, dist, mindist = 0x7fffffff;
4797 POINT Position, Destination;
4800 if (!lpFindInfo || nItem < 0) return -1;
4803 if (lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL))
4805 lvItem.mask |= LVIF_TEXT;
4806 lvItem.pszText = szDispText;
4807 lvItem.cchTextMax = DISP_TEXT_SIZE;
4810 if (lpFindInfo->flags & LVFI_WRAP)
4813 if ((lpFindInfo->flags & LVFI_NEARESTXY) &&
4814 (uView == LVS_ICON || uView ==LVS_SMALLICON))
4819 LISTVIEW_GetOrigin(infoPtr, &Origin);
4820 Destination.x = lpFindInfo->pt.x - Origin.x;
4821 Destination.y = lpFindInfo->pt.y - Origin.y;
4822 switch(lpFindInfo->vkDirection)
4824 case VK_DOWN: Destination.y += infoPtr->nItemHeight; break;
4825 case VK_UP: Destination.y -= infoPtr->nItemHeight; break;
4826 case VK_RIGHT: Destination.x += infoPtr->nItemWidth; break;
4827 case VK_LEFT: Destination.x -= infoPtr->nItemWidth; break;
4828 case VK_HOME: Destination.x = Destination.y = 0; break;
4829 case VK_NEXT: Destination.y += infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4830 case VK_PRIOR: Destination.y -= infoPtr->rcList.bottom - infoPtr->rcList.top; break;
4832 LISTVIEW_GetAreaRect(infoPtr, &rcArea);
4833 Destination.x = rcArea.right;
4834 Destination.y = rcArea.bottom;
4836 default: ERR("Unknown vkDirection=%d\n", lpFindInfo->vkDirection);
4840 else Destination.x = Destination.y = 0;
4842 /* if LVFI_PARAM is specified, all other flags are ignored */
4843 if (lpFindInfo->flags & LVFI_PARAM)
4845 lvItem.mask |= LVIF_PARAM;
4847 lvItem.mask &= ~LVIF_TEXT;
4851 for (; nItem < nLast; nItem++)
4853 lvItem.iItem = nItem;
4854 lvItem.iSubItem = 0;
4855 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
4857 if (lvItem.mask & LVIF_PARAM)
4859 if (lpFindInfo->lParam == lvItem.lParam)
4865 if (lvItem.mask & LVIF_TEXT)
4867 if (lpFindInfo->flags & LVFI_PARTIAL)
4869 if (strstrW(lvItem.pszText, lpFindInfo->psz) == NULL) continue;
4873 if (lstrcmpW(lvItem.pszText, lpFindInfo->psz) != 0) continue;
4877 if (!bNearest) return nItem;
4879 /* This is very inefficient. To do a good job here,
4880 * we need a sorted array of (x,y) item positions */
4881 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
4883 /* compute the distance^2 to the destination */
4884 xdist = Destination.x - Position.x;
4885 ydist = Destination.y - Position.y;
4886 dist = xdist * xdist + ydist * ydist;
4888 /* remember the distance, and item if it's closer */
4892 nNearestItem = nItem;
4899 nLast = min(nStart + 1, infoPtr->nItemCount);
4904 return nNearestItem;
4909 * Searches for an item with specific characteristics.
4912 * [I] hwnd : window handle
4913 * [I] nStart : base item index
4914 * [I] lpFindInfo : item information to look for
4917 * SUCCESS : index of item
4920 static INT LISTVIEW_FindItemA(LISTVIEW_INFO *infoPtr, INT nStart,
4921 const LVFINDINFOA *lpFindInfo)
4923 BOOL hasText = lpFindInfo->flags & (LVFI_STRING | LVFI_PARTIAL);
4927 memcpy(&fiw, lpFindInfo, sizeof(fiw));
4928 if (hasText) fiw.psz = textdupTtoW((LPCWSTR)lpFindInfo->psz, FALSE);
4929 res = LISTVIEW_FindItemW(infoPtr, nStart, &fiw);
4930 if (hasText) textfreeT((LPWSTR)fiw.psz, FALSE);
4936 * Retrieves the background image of the listview control.
4939 * [I] infoPtr : valid pointer to the listview structure
4940 * [O] lpBkImage : background image attributes
4946 /* static BOOL LISTVIEW_GetBkImage(LISTVIEW_INFO *infoPtr, LPLVBKIMAGE lpBkImage) */
4948 /* FIXME (listview, "empty stub!\n"); */
4954 * Retrieves column attributes.
4957 * [I] infoPtr : valid pointer to the listview structure
4958 * [I] nColumn : column index
4959 * [IO] lpColumn : column information
4960 * [I] isW : if TRUE, then lpColumn is a LPLVCOLUMNW
4961 * otherwise it is in fact a LPLVCOLUMNA
4967 static BOOL LISTVIEW_GetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn, LPLVCOLUMNW lpColumn, BOOL isW)
4969 COLUMN_INFO *lpColumnInfo;
4972 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
4973 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
4975 /* initialize memory */
4976 ZeroMemory(&hdi, sizeof(hdi));
4978 if (lpColumn->mask & LVCF_TEXT)
4980 hdi.mask |= HDI_TEXT;
4981 hdi.pszText = lpColumn->pszText;
4982 hdi.cchTextMax = lpColumn->cchTextMax;
4985 if (lpColumn->mask & LVCF_IMAGE)
4986 hdi.mask |= HDI_IMAGE;
4988 if (lpColumn->mask & LVCF_ORDER)
4989 hdi.mask |= HDI_ORDER;
4991 if (!SendMessageW(infoPtr->hwndHeader, isW ? HDM_GETITEMW : HDM_GETITEMA, nColumn, (LPARAM)&hdi)) return FALSE;
4993 if (lpColumn->mask & LVCF_FMT)
4994 lpColumn->fmt = lpColumnInfo->fmt;
4996 if (lpColumn->mask & LVCF_WIDTH)
4997 lpColumn->cx = lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left;
4999 if (lpColumn->mask & LVCF_IMAGE)
5000 lpColumn->iImage = hdi.iImage;
5002 if (lpColumn->mask & LVCF_ORDER)
5003 lpColumn->iOrder = hdi.iOrder;
5009 static BOOL LISTVIEW_GetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, LPINT lpiArray)
5016 /* FIXME: little hack */
5017 for (i = 0; i < iCount; i++)
5025 * Retrieves the column width.
5028 * [I] infoPtr : valid pointer to the listview structure
5029 * [I] int : column index
5032 * SUCCESS : column width
5035 static INT LISTVIEW_GetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn)
5037 INT nColumnWidth = 0;
5040 TRACE("nColumn=%d\n", nColumn);
5042 /* we have a 'column' in LIST and REPORT mode only */
5043 switch(infoPtr->dwStyle & LVS_TYPEMASK)
5046 nColumnWidth = infoPtr->nItemWidth;
5049 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
5050 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
5051 nColumnWidth = rcHeader.right - rcHeader.left;
5055 TRACE("nColumnWidth=%d\n", nColumnWidth);
5056 return nColumnWidth;
5061 * In list or report display mode, retrieves the number of items that can fit
5062 * vertically in the visible area. In icon or small icon display mode,
5063 * retrieves the total number of visible items.
5066 * [I] infoPtr : valid pointer to the listview structure
5069 * Number of fully visible items.
5071 static INT LISTVIEW_GetCountPerPage(LISTVIEW_INFO *infoPtr)
5073 switch (infoPtr->dwStyle & LVS_TYPEMASK)
5077 return infoPtr->nItemCount;
5079 return LISTVIEW_GetCountPerColumn(infoPtr);
5081 return LISTVIEW_GetCountPerRow(infoPtr) * LISTVIEW_GetCountPerColumn(infoPtr);
5089 * Retrieves an image list handle.
5092 * [I] infoPtr : valid pointer to the listview structure
5093 * [I] nImageList : image list identifier
5096 * SUCCESS : image list handle
5099 static HIMAGELIST LISTVIEW_GetImageList(LISTVIEW_INFO *infoPtr, INT nImageList)
5103 case LVSIL_NORMAL: return infoPtr->himlNormal;
5104 case LVSIL_SMALL: return infoPtr->himlSmall;
5105 case LVSIL_STATE: return infoPtr->himlState;
5110 /* LISTVIEW_GetISearchString */
5114 * Retrieves item attributes.
5117 * [I] hwnd : window handle
5118 * [IO] lpLVItem : item info
5119 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5120 * if FALSE, the lpLVItem is a LPLVITEMA.
5123 * This is the internal 'GetItem' interface -- it tries to
5124 * be smart and avoid text copies, if possible, by modifying
5125 * lpLVItem->pszText to point to the text string. Please note
5126 * that this is not always possible (e.g. OWNERDATA), so on
5127 * entry you *must* supply valid values for pszText, and cchTextMax.
5128 * The only difference to the documented interface is that upon
5129 * return, you should use *only* the lpLVItem->pszText, rather than
5130 * the buffer pointer you provided on input. Most code already does
5131 * that, so it's not a problem.
5132 * For the two cases when the text must be copied (that is,
5133 * for LVM_GETITEM, and LVM_GETITEMTEXT), use LISTVIEW_GetItemExtT.
5139 static BOOL LISTVIEW_GetItemT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5141 ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
5142 NMLVDISPINFOW dispInfo;
5148 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
5150 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5153 if (lpLVItem->mask == 0) return TRUE;
5155 /* make a local copy */
5156 isubitem = lpLVItem->iSubItem;
5158 /* a quick optimization if all we're asked is the focus state
5159 * these queries are worth optimising since they are common,
5160 * and can be answered in constant time, without the heavy accesses */
5161 if ( (lpLVItem->mask == LVIF_STATE) && (lpLVItem->stateMask == LVIS_FOCUSED) &&
5162 !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
5164 lpLVItem->state = 0;
5165 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5166 lpLVItem->state |= LVIS_FOCUSED;
5170 ZeroMemory(&dispInfo, sizeof(dispInfo));
5172 /* if the app stores all the data, handle it separately */
5173 if (infoPtr->dwStyle & LVS_OWNERDATA)
5175 dispInfo.item.state = 0;
5177 /* apprently, we should not callback for lParam in LVS_OWNERDATA */
5178 if ((lpLVItem->mask & ~(LVIF_STATE | LVIF_PARAM)) || infoPtr->uCallbackMask)
5180 /* NOTE: copy only fields which we _know_ are initialized, some apps
5181 * depend on the uninitialized fields being 0 */
5182 dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
5183 dispInfo.item.iItem = lpLVItem->iItem;
5184 dispInfo.item.iSubItem = isubitem;
5185 if (lpLVItem->mask & LVIF_TEXT)
5187 dispInfo.item.pszText = lpLVItem->pszText;
5188 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5190 if (lpLVItem->mask & LVIF_STATE)
5191 dispInfo.item.stateMask = lpLVItem->stateMask & infoPtr->uCallbackMask;
5192 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5193 dispInfo.item.stateMask = lpLVItem->stateMask;
5194 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
5196 /* full size structure expected - _WIN32IE >= 0x560 */
5197 *lpLVItem = dispInfo.item;
5199 else if (lpLVItem->mask & LVIF_INDENT)
5201 /* indent member expected - _WIN32IE >= 0x300 */
5202 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iGroupId ));
5206 /* minimal structure expected */
5207 memcpy(lpLVItem, &dispInfo.item, offsetof( LVITEMW, iIndent ));
5209 TRACE(" getdispinfo(1):lpLVItem=%s\n", debuglvitem_t(lpLVItem, isW));
5212 /* make sure lParam is zeroed out */
5213 if (lpLVItem->mask & LVIF_PARAM) lpLVItem->lParam = 0;
5215 /* we store only a little state, so if we're not asked, we're done */
5216 if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
5218 /* if focus is handled by us, report it */
5219 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5221 lpLVItem->state &= ~LVIS_FOCUSED;
5222 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5223 lpLVItem->state |= LVIS_FOCUSED;
5226 /* and do the same for selection, if we handle it */
5227 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5229 lpLVItem->state &= ~LVIS_SELECTED;
5230 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5231 lpLVItem->state |= LVIS_SELECTED;
5237 /* find the item and subitem structures before we proceed */
5238 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, lpLVItem->iItem);
5239 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
5244 SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
5245 pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
5248 WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
5253 pItemHdr = &lpItem->hdr;
5255 /* Do we need to query the state from the app? */
5256 if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
5258 dispInfo.item.mask |= LVIF_STATE;
5259 dispInfo.item.stateMask = infoPtr->uCallbackMask;
5262 /* Do we need to enquire about the image? */
5263 if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
5264 (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
5266 dispInfo.item.mask |= LVIF_IMAGE;
5267 dispInfo.item.iImage = I_IMAGECALLBACK;
5270 /* Apps depend on calling back for text if it is NULL or LPSTR_TEXTCALLBACKW */
5271 if ((lpLVItem->mask & LVIF_TEXT) && !is_textW(pItemHdr->pszText))
5273 dispInfo.item.mask |= LVIF_TEXT;
5274 dispInfo.item.pszText = lpLVItem->pszText;
5275 dispInfo.item.cchTextMax = lpLVItem->cchTextMax;
5276 if (dispInfo.item.pszText && dispInfo.item.cchTextMax > 0)
5277 *dispInfo.item.pszText = '\0';
5280 /* If we don't have all the requested info, query the application */
5281 if (dispInfo.item.mask != 0)
5283 dispInfo.item.iItem = lpLVItem->iItem;
5284 dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
5285 dispInfo.item.lParam = lpItem->lParam;
5286 notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
5287 TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
5290 /* we should not store values for subitems */
5291 if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
5293 /* Now, handle the iImage field */
5294 if (dispInfo.item.mask & LVIF_IMAGE)
5296 lpLVItem->iImage = dispInfo.item.iImage;
5297 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->iImage == I_IMAGECALLBACK)
5298 pItemHdr->iImage = dispInfo.item.iImage;
5300 else if (lpLVItem->mask & LVIF_IMAGE)
5302 if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
5303 lpLVItem->iImage = pItemHdr->iImage;
5305 lpLVItem->iImage = 0;
5308 /* The pszText field */
5309 if (dispInfo.item.mask & LVIF_TEXT)
5311 if ((dispInfo.item.mask & LVIF_DI_SETITEM) && pItemHdr->pszText)
5312 textsetptrT(&pItemHdr->pszText, dispInfo.item.pszText, isW);
5314 lpLVItem->pszText = dispInfo.item.pszText;
5316 else if (lpLVItem->mask & LVIF_TEXT)
5318 if (isW) lpLVItem->pszText = pItemHdr->pszText;
5319 else textcpynT(lpLVItem->pszText, isW, pItemHdr->pszText, TRUE, lpLVItem->cchTextMax);
5322 /* if this is a subitem, we're done */
5323 if (isubitem) return TRUE;
5325 /* Next is the lParam field */
5326 if (dispInfo.item.mask & LVIF_PARAM)
5328 lpLVItem->lParam = dispInfo.item.lParam;
5329 if ((dispInfo.item.mask & LVIF_DI_SETITEM))
5330 lpItem->lParam = dispInfo.item.lParam;
5332 else if (lpLVItem->mask & LVIF_PARAM)
5333 lpLVItem->lParam = lpItem->lParam;
5335 /* ... the state field (this one is different due to uCallbackmask) */
5336 if (lpLVItem->mask & LVIF_STATE)
5338 lpLVItem->state = lpItem->state;
5339 if (dispInfo.item.mask & LVIF_STATE)
5341 lpLVItem->state &= ~dispInfo.item.stateMask;
5342 lpLVItem->state |= (dispInfo.item.state & dispInfo.item.stateMask);
5344 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
5346 lpLVItem->state &= ~LVIS_FOCUSED;
5347 if (infoPtr->nFocusedItem == lpLVItem->iItem)
5348 lpLVItem->state |= LVIS_FOCUSED;
5350 if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_SELECTED )
5352 lpLVItem->state &= ~LVIS_SELECTED;
5353 if (ranges_contain(infoPtr->selectionRanges, lpLVItem->iItem))
5354 lpLVItem->state |= LVIS_SELECTED;
5358 /* and last, but not least, the indent field */
5359 if (lpLVItem->mask & LVIF_INDENT)
5360 lpLVItem->iIndent = lpItem->iIndent;
5367 * Retrieves item attributes.
5370 * [I] hwnd : window handle
5371 * [IO] lpLVItem : item info
5372 * [I] isW : if TRUE, then lpLVItem is a LPLVITEMW,
5373 * if FALSE, the lpLVItem is a LPLVITEMA.
5376 * This is the external 'GetItem' interface -- it properly copies
5377 * the text in the provided buffer.
5383 static BOOL LISTVIEW_GetItemExtT(LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
5388 if (!lpLVItem || lpLVItem->iItem < 0 || lpLVItem->iItem >= infoPtr->nItemCount)
5391 pszText = lpLVItem->pszText;
5392 bResult = LISTVIEW_GetItemT(infoPtr, lpLVItem, isW);
5393 if (bResult && lpLVItem->pszText != pszText)
5394 textcpynT(pszText, isW, lpLVItem->pszText, isW, lpLVItem->cchTextMax);
5395 lpLVItem->pszText = pszText;
5403 * Retrieves the position (upper-left) of the listview control item.
5404 * Note that for LVS_ICON style, the upper-left is that of the icon
5405 * and not the bounding box.
5408 * [I] infoPtr : valid pointer to the listview structure
5409 * [I] nItem : item index
5410 * [O] lpptPosition : coordinate information
5416 static BOOL LISTVIEW_GetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, LPPOINT lpptPosition)
5418 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5421 TRACE("(nItem=%d, lpptPosition=%p)\n", nItem, lpptPosition);
5423 if (!lpptPosition || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5425 LISTVIEW_GetOrigin(infoPtr, &Origin);
5426 LISTVIEW_GetItemOrigin(infoPtr, nItem, lpptPosition);
5428 if (uView == LVS_ICON)
5430 lpptPosition->x += (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
5431 lpptPosition->y += ICON_TOP_PADDING;
5433 lpptPosition->x += Origin.x;
5434 lpptPosition->y += Origin.y;
5436 TRACE (" lpptPosition=%s\n", wine_dbgstr_point(lpptPosition));
5443 * Retrieves the bounding rectangle for a listview control item.
5446 * [I] infoPtr : valid pointer to the listview structure
5447 * [I] nItem : item index
5448 * [IO] lprc : bounding rectangle coordinates
5449 * lprc->left specifies the portion of the item for which the bounding
5450 * rectangle will be retrieved.
5452 * LVIR_BOUNDS Returns the bounding rectangle of the entire item,
5453 * including the icon and label.
5456 * * Experiment shows that native control returns:
5457 * * width = min (48, length of text line)
5458 * * .left = position.x - (width - iconsize.cx)/2
5459 * * .right = .left + width
5460 * * height = #lines of text * ntmHeight + icon height + 8
5461 * * .top = position.y - 2
5462 * * .bottom = .top + height
5463 * * separation between items .y = itemSpacing.cy - height
5464 * * .x = itemSpacing.cx - width
5465 * LVIR_ICON Returns the bounding rectangle of the icon or small icon.
5468 * * Experiment shows that native control returns:
5469 * * width = iconSize.cx + 16
5470 * * .left = position.x - (width - iconsize.cx)/2
5471 * * .right = .left + width
5472 * * height = iconSize.cy + 4
5473 * * .top = position.y - 2
5474 * * .bottom = .top + height
5475 * * separation between items .y = itemSpacing.cy - height
5476 * * .x = itemSpacing.cx - width
5477 * LVIR_LABEL Returns the bounding rectangle of the item text.
5480 * * Experiment shows that native control returns:
5481 * * width = text length
5482 * * .left = position.x - width/2
5483 * * .right = .left + width
5484 * * height = ntmH * linecount + 2
5485 * * .top = position.y + iconSize.cy + 6
5486 * * .bottom = .top + height
5487 * * separation between items .y = itemSpacing.cy - height
5488 * * .x = itemSpacing.cx - width
5489 * LVIR_SELECTBOUNDS Returns the union of the LVIR_ICON and LVIR_LABEL
5490 * rectangles, but excludes columns in report view.
5497 * Note that the bounding rectangle of the label in the LVS_ICON view depends
5498 * upon whether the window has the focus currently and on whether the item
5499 * is the one with the focus. Ensure that the control's record of which
5500 * item has the focus agrees with the items' records.
5502 static BOOL LISTVIEW_GetItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5504 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5505 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5506 BOOL doLabel = TRUE, oversizedBox = FALSE;
5507 POINT Position, Origin;
5511 TRACE("(hwnd=%p, nItem=%d, lprc=%p)\n", infoPtr->hwndSelf, nItem, lprc);
5513 if (!lprc || nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
5515 LISTVIEW_GetOrigin(infoPtr, &Origin);
5516 LISTVIEW_GetItemOrigin(infoPtr, nItem, &Position);
5518 /* Be smart and try to figure out the minimum we have to do */
5519 if (lprc->left == LVIR_ICON) doLabel = FALSE;
5520 if (uView == LVS_REPORT && lprc->left == LVIR_BOUNDS) doLabel = FALSE;
5521 if (uView == LVS_ICON && lprc->left != LVIR_ICON &&
5522 infoPtr->bFocus && LISTVIEW_GetItemState(infoPtr, nItem, LVIS_FOCUSED))
5523 oversizedBox = TRUE;
5525 /* get what we need from the item before hand, so we make
5526 * only one request. This can speed up things, if data
5527 * is stored on the app side */
5529 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
5530 if (doLabel) lvItem.mask |= LVIF_TEXT;
5531 lvItem.iItem = nItem;
5532 lvItem.iSubItem = 0;
5533 lvItem.pszText = szDispText;
5534 lvItem.cchTextMax = DISP_TEXT_SIZE;
5535 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5536 /* we got the state already up, simulate it here, to avoid a reget */
5537 if (uView == LVS_ICON && (lprc->left != LVIR_ICON))
5539 lvItem.mask |= LVIF_STATE;
5540 lvItem.stateMask = LVIS_FOCUSED;
5541 lvItem.state = (oversizedBox ? LVIS_FOCUSED : 0);
5544 if (uView == LVS_REPORT && (infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) && lprc->left == LVIR_SELECTBOUNDS)
5545 lprc->left = LVIR_BOUNDS;
5549 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5553 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, NULL, lprc);
5557 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5560 case LVIR_SELECTBOUNDS:
5561 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, &label_rect);
5562 UnionRect(lprc, lprc, &label_rect);
5566 WARN("Unknown value: %ld\n", lprc->left);
5570 OffsetRect(lprc, Position.x + Origin.x, Position.y + Origin.y);
5572 TRACE(" rect=%s\n", wine_dbgstr_rect(lprc));
5579 * Retrieves the spacing between listview control items.
5582 * [I] infoPtr : valid pointer to the listview structure
5583 * [IO] lprc : rectangle to receive the output
5584 * on input, lprc->top = nSubItem
5585 * lprc->left = LVIR_ICON | LVIR_BOUNDS | LVIR_LABEL
5587 * NOTE: for subItem = 0, we should return the bounds of the _entire_ item,
5588 * not only those of the first column.
5589 * Fortunately, LISTVIEW_GetItemMetrics does the right thing.
5595 static BOOL LISTVIEW_GetSubItemRect(LISTVIEW_INFO *infoPtr, INT nItem, LPRECT lprc)
5599 INT nColumn = lprc->top;
5601 if (!lprc) return FALSE;
5603 TRACE("(nItem=%d, nSubItem=%ld)\n", nItem, lprc->top);
5604 /* On WinNT, a subitem of '0' calls LISTVIEW_GetItemRect */
5606 return LISTVIEW_GetItemRect(infoPtr, nItem, lprc);
5608 if ((infoPtr->dwStyle & LVS_TYPEMASK) != LVS_REPORT) return FALSE;
5610 if (!LISTVIEW_GetItemPosition(infoPtr, nItem, &Position)) return FALSE;
5612 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
5615 lvItem.iItem = nItem;
5616 lvItem.iSubItem = nColumn;
5618 if (lvItem.mask && !LISTVIEW_GetItemW(infoPtr, &lvItem)) return FALSE;
5622 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, NULL, NULL, lprc, NULL);
5627 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, lprc, NULL, NULL, NULL);
5631 ERR("Unknown bounds=%ld\n", lprc->left);
5635 OffsetRect(lprc, Position.x, Position.y);
5642 * Retrieves the width of a label.
5645 * [I] infoPtr : valid pointer to the listview structure
5648 * SUCCESS : string width (in pixels)
5651 static INT LISTVIEW_GetLabelWidth(LISTVIEW_INFO *infoPtr, INT nItem)
5653 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
5656 TRACE("(nItem=%d)\n", nItem);
5658 lvItem.mask = LVIF_TEXT;
5659 lvItem.iItem = nItem;
5660 lvItem.iSubItem = 0;
5661 lvItem.pszText = szDispText;
5662 lvItem.cchTextMax = DISP_TEXT_SIZE;
5663 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5665 return LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
5670 * Retrieves the spacing between listview control items.
5673 * [I] infoPtr : valid pointer to the listview structure
5674 * [I] bSmall : flag for small or large icon
5677 * Horizontal + vertical spacing
5679 static LONG LISTVIEW_GetItemSpacing(LISTVIEW_INFO *infoPtr, BOOL bSmall)
5685 lResult = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
5689 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_ICON)
5690 lResult = MAKELONG(DEFAULT_COLUMN_WIDTH, GetSystemMetrics(SM_CXSMICON)+HEIGHT_PADDING);
5692 lResult = MAKELONG(infoPtr->nItemWidth, infoPtr->nItemHeight);
5699 * Retrieves the state of a listview control item.
5702 * [I] infoPtr : valid pointer to the listview structure
5703 * [I] nItem : item index
5704 * [I] uMask : state mask
5707 * State specified by the mask.
5709 static UINT LISTVIEW_GetItemState(LISTVIEW_INFO *infoPtr, INT nItem, UINT uMask)
5713 if (nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5715 lvItem.iItem = nItem;
5716 lvItem.iSubItem = 0;
5717 lvItem.mask = LVIF_STATE;
5718 lvItem.stateMask = uMask;
5719 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return 0;
5721 return lvItem.state & uMask;
5726 * Retrieves the text of a listview control item or subitem.
5729 * [I] hwnd : window handle
5730 * [I] nItem : item index
5731 * [IO] lpLVItem : item information
5732 * [I] isW : TRUE if lpLVItem is Unicode
5735 * SUCCESS : string length
5738 static INT LISTVIEW_GetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, LPLVITEMW lpLVItem, BOOL isW)
5740 if (!lpLVItem || nItem < 0 || nItem >= infoPtr->nItemCount) return 0;
5742 lpLVItem->mask = LVIF_TEXT;
5743 lpLVItem->iItem = nItem;
5744 if (!LISTVIEW_GetItemExtT(infoPtr, lpLVItem, isW)) return 0;
5746 return textlenT(lpLVItem->pszText, isW);
5751 * Searches for an item based on properties + relationships.
5754 * [I] infoPtr : valid pointer to the listview structure
5755 * [I] nItem : item index
5756 * [I] uFlags : relationship flag
5759 * SUCCESS : item index
5762 static INT LISTVIEW_GetNextItem(LISTVIEW_INFO *infoPtr, INT nItem, UINT uFlags)
5764 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5766 LVFINDINFOW lvFindInfo;
5767 INT nCountPerColumn;
5771 TRACE("nItem=%d, uFlags=%x, nItemCount=%d\n", nItem, uFlags, infoPtr->nItemCount);
5772 if (nItem < -1 || nItem >= infoPtr->nItemCount) return -1;
5774 ZeroMemory(&lvFindInfo, sizeof(lvFindInfo));
5776 if (uFlags & LVNI_CUT)
5779 if (uFlags & LVNI_DROPHILITED)
5780 uMask |= LVIS_DROPHILITED;
5782 if (uFlags & LVNI_FOCUSED)
5783 uMask |= LVIS_FOCUSED;
5785 if (uFlags & LVNI_SELECTED)
5786 uMask |= LVIS_SELECTED;
5788 /* if we're asked for the focused item, that's only one,
5789 * so it's worth optimizing */
5790 if (uFlags & LVNI_FOCUSED)
5792 if ((LISTVIEW_GetItemState(infoPtr, infoPtr->nFocusedItem, uMask) & uMask) != uMask) return -1;
5793 return (infoPtr->nFocusedItem == nItem) ? -1 : infoPtr->nFocusedItem;
5796 if (uFlags & LVNI_ABOVE)
5798 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5803 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5809 /* Special case for autoarrange - move 'til the top of a list */
5810 if (is_autoarrange(infoPtr))
5812 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5813 while (nItem - nCountPerRow >= 0)
5815 nItem -= nCountPerRow;
5816 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5821 lvFindInfo.flags = LVFI_NEARESTXY;
5822 lvFindInfo.vkDirection = VK_UP;
5823 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5824 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5826 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5831 else if (uFlags & LVNI_BELOW)
5833 if ((uView == LVS_LIST) || (uView == LVS_REPORT))
5835 while (nItem < infoPtr->nItemCount)
5838 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5844 /* Special case for autoarrange - move 'til the bottom of a list */
5845 if (is_autoarrange(infoPtr))
5847 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5848 while (nItem + nCountPerRow < infoPtr->nItemCount )
5850 nItem += nCountPerRow;
5851 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5856 lvFindInfo.flags = LVFI_NEARESTXY;
5857 lvFindInfo.vkDirection = VK_DOWN;
5858 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5859 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5861 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5866 else if (uFlags & LVNI_TOLEFT)
5868 if (uView == LVS_LIST)
5870 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5871 while (nItem - nCountPerColumn >= 0)
5873 nItem -= nCountPerColumn;
5874 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5878 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5880 /* Special case for autoarrange - move 'ti the beginning of a row */
5881 if (is_autoarrange(infoPtr))
5883 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5884 while (nItem % nCountPerRow > 0)
5887 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5892 lvFindInfo.flags = LVFI_NEARESTXY;
5893 lvFindInfo.vkDirection = VK_LEFT;
5894 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5895 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5897 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5902 else if (uFlags & LVNI_TORIGHT)
5904 if (uView == LVS_LIST)
5906 nCountPerColumn = LISTVIEW_GetCountPerColumn(infoPtr);
5907 while (nItem + nCountPerColumn < infoPtr->nItemCount)
5909 nItem += nCountPerColumn;
5910 if ((ListView_GetItemState(infoPtr->hwndSelf, nItem, uMask) & uMask) == uMask)
5914 else if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
5916 /* Special case for autoarrange - move 'til the end of a row */
5917 if (is_autoarrange(infoPtr))
5919 nCountPerRow = LISTVIEW_GetCountPerRow(infoPtr);
5920 while (nItem % nCountPerRow < nCountPerRow - 1 )
5923 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5928 lvFindInfo.flags = LVFI_NEARESTXY;
5929 lvFindInfo.vkDirection = VK_RIGHT;
5930 SendMessageW( infoPtr->hwndSelf, LVM_GETITEMPOSITION, nItem, (LPARAM)&lvFindInfo.pt );
5931 while ((nItem = ListView_FindItemW(infoPtr->hwndSelf, nItem, &lvFindInfo)) != -1)
5933 if ((LISTVIEW_GetItemState(infoPtr, nItem, uMask) & uMask) == uMask)
5942 /* search by index */
5943 for (i = nItem; i < infoPtr->nItemCount; i++)
5945 if ((LISTVIEW_GetItemState(infoPtr, i, uMask) & uMask) == uMask)
5953 /* LISTVIEW_GetNumberOfWorkAreas */
5957 * Retrieves the origin coordinates when in icon or small icon display mode.
5960 * [I] infoPtr : valid pointer to the listview structure
5961 * [O] lpptOrigin : coordinate information
5966 static void LISTVIEW_GetOrigin(LISTVIEW_INFO *infoPtr, LPPOINT lpptOrigin)
5968 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
5969 INT nHorzPos = 0, nVertPos = 0;
5970 SCROLLINFO scrollInfo;
5972 scrollInfo.cbSize = sizeof(SCROLLINFO);
5973 scrollInfo.fMask = SIF_POS;
5975 if (GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo))
5976 nHorzPos = scrollInfo.nPos;
5977 if (GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo))
5978 nVertPos = scrollInfo.nPos;
5980 TRACE("nHorzPos=%d, nVertPos=%d\n", nHorzPos, nVertPos);
5982 lpptOrigin->x = infoPtr->rcList.left;
5983 lpptOrigin->y = infoPtr->rcList.top;
5984 if (uView == LVS_LIST)
5985 nHorzPos *= infoPtr->nItemWidth;
5986 else if (uView == LVS_REPORT)
5987 nVertPos *= infoPtr->nItemHeight;
5989 lpptOrigin->x -= nHorzPos;
5990 lpptOrigin->y -= nVertPos;
5992 TRACE(" origin=%s\n", wine_dbgstr_point(lpptOrigin));
5997 * Retrieves the width of a string.
6000 * [I] hwnd : window handle
6001 * [I] lpszText : text string to process
6002 * [I] isW : TRUE if lpszText is Unicode, FALSE otherwise
6005 * SUCCESS : string width (in pixels)
6008 static INT LISTVIEW_GetStringWidthT(LISTVIEW_INFO *infoPtr, LPCWSTR lpszText, BOOL isW)
6013 if (is_textT(lpszText, isW))
6015 HFONT hFont = infoPtr->hFont ? infoPtr->hFont : infoPtr->hDefaultFont;
6016 HDC hdc = GetDC(infoPtr->hwndSelf);
6017 HFONT hOldFont = SelectObject(hdc, hFont);
6020 GetTextExtentPointW(hdc, lpszText, lstrlenW(lpszText), &stringSize);
6022 GetTextExtentPointA(hdc, (LPCSTR)lpszText, lstrlenA((LPCSTR)lpszText), &stringSize);
6023 SelectObject(hdc, hOldFont);
6024 ReleaseDC(infoPtr->hwndSelf, hdc);
6026 return stringSize.cx;
6031 * Determines which listview item is located at the specified position.
6034 * [I] infoPtr : valid pointer to the listview structure
6035 * [IO] lpht : hit test information
6036 * [I] subitem : fill out iSubItem.
6037 * [I] select : return the index only if the hit selects the item
6040 * (mm 20001022): We must not allow iSubItem to be touched, for
6041 * an app might pass only a structure with space up to iItem!
6042 * (MS Office 97 does that for instance in the file open dialog)
6045 * SUCCESS : item index
6048 static INT LISTVIEW_HitTest(LISTVIEW_INFO *infoPtr, LPLVHITTESTINFO lpht, BOOL subitem, BOOL select)
6050 WCHAR szDispText[DISP_TEXT_SIZE] = { '\0' };
6051 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6052 RECT rcBox, rcBounds, rcState, rcIcon, rcLabel, rcSearch;
6053 POINT Origin, Position, opt;
6058 TRACE("(pt=%s, subitem=%d, select=%d)\n", wine_dbgstr_point(&lpht->pt), subitem, select);
6062 if (subitem) lpht->iSubItem = 0;
6064 if (infoPtr->rcList.left > lpht->pt.x)
6065 lpht->flags |= LVHT_TOLEFT;
6066 else if (infoPtr->rcList.right < lpht->pt.x)
6067 lpht->flags |= LVHT_TORIGHT;
6069 if (infoPtr->rcList.top > lpht->pt.y)
6070 lpht->flags |= LVHT_ABOVE;
6071 else if (infoPtr->rcList.bottom < lpht->pt.y)
6072 lpht->flags |= LVHT_BELOW;
6074 TRACE("lpht->flags=0x%x\n", lpht->flags);
6075 if (lpht->flags) return -1;
6077 lpht->flags |= LVHT_NOWHERE;
6079 LISTVIEW_GetOrigin(infoPtr, &Origin);
6081 /* first deal with the large items */
6082 rcSearch.left = lpht->pt.x;
6083 rcSearch.top = lpht->pt.y;
6084 rcSearch.right = rcSearch.left + 1;
6085 rcSearch.bottom = rcSearch.top + 1;
6087 iterator_frameditems(&i, infoPtr, &rcSearch);
6088 iterator_next(&i); /* go to first item in the sequence */
6090 iterator_destroy(&i);
6092 TRACE("lpht->iItem=%d\n", iItem);
6093 if (iItem == -1) return -1;
6095 lvItem.mask = LVIF_STATE | LVIF_TEXT;
6096 if (uView == LVS_REPORT) lvItem.mask |= LVIF_INDENT;
6097 lvItem.stateMask = LVIS_STATEIMAGEMASK;
6098 if (uView == LVS_ICON) lvItem.stateMask |= LVIS_FOCUSED;
6099 lvItem.iItem = iItem;
6100 lvItem.iSubItem = 0;
6101 lvItem.pszText = szDispText;
6102 lvItem.cchTextMax = DISP_TEXT_SIZE;
6103 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) return -1;
6104 if (!infoPtr->bFocus) lvItem.state &= ~LVIS_FOCUSED;
6106 LISTVIEW_GetItemMetrics(infoPtr, &lvItem, &rcBox, &rcState, &rcIcon, &rcLabel);
6107 LISTVIEW_GetItemOrigin(infoPtr, iItem, &Position);
6108 opt.x = lpht->pt.x - Position.x - Origin.x;
6109 opt.y = lpht->pt.y - Position.y - Origin.y;
6111 if (uView == LVS_REPORT)
6114 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6115 TRACE("rcBounds=%s\n", wine_dbgstr_rect(&rcBounds));
6116 if (!PtInRect(&rcBounds, opt)) return -1;
6118 if (PtInRect(&rcIcon, opt))
6119 lpht->flags |= LVHT_ONITEMICON;
6120 else if (PtInRect(&rcLabel, opt))
6121 lpht->flags |= LVHT_ONITEMLABEL;
6122 else if (infoPtr->himlState && STATEIMAGEINDEX(lvItem.state) && PtInRect(&rcState, opt))
6123 lpht->flags |= LVHT_ONITEMSTATEICON;
6124 if (lpht->flags & LVHT_ONITEM)
6125 lpht->flags &= ~LVHT_NOWHERE;
6127 TRACE("lpht->flags=0x%x\n", lpht->flags);
6128 if (uView == LVS_REPORT && subitem)
6132 rcBounds.right = rcBounds.left;
6133 for (j = 0; j < DPA_GetPtrCount(infoPtr->hdpaColumns); j++)
6135 rcBounds.left = rcBounds.right;
6136 rcBounds.right += LISTVIEW_GetColumnWidth(infoPtr, j);
6137 if (PtInRect(&rcBounds, opt))
6145 if (select && !(uView == LVS_REPORT &&
6146 ((infoPtr->dwLvExStyle & LVS_EX_FULLROWSELECT) ||
6147 (infoPtr->dwStyle & LVS_OWNERDRAWFIXED))))
6149 if (uView == LVS_REPORT)
6151 UnionRect(&rcBounds, &rcIcon, &rcLabel);
6152 UnionRect(&rcBounds, &rcBounds, &rcState);
6154 if (!PtInRect(&rcBounds, opt)) iItem = -1;
6156 return lpht->iItem = iItem;
6160 /* LISTVIEW_InsertCompare: callback routine for comparing pszText members of the LV_ITEMS
6161 in a LISTVIEW on insert. Passed to DPA_Sort in LISTVIEW_InsertItem.
6162 This function should only be used for inserting items into a sorted list (LVM_INSERTITEM)
6163 and not during the processing of a LVM_SORTITEMS message. Applications should provide
6164 their own sort proc. when sending LVM_SORTITEMS.
6167 (remarks on LVITEM: LVM_INSERTITEM will insert the new item in the proper sort postion...
6169 LVS_SORTXXX must be specified,
6170 LVS_OWNERDRAW is not set,
6171 <item>.pszText is not LPSTR_TEXTCALLBACK.
6173 (LVS_SORT* flags): "For the LVS_SORTASCENDING... styles, item indices
6174 are sorted based on item text..."
6176 static INT WINAPI LISTVIEW_InsertCompare( LPVOID first, LPVOID second, LPARAM lParam)
6178 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
6179 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
6180 INT cmpv = textcmpWT(lv_first->hdr.pszText, lv_second->hdr.pszText, TRUE);
6182 /* if we're sorting descending, negate the return value */
6183 return (((LISTVIEW_INFO *)lParam)->dwStyle & LVS_SORTDESCENDING) ? -cmpv : cmpv;
6188 * Inserts a new item in the listview control.
6191 * [I] infoPtr : valid pointer to the listview structure
6192 * [I] lpLVItem : item information
6193 * [I] isW : TRUE if lpLVItem is Unicode, FALSE if it's ANSI
6196 * SUCCESS : new item index
6199 static INT LISTVIEW_InsertItemT(LISTVIEW_INFO *infoPtr, const LVITEMW *lpLVItem, BOOL isW)
6201 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6206 BOOL is_sorted, has_changed;
6208 HWND hwndSelf = infoPtr->hwndSelf;
6210 TRACE("(lpLVItem=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
6212 if (infoPtr->dwStyle & LVS_OWNERDATA) return infoPtr->nItemCount++;
6214 /* make sure it's an item, and not a subitem; cannot insert a subitem */
6215 if (!lpLVItem || lpLVItem->iSubItem) return -1;
6217 if (!is_assignable_item(lpLVItem, infoPtr->dwStyle)) return -1;
6219 if (!(lpItem = (ITEM_INFO *)Alloc(sizeof(ITEM_INFO)))) return -1;
6221 /* insert item in listview control data structure */
6222 if ( !(hdpaSubItems = DPA_Create(8)) ) goto fail;
6223 if ( !DPA_SetPtr(hdpaSubItems, 0, lpItem) ) assert (FALSE);
6225 is_sorted = (infoPtr->dwStyle & (LVS_SORTASCENDING | LVS_SORTDESCENDING)) &&
6226 !(infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (LPSTR_TEXTCALLBACKW != lpLVItem->pszText);
6228 if (lpLVItem->iItem < 0 && !is_sorted) return -1;
6230 nItem = is_sorted ? infoPtr->nItemCount : min(lpLVItem->iItem, infoPtr->nItemCount);
6231 TRACE(" inserting at %d, sorted=%d, count=%d, iItem=%d\n", nItem, is_sorted, infoPtr->nItemCount, lpLVItem->iItem);
6232 nItem = DPA_InsertPtr( infoPtr->hdpaItems, nItem, hdpaSubItems );
6233 if (nItem == -1) goto fail;
6234 infoPtr->nItemCount++;
6236 /* shift indices first so they don't get tangled */
6237 LISTVIEW_ShiftIndices(infoPtr, nItem, 1);
6239 /* set the item attributes */
6240 if (lpLVItem->mask & (LVIF_GROUPID|LVIF_COLUMNS))
6242 /* full size structure expected - _WIN32IE >= 0x560 */
6245 else if (lpLVItem->mask & LVIF_INDENT)
6247 /* indent member expected - _WIN32IE >= 0x300 */
6248 memcpy(&item, lpLVItem, offsetof( LVITEMW, iGroupId ));
6252 /* minimal structure expected */
6253 memcpy(&item, lpLVItem, offsetof( LVITEMW, iIndent ));
6256 if (infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) item.state &= ~LVIS_STATEIMAGEMASK;
6257 if (!set_main_item(infoPtr, &item, TRUE, isW, &has_changed)) goto undo;
6259 /* if we're sorted, sort the list, and update the index */
6262 DPA_Sort( infoPtr->hdpaItems, LISTVIEW_InsertCompare, (LPARAM)infoPtr );
6263 nItem = DPA_GetPtrIndex( infoPtr->hdpaItems, hdpaSubItems );
6264 assert(nItem != -1);
6267 /* make room for the position, if we are in the right mode */
6268 if ((uView == LVS_SMALLICON) || (uView == LVS_ICON))
6270 if (DPA_InsertPtr(infoPtr->hdpaPosX, nItem, 0) == -1)
6272 if (DPA_InsertPtr(infoPtr->hdpaPosY, nItem, 0) == -1)
6274 DPA_DeletePtr(infoPtr->hdpaPosX, nItem);
6279 /* send LVN_INSERTITEM notification */
6280 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
6282 nmlv.lParam = lpItem->lParam;
6283 notify_listview(infoPtr, LVN_INSERTITEM, &nmlv);
6284 if (!IsWindow(hwndSelf))
6287 /* align items (set position of each item) */
6288 if ((uView == LVS_SMALLICON || uView == LVS_ICON))
6292 if (infoPtr->dwStyle & LVS_ALIGNLEFT)
6293 LISTVIEW_NextIconPosLeft(infoPtr, &pt);
6295 LISTVIEW_NextIconPosTop(infoPtr, &pt);
6297 LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, TRUE);
6300 /* now is the invalidation fun */
6301 LISTVIEW_ScrollOnInsert(infoPtr, nItem, 1);
6305 LISTVIEW_ShiftIndices(infoPtr, nItem, -1);
6306 DPA_DeletePtr(infoPtr->hdpaItems, nItem);
6307 infoPtr->nItemCount--;
6309 DPA_DeletePtr(hdpaSubItems, 0);
6310 DPA_Destroy (hdpaSubItems);
6317 * Redraws a range of items.
6320 * [I] infoPtr : valid pointer to the listview structure
6321 * [I] nFirst : first item
6322 * [I] nLast : last item
6328 static BOOL LISTVIEW_RedrawItems(LISTVIEW_INFO *infoPtr, INT nFirst, INT nLast)
6332 if (nLast < nFirst || min(nFirst, nLast) < 0 ||
6333 max(nFirst, nLast) >= infoPtr->nItemCount)
6336 for (i = nFirst; i <= nLast; i++)
6337 LISTVIEW_InvalidateItem(infoPtr, i);
6344 * Scroll the content of a listview.
6347 * [I] infoPtr : valid pointer to the listview structure
6348 * [I] dx : horizontal scroll amount in pixels
6349 * [I] dy : vertical scroll amount in pixels
6356 * If the control is in report mode (LVS_REPORT) the control can
6357 * be scrolled only in line increments. "dy" will be rounded to the
6358 * nearest number of pixels that are a whole line. Ex: if line height
6359 * is 16 and an 8 is passed, the list will be scrolled by 16. If a 7
6360 * is passed the the scroll will be 0. (per MSDN 7/2002)
6362 * For: (per experimentaion with native control and CSpy ListView)
6363 * LVS_ICON dy=1 = 1 pixel (vertical only)
6365 * LVS_SMALLICON dy=1 = 1 pixel (vertical only)
6367 * LVS_LIST dx=1 = 1 column (horizontal only)
6368 * but will only scroll 1 column per message
6369 * no matter what the value.
6370 * dy must be 0 or FALSE returned.
6371 * LVS_REPORT dx=1 = 1 pixel
6375 static BOOL LISTVIEW_Scroll(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
6377 switch(infoPtr->dwStyle & LVS_TYPEMASK) {
6379 dy += (dy < 0 ? -1 : 1) * infoPtr->nItemHeight/2;
6380 dy /= infoPtr->nItemHeight;
6383 if (dy != 0) return FALSE;
6390 if (dx != 0) LISTVIEW_HScroll(infoPtr, SB_INTERNAL, dx, 0);
6391 if (dy != 0) LISTVIEW_VScroll(infoPtr, SB_INTERNAL, dy, 0);
6398 * Sets the background color.
6401 * [I] infoPtr : valid pointer to the listview structure
6402 * [I] clrBk : background color
6408 static BOOL LISTVIEW_SetBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrBk)
6410 TRACE("(clrBk=%lx)\n", clrBk);
6412 if(infoPtr->clrBk != clrBk) {
6413 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
6414 infoPtr->clrBk = clrBk;
6415 if (clrBk == CLR_NONE)
6416 infoPtr->hBkBrush = (HBRUSH)GetClassLongPtrW(infoPtr->hwndSelf, GCLP_HBRBACKGROUND);
6418 infoPtr->hBkBrush = CreateSolidBrush(clrBk);
6419 LISTVIEW_InvalidateList(infoPtr);
6425 /* LISTVIEW_SetBkImage */
6427 /*** Helper for {Insert,Set}ColumnT *only* */
6428 static void column_fill_hditem(LISTVIEW_INFO *infoPtr, HDITEMW *lphdi, INT nColumn, const LVCOLUMNW *lpColumn, BOOL isW)
6430 if (lpColumn->mask & LVCF_FMT)
6432 /* format member is valid */
6433 lphdi->mask |= HDI_FORMAT;
6435 /* set text alignment (leftmost column must be left-aligned) */
6436 if (nColumn == 0 || (lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
6437 lphdi->fmt |= HDF_LEFT;
6438 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
6439 lphdi->fmt |= HDF_RIGHT;
6440 else if ((lpColumn->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_CENTER)
6441 lphdi->fmt |= HDF_CENTER;
6443 if (lpColumn->fmt & LVCFMT_BITMAP_ON_RIGHT)
6444 lphdi->fmt |= HDF_BITMAP_ON_RIGHT;
6446 if (lpColumn->fmt & LVCFMT_COL_HAS_IMAGES)
6448 lphdi->fmt |= HDF_IMAGE;
6449 lphdi->iImage = I_IMAGECALLBACK;
6453 if (lpColumn->mask & LVCF_WIDTH)
6455 lphdi->mask |= HDI_WIDTH;
6456 if(lpColumn->cx == LVSCW_AUTOSIZE_USEHEADER)
6458 /* make it fill the remainder of the controls width */
6462 for(item_index = 0; item_index < (nColumn - 1); item_index++)
6464 LISTVIEW_GetHeaderRect(infoPtr, item_index, &rcHeader);
6465 lphdi->cxy += rcHeader.right - rcHeader.left;
6468 /* retrieve the layout of the header */
6469 GetClientRect(infoPtr->hwndSelf, &rcHeader);
6470 TRACE("start cxy=%d rcHeader=%s\n", lphdi->cxy, wine_dbgstr_rect(&rcHeader));
6472 lphdi->cxy = (rcHeader.right - rcHeader.left) - lphdi->cxy;
6475 lphdi->cxy = lpColumn->cx;
6478 if (lpColumn->mask & LVCF_TEXT)
6480 lphdi->mask |= HDI_TEXT | HDI_FORMAT;
6481 lphdi->fmt |= HDF_STRING;
6482 lphdi->pszText = lpColumn->pszText;
6483 lphdi->cchTextMax = textlenT(lpColumn->pszText, isW);
6486 if (lpColumn->mask & LVCF_IMAGE)
6488 lphdi->mask |= HDI_IMAGE;
6489 lphdi->iImage = lpColumn->iImage;
6492 if (lpColumn->mask & LVCF_ORDER)
6494 lphdi->mask |= HDI_ORDER;
6495 lphdi->iOrder = lpColumn->iOrder;
6502 * Inserts a new column.
6505 * [I] infoPtr : valid pointer to the listview structure
6506 * [I] nColumn : column index
6507 * [I] lpColumn : column information
6508 * [I] isW : TRUE if lpColumn is Unicode, FALSE otherwise
6511 * SUCCESS : new column index
6514 static INT LISTVIEW_InsertColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6515 const LVCOLUMNW *lpColumn, BOOL isW)
6517 COLUMN_INFO *lpColumnInfo;
6521 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6523 if (!lpColumn || nColumn < 0) return -1;
6524 nColumn = min(nColumn, DPA_GetPtrCount(infoPtr->hdpaColumns));
6526 ZeroMemory(&hdi, sizeof(HDITEMW));
6527 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6529 /* insert item in header control */
6530 nNewColumn = SendMessageW(infoPtr->hwndHeader,
6531 isW ? HDM_INSERTITEMW : HDM_INSERTITEMA,
6532 (WPARAM)nColumn, (LPARAM)&hdi);
6533 if (nNewColumn == -1) return -1;
6534 if (nNewColumn != nColumn) ERR("nColumn=%d, nNewColumn=%d\n", nColumn, nNewColumn);
6536 /* create our own column info */
6537 if (!(lpColumnInfo = Alloc(sizeof(COLUMN_INFO)))) goto fail;
6538 if (DPA_InsertPtr(infoPtr->hdpaColumns, nNewColumn, lpColumnInfo) == -1) goto fail;
6540 if (lpColumn->mask & LVCF_FMT) lpColumnInfo->fmt = lpColumn->fmt;
6541 if (!Header_GetItemRect(infoPtr->hwndHeader, nNewColumn, &lpColumnInfo->rcHeader)) goto fail;
6543 /* now we have to actually adjust the data */
6544 if (!(infoPtr->dwStyle & LVS_OWNERDATA) && infoPtr->nItemCount > 0)
6546 SUBITEM_INFO *lpSubItem;
6550 for (nItem = 0; nItem < infoPtr->nItemCount; nItem++)
6552 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, nItem);
6553 for (i = 1; i < DPA_GetPtrCount(hdpaSubItems); i++)
6555 lpSubItem = (SUBITEM_INFO *)DPA_GetPtr(hdpaSubItems, i);
6556 if (lpSubItem->iSubItem >= nNewColumn)
6557 lpSubItem->iSubItem++;
6562 /* make space for the new column */
6563 LISTVIEW_ScrollColumns(infoPtr, nNewColumn + 1, lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
6568 if (nNewColumn != -1) SendMessageW(infoPtr->hwndHeader, HDM_DELETEITEM, nNewColumn, 0);
6571 DPA_DeletePtr(infoPtr->hdpaColumns, nNewColumn);
6579 * Sets the attributes of a header item.
6582 * [I] infoPtr : valid pointer to the listview structure
6583 * [I] nColumn : column index
6584 * [I] lpColumn : column attributes
6585 * [I] isW: if TRUE, the lpColumn is a LPLVCOLUMNW, else it is a LPLVCOLUMNA
6591 static BOOL LISTVIEW_SetColumnT(LISTVIEW_INFO *infoPtr, INT nColumn,
6592 const LVCOLUMNW *lpColumn, BOOL isW)
6594 HDITEMW hdi, hdiget;
6597 TRACE("(nColumn=%d, lpColumn=%s, isW=%d)\n", nColumn, debuglvcolumn_t(lpColumn, isW), isW);
6599 if (!lpColumn || nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6601 ZeroMemory(&hdi, sizeof(HDITEMW));
6602 if (lpColumn->mask & LVCF_FMT)
6604 hdi.mask |= HDI_FORMAT;
6605 hdiget.mask = HDI_FORMAT;
6606 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdiget))
6607 hdi.fmt = hdiget.fmt & HDF_STRING;
6609 column_fill_hditem(infoPtr, &hdi, nColumn, lpColumn, isW);
6611 /* set header item attributes */
6612 bResult = SendMessageW(infoPtr->hwndHeader, isW ? HDM_SETITEMW : HDM_SETITEMA, (WPARAM)nColumn, (LPARAM)&hdi);
6613 if (!bResult) return FALSE;
6615 if (lpColumn->mask & LVCF_FMT)
6617 COLUMN_INFO *lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, nColumn);
6618 int oldFmt = lpColumnInfo->fmt;
6620 lpColumnInfo->fmt = lpColumn->fmt;
6621 if ((oldFmt ^ lpColumn->fmt) & (LVCFMT_JUSTIFYMASK | LVCFMT_IMAGE))
6623 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6624 if (uView == LVS_REPORT) LISTVIEW_InvalidateColumn(infoPtr, nColumn);
6633 * Sets the column order array
6636 * [I] infoPtr : valid pointer to the listview structure
6637 * [I] iCount : number of elements in column order array
6638 * [I] lpiArray : pointer to column order array
6644 static BOOL LISTVIEW_SetColumnOrderArray(LISTVIEW_INFO *infoPtr, INT iCount, const INT *lpiArray)
6646 FIXME("iCount %d lpiArray %p\n", iCount, lpiArray);
6657 * Sets the width of a column
6660 * [I] infoPtr : valid pointer to the listview structure
6661 * [I] nColumn : column index
6662 * [I] cx : column width
6668 static BOOL LISTVIEW_SetColumnWidth(LISTVIEW_INFO *infoPtr, INT nColumn, INT cx)
6670 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6671 WCHAR szDispText[DISP_TEXT_SIZE] = { 0 };
6675 TRACE("(nColumn=%d, cx=%d\n", nColumn, cx);
6677 /* set column width only if in report or list mode */
6678 if (uView != LVS_REPORT && uView != LVS_LIST) return FALSE;
6680 /* take care of invalid cx values */
6681 if(uView == LVS_REPORT && cx < -2) cx = LVSCW_AUTOSIZE;
6682 else if (uView == LVS_LIST && cx < 1) return FALSE;
6684 /* resize all columns if in LVS_LIST mode */
6685 if(uView == LVS_LIST)
6687 infoPtr->nItemWidth = cx;
6688 LISTVIEW_InvalidateList(infoPtr);
6692 if (nColumn < 0 || nColumn >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return FALSE;
6694 if (cx == LVSCW_AUTOSIZE || (cx == LVSCW_AUTOSIZE_USEHEADER && nColumn < DPA_GetPtrCount(infoPtr->hdpaColumns) -1))
6699 lvItem.mask = LVIF_TEXT;
6701 lvItem.iSubItem = nColumn;
6702 lvItem.pszText = szDispText;
6703 lvItem.cchTextMax = DISP_TEXT_SIZE;
6704 for (; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
6706 if (!LISTVIEW_GetItemW(infoPtr, &lvItem)) continue;
6707 nLabelWidth = LISTVIEW_GetStringWidthT(infoPtr, lvItem.pszText, TRUE);
6708 if (max_cx < nLabelWidth) max_cx = nLabelWidth;
6710 if (infoPtr->himlSmall && (nColumn == 0 || (LISTVIEW_GetColumnInfo(infoPtr, nColumn)->fmt & LVCFMT_IMAGE)))
6711 max_cx += infoPtr->iconSize.cx;
6712 max_cx += TRAILING_LABEL_PADDING;
6715 /* autosize based on listview items width */
6716 if(cx == LVSCW_AUTOSIZE)
6718 else if(cx == LVSCW_AUTOSIZE_USEHEADER)
6720 /* if iCol is the last column make it fill the remainder of the controls width */
6721 if(nColumn == DPA_GetPtrCount(infoPtr->hdpaColumns) - 1)
6726 LISTVIEW_GetOrigin(infoPtr, &Origin);
6727 LISTVIEW_GetHeaderRect(infoPtr, nColumn, &rcHeader);
6729 cx = infoPtr->rcList.right - Origin.x - rcHeader.left;
6733 /* Despite what the MS docs say, if this is not the last
6734 column, then MS resizes the column to the width of the
6735 largest text string in the column, including headers
6736 and items. This is different from LVSCW_AUTOSIZE in that
6737 LVSCW_AUTOSIZE ignores the header string length. */
6740 /* retrieve header text */
6741 hdi.mask = HDI_TEXT;
6742 hdi.cchTextMax = DISP_TEXT_SIZE;
6743 hdi.pszText = szDispText;
6744 if (Header_GetItemW(infoPtr->hwndHeader, nColumn, &hdi))
6746 HDC hdc = GetDC(infoPtr->hwndSelf);
6747 HFONT old_font = SelectObject(hdc, (HFONT)SendMessageW(infoPtr->hwndHeader, WM_GETFONT, 0, 0));
6750 if (GetTextExtentPoint32W(hdc, hdi.pszText, lstrlenW(hdi.pszText), &size))
6751 cx = size.cx + TRAILING_HEADER_PADDING;
6752 /* FIXME: Take into account the header image, if one is present */
6753 SelectObject(hdc, old_font);
6754 ReleaseDC(infoPtr->hwndSelf, hdc);
6756 cx = max (cx, max_cx);
6760 if (cx < 0) return FALSE;
6762 /* call header to update the column change */
6763 hdi.mask = HDI_WIDTH;
6765 TRACE("hdi.cxy=%d\n", hdi.cxy);
6766 return Header_SetItemW(infoPtr->hwndHeader, nColumn, &hdi);
6770 * Creates the checkbox imagelist. Helper for LISTVIEW_SetExtendedListViewStyle
6773 static HIMAGELIST LISTVIEW_CreateCheckBoxIL(LISTVIEW_INFO *infoPtr)
6776 HBITMAP hbm_im, hbm_mask, hbm_orig;
6778 HBRUSH hbr_white = GetStockObject(WHITE_BRUSH);
6779 HBRUSH hbr_black = GetStockObject(BLACK_BRUSH);
6782 himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
6783 ILC_COLOR | ILC_MASK, 2, 2);
6784 hdc_wnd = GetDC(infoPtr->hwndSelf);
6785 hdc = CreateCompatibleDC(hdc_wnd);
6786 hbm_im = CreateCompatibleBitmap(hdc_wnd, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
6787 hbm_mask = CreateBitmap(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 1, 1, NULL);
6788 ReleaseDC(infoPtr->hwndSelf, hdc_wnd);
6790 rc.left = rc.top = 0;
6791 rc.right = GetSystemMetrics(SM_CXSMICON);
6792 rc.bottom = GetSystemMetrics(SM_CYSMICON);
6794 hbm_orig = SelectObject(hdc, hbm_mask);
6795 FillRect(hdc, &rc, hbr_white);
6796 InflateRect(&rc, -3, -3);
6797 FillRect(hdc, &rc, hbr_black);
6799 SelectObject(hdc, hbm_im);
6800 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO);
6801 SelectObject(hdc, hbm_orig);
6802 ImageList_Add(himl, hbm_im, hbm_mask);
6804 SelectObject(hdc, hbm_im);
6805 DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_MONO | DFCS_CHECKED);
6806 SelectObject(hdc, hbm_orig);
6807 ImageList_Add(himl, hbm_im, hbm_mask);
6809 DeleteObject(hbm_mask);
6810 DeleteObject(hbm_im);
6818 * Sets the extended listview style.
6821 * [I] infoPtr : valid pointer to the listview structure
6823 * [I] dwStyle : style
6826 * SUCCESS : previous style
6829 static DWORD LISTVIEW_SetExtendedListViewStyle(LISTVIEW_INFO *infoPtr, DWORD dwMask, DWORD dwStyle)
6831 DWORD dwOldStyle = infoPtr->dwLvExStyle;
6835 infoPtr->dwLvExStyle = (dwOldStyle & ~dwMask) | (dwStyle & dwMask);
6837 infoPtr->dwLvExStyle = dwStyle;
6839 if((infoPtr->dwLvExStyle ^ dwOldStyle) & LVS_EX_CHECKBOXES)
6841 HIMAGELIST himl = 0;
6842 if(infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES)
6843 himl = LISTVIEW_CreateCheckBoxIL(infoPtr);
6844 LISTVIEW_SetImageList(infoPtr, LVSIL_STATE, himl);
6852 * Sets the new hot cursor used during hot tracking and hover selection.
6855 * [I] infoPtr : valid pointer to the listview structure
6856 * [I] hCursor : the new hot cursor handle
6859 * Returns the previous hot cursor
6861 static HCURSOR LISTVIEW_SetHotCursor(LISTVIEW_INFO *infoPtr, HCURSOR hCursor)
6863 HCURSOR oldCursor = infoPtr->hHotCursor;
6865 infoPtr->hHotCursor = hCursor;
6873 * Sets the hot item index.
6876 * [I] infoPtr : valid pointer to the listview structure
6877 * [I] iIndex : index
6880 * SUCCESS : previous hot item index
6881 * FAILURE : -1 (no hot item)
6883 static INT LISTVIEW_SetHotItem(LISTVIEW_INFO *infoPtr, INT iIndex)
6885 INT iOldIndex = infoPtr->nHotItem;
6887 infoPtr->nHotItem = iIndex;
6895 * Sets the amount of time the cursor must hover over an item before it is selected.
6898 * [I] infoPtr : valid pointer to the listview structure
6899 * [I] dwHoverTime : hover time, if -1 the hover time is set to the default
6902 * Returns the previous hover time
6904 static DWORD LISTVIEW_SetHoverTime(LISTVIEW_INFO *infoPtr, DWORD dwHoverTime)
6906 DWORD oldHoverTime = infoPtr->dwHoverTime;
6908 infoPtr->dwHoverTime = dwHoverTime;
6910 return oldHoverTime;
6915 * Sets spacing for icons of LVS_ICON style.
6918 * [I] infoPtr : valid pointer to the listview structure
6919 * [I] cx : horizontal spacing (-1 = system spacing, 0 = autosize)
6920 * [I] cy : vertical spacing (-1 = system spacing, 0 = autosize)
6923 * MAKELONG(oldcx, oldcy)
6925 static DWORD LISTVIEW_SetIconSpacing(LISTVIEW_INFO *infoPtr, INT cx, INT cy)
6927 DWORD oldspacing = MAKELONG(infoPtr->iconSpacing.cx, infoPtr->iconSpacing.cy);
6928 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6930 TRACE("requested=(%d,%d)\n", cx, cy);
6932 /* this is supported only for LVS_ICON style */
6933 if (uView != LVS_ICON) return oldspacing;
6935 /* set to defaults, if instructed to */
6936 if (cx == -1) cx = GetSystemMetrics(SM_CXICONSPACING);
6937 if (cy == -1) cy = GetSystemMetrics(SM_CYICONSPACING);
6939 /* if 0 then compute width
6940 * FIXME: Should scan each item and determine max width of
6941 * icon or label, then make that the width */
6943 cx = infoPtr->iconSpacing.cx;
6945 /* if 0 then compute height */
6947 cy = infoPtr->iconSize.cy + 2 * infoPtr->ntmHeight +
6948 ICON_BOTTOM_PADDING + ICON_TOP_PADDING + LABEL_VERT_PADDING;
6951 infoPtr->iconSpacing.cx = cx;
6952 infoPtr->iconSpacing.cy = cy;
6954 TRACE("old=(%d,%d), new=(%d,%d), iconSize=(%ld,%ld), ntmH=%d\n",
6955 LOWORD(oldspacing), HIWORD(oldspacing), cx, cy,
6956 infoPtr->iconSize.cx, infoPtr->iconSize.cy,
6957 infoPtr->ntmHeight);
6959 /* these depend on the iconSpacing */
6960 LISTVIEW_UpdateItemSize(infoPtr);
6965 static inline void set_icon_size(SIZE *size, HIMAGELIST himl, BOOL small)
6969 if (himl && ImageList_GetIconSize(himl, &cx, &cy))
6976 size->cx = GetSystemMetrics(small ? SM_CXSMICON : SM_CXICON);
6977 size->cy = GetSystemMetrics(small ? SM_CYSMICON : SM_CYICON);
6986 * [I] infoPtr : valid pointer to the listview structure
6987 * [I] nType : image list type
6988 * [I] himl : image list handle
6991 * SUCCESS : old image list
6994 static HIMAGELIST LISTVIEW_SetImageList(LISTVIEW_INFO *infoPtr, INT nType, HIMAGELIST himl)
6996 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
6997 INT oldHeight = infoPtr->nItemHeight;
6998 HIMAGELIST himlOld = 0;
7000 TRACE("(nType=%d, himl=%p\n", nType, himl);
7005 himlOld = infoPtr->himlNormal;
7006 infoPtr->himlNormal = himl;
7007 if (uView == LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, FALSE);
7008 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
7012 himlOld = infoPtr->himlSmall;
7013 infoPtr->himlSmall = himl;
7014 if (uView != LVS_ICON) set_icon_size(&infoPtr->iconSize, himl, TRUE);
7018 himlOld = infoPtr->himlState;
7019 infoPtr->himlState = himl;
7020 set_icon_size(&infoPtr->iconStateSize, himl, TRUE);
7021 ImageList_SetBkColor(infoPtr->himlState, CLR_NONE);
7025 ERR("Unknown icon type=%d\n", nType);
7029 infoPtr->nItemHeight = LISTVIEW_CalculateItemHeight(infoPtr);
7030 if (infoPtr->nItemHeight != oldHeight)
7031 LISTVIEW_UpdateScroll(infoPtr);
7038 * Preallocates memory (does *not* set the actual count of items !)
7041 * [I] infoPtr : valid pointer to the listview structure
7042 * [I] nItems : item count (projected number of items to allocate)
7043 * [I] dwFlags : update flags
7049 static BOOL LISTVIEW_SetItemCount(LISTVIEW_INFO *infoPtr, INT nItems, DWORD dwFlags)
7051 TRACE("(nItems=%d, dwFlags=%lx)\n", nItems, dwFlags);
7053 if (infoPtr->dwStyle & LVS_OWNERDATA)
7055 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7056 INT nOldCount = infoPtr->nItemCount;
7058 if (nItems < nOldCount)
7060 RANGE range = { nItems, nOldCount };
7061 ranges_del(infoPtr->selectionRanges, range);
7062 if (infoPtr->nFocusedItem >= nItems)
7064 infoPtr->nFocusedItem = -1;
7065 SetRectEmpty(&infoPtr->rcFocus);
7069 infoPtr->nItemCount = nItems;
7070 LISTVIEW_UpdateScroll(infoPtr);
7072 /* the flags are valid only in ownerdata report and list modes */
7073 if (uView == LVS_ICON || uView == LVS_SMALLICON) dwFlags = 0;
7075 if (!(dwFlags & LVSICF_NOSCROLL) && infoPtr->nFocusedItem != -1)
7076 LISTVIEW_EnsureVisible(infoPtr, infoPtr->nFocusedItem, FALSE);
7078 if (!(dwFlags & LVSICF_NOINVALIDATEALL))
7079 LISTVIEW_InvalidateList(infoPtr);
7086 LISTVIEW_GetOrigin(infoPtr, &Origin);
7087 nFrom = min(nOldCount, nItems);
7088 nTo = max(nOldCount, nItems);
7090 if (uView == LVS_REPORT)
7093 rcErase.top = nFrom * infoPtr->nItemHeight;
7094 rcErase.right = infoPtr->nItemWidth;
7095 rcErase.bottom = nTo * infoPtr->nItemHeight;
7096 OffsetRect(&rcErase, Origin.x, Origin.y);
7097 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7098 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7102 INT nPerCol = LISTVIEW_GetCountPerColumn(infoPtr);
7104 rcErase.left = (nFrom / nPerCol) * infoPtr->nItemWidth;
7105 rcErase.top = (nFrom % nPerCol) * infoPtr->nItemHeight;
7106 rcErase.right = rcErase.left + infoPtr->nItemWidth;
7107 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7108 OffsetRect(&rcErase, Origin.x, Origin.y);
7109 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7110 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7112 rcErase.left = (nFrom / nPerCol + 1) * infoPtr->nItemWidth;
7114 rcErase.right = (nTo / nPerCol + 1) * infoPtr->nItemWidth;
7115 rcErase.bottom = nPerCol * infoPtr->nItemHeight;
7116 OffsetRect(&rcErase, Origin.x, Origin.y);
7117 if (IntersectRect(&rcErase, &rcErase, &infoPtr->rcList))
7118 LISTVIEW_InvalidateRect(infoPtr, &rcErase);
7124 /* According to MSDN for non-LVS_OWNERDATA this is just
7125 * a performance issue. The control allocates its internal
7126 * data structures for the number of items specified. It
7127 * cuts down on the number of memory allocations. Therefore
7128 * we will just issue a WARN here
7130 WARN("for non-ownerdata performance option not implemented.\n");
7138 * Sets the position of an item.
7141 * [I] infoPtr : valid pointer to the listview structure
7142 * [I] nItem : item index
7143 * [I] pt : coordinate
7149 static BOOL LISTVIEW_SetItemPosition(LISTVIEW_INFO *infoPtr, INT nItem, POINT pt)
7151 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7154 TRACE("(nItem=%d, &pt=%s\n", nItem, wine_dbgstr_point(&pt));
7156 if (nItem < 0 || nItem >= infoPtr->nItemCount ||
7157 !(uView == LVS_ICON || uView == LVS_SMALLICON)) return FALSE;
7159 LISTVIEW_GetOrigin(infoPtr, &Origin);
7161 /* This point value seems to be an undocumented feature.
7162 * The best guess is that it means either at the origin,
7163 * or at true beginning of the list. I will assume the origin. */
7164 if ((pt.x == -1) && (pt.y == -1))
7167 if (uView == LVS_ICON)
7169 pt.x -= (infoPtr->nItemWidth - infoPtr->iconSize.cx) / 2;
7170 pt.y -= ICON_TOP_PADDING;
7175 infoPtr->bAutoarrange = FALSE;
7177 return LISTVIEW_MoveIconTo(infoPtr, nItem, &pt, FALSE);
7182 * Sets the state of one or many items.
7185 * [I] infoPtr : valid pointer to the listview structure
7186 * [I] nItem : item index
7187 * [I] lpLVItem : item or subitem info
7193 static BOOL LISTVIEW_SetItemState(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem)
7195 BOOL bResult = TRUE;
7198 lvItem.iItem = nItem;
7199 lvItem.iSubItem = 0;
7200 lvItem.mask = LVIF_STATE;
7201 lvItem.state = lpLVItem->state;
7202 lvItem.stateMask = lpLVItem->stateMask;
7203 TRACE("lvItem=%s\n", debuglvitem_t(&lvItem, TRUE));
7207 /* apply to all items */
7208 for (lvItem.iItem = 0; lvItem.iItem < infoPtr->nItemCount; lvItem.iItem++)
7209 if (!LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE)) bResult = FALSE;
7212 bResult = LISTVIEW_SetItemT(infoPtr, &lvItem, TRUE);
7215 * Update selection mark
7217 * Investigation on windows 2k showed that selection mark was updated
7218 * whenever a new selection was made, but if the selected item was
7219 * unselected it was not updated.
7221 * we are probably still not 100% accurate, but this at least sets the
7222 * proper selection mark when it is needed
7225 if (bResult && (lvItem.state & lvItem.stateMask & LVIS_SELECTED) &&
7226 ((infoPtr->nSelectionMark == -1) || (lvItem.iItem <= infoPtr->nSelectionMark)))
7229 infoPtr->nSelectionMark = -1;
7230 for (i = 0; i < infoPtr->nItemCount; i++)
7232 if (infoPtr->uCallbackMask & LVIS_SELECTED)
7234 if (LISTVIEW_GetItemState(infoPtr, i, LVIS_SELECTED))
7236 infoPtr->nSelectionMark = i;
7240 else if (ranges_contain(infoPtr->selectionRanges, i))
7242 infoPtr->nSelectionMark = i;
7253 * Sets the text of an item or subitem.
7256 * [I] hwnd : window handle
7257 * [I] nItem : item index
7258 * [I] lpLVItem : item or subitem info
7259 * [I] isW : TRUE if input is Unicode
7265 static BOOL LISTVIEW_SetItemTextT(LISTVIEW_INFO *infoPtr, INT nItem, const LVITEMW *lpLVItem, BOOL isW)
7269 if (nItem < 0 && nItem >= infoPtr->nItemCount) return FALSE;
7271 lvItem.iItem = nItem;
7272 lvItem.iSubItem = lpLVItem->iSubItem;
7273 lvItem.mask = LVIF_TEXT;
7274 lvItem.pszText = lpLVItem->pszText;
7275 lvItem.cchTextMax = lpLVItem->cchTextMax;
7277 TRACE("(nItem=%d, lpLVItem=%s, isW=%d)\n", nItem, debuglvitem_t(&lvItem, isW), isW);
7279 return LISTVIEW_SetItemT(infoPtr, &lvItem, isW);
7284 * Set item index that marks the start of a multiple selection.
7287 * [I] infoPtr : valid pointer to the listview structure
7288 * [I] nIndex : index
7291 * Index number or -1 if there is no selection mark.
7293 static INT LISTVIEW_SetSelectionMark(LISTVIEW_INFO *infoPtr, INT nIndex)
7295 INT nOldIndex = infoPtr->nSelectionMark;
7297 TRACE("(nIndex=%d)\n", nIndex);
7299 infoPtr->nSelectionMark = nIndex;
7306 * Sets the text background color.
7309 * [I] infoPtr : valid pointer to the listview structure
7310 * [I] clrTextBk : text background color
7316 static BOOL LISTVIEW_SetTextBkColor(LISTVIEW_INFO *infoPtr, COLORREF clrTextBk)
7318 TRACE("(clrTextBk=%lx)\n", clrTextBk);
7320 if (infoPtr->clrTextBk != clrTextBk)
7322 infoPtr->clrTextBk = clrTextBk;
7323 LISTVIEW_InvalidateList(infoPtr);
7331 * Sets the text foreground color.
7334 * [I] infoPtr : valid pointer to the listview structure
7335 * [I] clrText : text color
7341 static BOOL LISTVIEW_SetTextColor (LISTVIEW_INFO *infoPtr, COLORREF clrText)
7343 TRACE("(clrText=%lx)\n", clrText);
7345 if (infoPtr->clrText != clrText)
7347 infoPtr->clrText = clrText;
7348 LISTVIEW_InvalidateList(infoPtr);
7356 * Determines which listview item is located at the specified position.
7359 * [I] infoPtr : valid pointer to the listview structure
7360 * [I] hwndNewToolTip : handle to new ToolTip
7365 static HWND LISTVIEW_SetToolTips( LISTVIEW_INFO *infoPtr, HWND hwndNewToolTip)
7367 HWND hwndOldToolTip = infoPtr->hwndToolTip;
7368 infoPtr->hwndToolTip = hwndNewToolTip;
7369 return hwndOldToolTip;
7372 /* LISTVIEW_SetUnicodeFormat */
7373 /* LISTVIEW_SetWorkAreas */
7377 * Callback internally used by LISTVIEW_SortItems()
7380 * [I] first : pointer to first ITEM_INFO to compare
7381 * [I] second : pointer to second ITEM_INFO to compare
7382 * [I] lParam : HWND of control
7385 * if first comes before second : negative
7386 * if first comes after second : positive
7387 * if first and second are equivalent : zero
7389 static INT WINAPI LISTVIEW_CallBackCompare(LPVOID first, LPVOID second, LPARAM lParam)
7391 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)lParam;
7392 ITEM_INFO* lv_first = (ITEM_INFO*) DPA_GetPtr( (HDPA)first, 0 );
7393 ITEM_INFO* lv_second = (ITEM_INFO*) DPA_GetPtr( (HDPA)second, 0 );
7395 /* Forward the call to the client defined callback */
7396 return (infoPtr->pfnCompare)( lv_first->lParam , lv_second->lParam, infoPtr->lParamSort );
7401 * Sorts the listview items.
7404 * [I] infoPtr : valid pointer to the listview structure
7405 * [I] pfnCompare : application-defined value
7406 * [I] lParamSort : pointer to comparision callback
7412 static BOOL LISTVIEW_SortItems(LISTVIEW_INFO *infoPtr, PFNLVCOMPARE pfnCompare, LPARAM lParamSort)
7414 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7417 LPVOID selectionMarkItem;
7421 TRACE("(pfnCompare=%p, lParamSort=%lx)\n", pfnCompare, lParamSort);
7423 if (infoPtr->dwStyle & LVS_OWNERDATA) return FALSE;
7425 if (!pfnCompare) return FALSE;
7426 if (!infoPtr->hdpaItems) return FALSE;
7428 /* if there are 0 or 1 items, there is no need to sort */
7429 if (infoPtr->nItemCount < 2) return TRUE;
7431 if (infoPtr->nFocusedItem >= 0)
7433 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nFocusedItem);
7434 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7435 if (lpItem) lpItem->state |= LVIS_FOCUSED;
7437 /* FIXME: go thorugh selected items and mark them so in lpItem->state */
7438 /* clear the lpItem->state for non-selected ones */
7439 /* remove the selection ranges */
7441 infoPtr->pfnCompare = pfnCompare;
7442 infoPtr->lParamSort = lParamSort;
7443 DPA_Sort(infoPtr->hdpaItems, LISTVIEW_CallBackCompare, (LPARAM)infoPtr);
7445 /* Adjust selections and indices so that they are the way they should
7446 * be after the sort (otherwise, the list items move around, but
7447 * whatever is at the item's previous original position will be
7450 selectionMarkItem=(infoPtr->nSelectionMark>=0)?DPA_GetPtr(infoPtr->hdpaItems, infoPtr->nSelectionMark):NULL;
7451 for (i=0; i < infoPtr->nItemCount; i++)
7453 hdpaSubItems = (HDPA)DPA_GetPtr(infoPtr->hdpaItems, i);
7454 lpItem = (ITEM_INFO *)DPA_GetPtr(hdpaSubItems, 0);
7456 if (lpItem->state & LVIS_SELECTED)
7458 item.state = LVIS_SELECTED;
7459 item.stateMask = LVIS_SELECTED;
7460 LISTVIEW_SetItemState(infoPtr, i, &item);
7462 if (lpItem->state & LVIS_FOCUSED)
7464 infoPtr->nFocusedItem = i;
7465 lpItem->state &= ~LVIS_FOCUSED;
7468 if (selectionMarkItem != NULL)
7469 infoPtr->nSelectionMark = DPA_GetPtrIndex(infoPtr->hdpaItems, selectionMarkItem);
7470 /* I believe nHotItem should be left alone, see LISTVIEW_ShiftIndices */
7472 /* refresh the display */
7473 if (uView != LVS_ICON && uView != LVS_SMALLICON)
7474 LISTVIEW_InvalidateList(infoPtr);
7481 * Update theme handle after a theme change.
7484 * [I] infoPtr : valid pointer to the listview structure
7488 * FAILURE : something else
7490 static LRESULT LISTVIEW_ThemeChanged(LISTVIEW_INFO *infoPtr)
7492 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7493 CloseThemeData(theme);
7494 OpenThemeData(infoPtr->hwndSelf, themeClass);
7500 * Updates an items or rearranges the listview control.
7503 * [I] infoPtr : valid pointer to the listview structure
7504 * [I] nItem : item index
7510 static BOOL LISTVIEW_Update(LISTVIEW_INFO *infoPtr, INT nItem)
7512 TRACE("(nItem=%d)\n", nItem);
7514 if (nItem < 0 || nItem >= infoPtr->nItemCount) return FALSE;
7516 /* rearrange with default alignment style */
7517 if (is_autoarrange(infoPtr))
7518 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
7520 LISTVIEW_InvalidateItem(infoPtr, nItem);
7527 * Draw the track line at the place defined in the infoPtr structure.
7528 * The line is drawn with a XOR pen so drawing the line for the second time
7529 * in the same place erases the line.
7532 * [I] infoPtr : valid pointer to the listview structure
7538 static BOOL LISTVIEW_DrawTrackLine(LISTVIEW_INFO *infoPtr)
7544 if (infoPtr->xTrackLine == -1)
7547 if (!(hdc = GetDC(infoPtr->hwndSelf)))
7549 hOldPen = SelectObject(hdc, GetStockObject(BLACK_PEN));
7550 oldROP = SetROP2(hdc, R2_XORPEN);
7551 MoveToEx(hdc, infoPtr->xTrackLine, infoPtr->rcList.top, NULL);
7552 LineTo(hdc, infoPtr->xTrackLine, infoPtr->rcList.bottom);
7553 SetROP2(hdc, oldROP);
7554 SelectObject(hdc, hOldPen);
7555 ReleaseDC(infoPtr->hwndSelf, hdc);
7562 * Creates the listview control.
7565 * [I] hwnd : window handle
7566 * [I] lpcs : the create parameters
7572 static LRESULT LISTVIEW_Create(HWND hwnd, const CREATESTRUCTW *lpcs)
7574 LISTVIEW_INFO *infoPtr;
7575 UINT uView = lpcs->style & LVS_TYPEMASK;
7578 TRACE("(lpcs=%p)\n", lpcs);
7580 /* initialize info pointer */
7581 infoPtr = (LISTVIEW_INFO *)Alloc(sizeof(LISTVIEW_INFO));
7582 if (!infoPtr) return -1;
7584 SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)infoPtr);
7586 infoPtr->hwndSelf = hwnd;
7587 infoPtr->dwStyle = lpcs->style;
7588 /* determine the type of structures to use */
7589 infoPtr->hwndNotify = lpcs->hwndParent;
7590 infoPtr->notifyFormat = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT,
7591 (WPARAM)infoPtr->hwndSelf, (LPARAM)NF_QUERY);
7593 /* initialize color information */
7594 infoPtr->clrBk = CLR_NONE;
7595 infoPtr->clrText = comctl32_color.clrWindowText;
7596 infoPtr->clrTextBk = CLR_DEFAULT;
7597 LISTVIEW_SetBkColor(infoPtr, comctl32_color.clrWindow);
7599 /* set default values */
7600 infoPtr->nFocusedItem = -1;
7601 infoPtr->nSelectionMark = -1;
7602 infoPtr->nHotItem = -1;
7603 infoPtr->bRedraw = TRUE;
7604 infoPtr->bNoItemMetrics = TRUE;
7605 infoPtr->bDoChangeNotify = TRUE;
7606 infoPtr->iconSpacing.cx = GetSystemMetrics(SM_CXICONSPACING);
7607 infoPtr->iconSpacing.cy = GetSystemMetrics(SM_CYICONSPACING);
7608 infoPtr->nEditLabelItem = -1;
7609 infoPtr->dwHoverTime = -1; /* default system hover time */
7610 infoPtr->nMeasureItemHeight = 0;
7611 infoPtr->xTrackLine = -1; /* no track line */
7613 /* get default font (icon title) */
7614 SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
7615 infoPtr->hDefaultFont = CreateFontIndirectW(&logFont);
7616 infoPtr->hFont = infoPtr->hDefaultFont;
7617 LISTVIEW_SaveTextMetrics(infoPtr);
7620 infoPtr->hwndHeader = CreateWindowW(WC_HEADERW, NULL,
7621 WS_CHILD | HDS_HORZ | HDS_FULLDRAG | (DWORD)((LVS_NOSORTHEADER & lpcs->style)?0:HDS_BUTTONS),
7622 0, 0, 0, 0, hwnd, NULL,
7623 lpcs->hInstance, NULL);
7624 if (!infoPtr->hwndHeader) goto fail;
7626 /* set header unicode format */
7627 SendMessageW(infoPtr->hwndHeader, HDM_SETUNICODEFORMAT, (WPARAM)TRUE, (LPARAM)NULL);
7629 /* set header font */
7630 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)infoPtr->hFont, (LPARAM)TRUE);
7632 /* allocate memory for the data structure */
7633 if (!(infoPtr->selectionRanges = ranges_create(10))) goto fail;
7634 if (!(infoPtr->hdpaItems = DPA_Create(10))) goto fail;
7635 if (!(infoPtr->hdpaPosX = DPA_Create(10))) goto fail;
7636 if (!(infoPtr->hdpaPosY = DPA_Create(10))) goto fail;
7637 if (!(infoPtr->hdpaColumns = DPA_Create(10))) goto fail;
7639 /* initialize the icon sizes */
7640 set_icon_size(&infoPtr->iconSize, infoPtr->himlNormal, uView != LVS_ICON);
7641 set_icon_size(&infoPtr->iconStateSize, infoPtr->himlState, TRUE);
7643 /* init item size to avoid division by 0 */
7644 LISTVIEW_UpdateItemSize (infoPtr);
7646 if (uView == LVS_REPORT)
7648 if (!(LVS_NOCOLUMNHEADER & lpcs->style))
7650 ShowWindow(infoPtr->hwndHeader, SW_SHOWNORMAL);
7654 /* set HDS_HIDDEN flag to hide the header bar */
7655 SetWindowLongW(infoPtr->hwndHeader, GWL_STYLE,
7656 GetWindowLongW(infoPtr->hwndHeader, GWL_STYLE) | HDS_HIDDEN);
7660 OpenThemeData(hwnd, themeClass);
7665 DestroyWindow(infoPtr->hwndHeader);
7666 ranges_destroy(infoPtr->selectionRanges);
7667 DPA_Destroy(infoPtr->hdpaItems);
7668 DPA_Destroy(infoPtr->hdpaPosX);
7669 DPA_Destroy(infoPtr->hdpaPosY);
7670 DPA_Destroy(infoPtr->hdpaColumns);
7677 * Destroys the listview control.
7680 * [I] infoPtr : valid pointer to the listview structure
7686 static LRESULT LISTVIEW_Destroy(LISTVIEW_INFO *infoPtr)
7688 HTHEME theme = GetWindowTheme(infoPtr->hwndSelf);
7689 CloseThemeData(theme);
7695 * Enables the listview control.
7698 * [I] infoPtr : valid pointer to the listview structure
7699 * [I] bEnable : specifies whether to enable or disable the window
7705 static BOOL LISTVIEW_Enable(LISTVIEW_INFO *infoPtr, BOOL bEnable)
7707 if (infoPtr->dwStyle & LVS_OWNERDRAWFIXED)
7708 InvalidateRect(infoPtr->hwndSelf, NULL, TRUE);
7714 * Erases the background of the listview control.
7717 * [I] infoPtr : valid pointer to the listview structure
7718 * [I] hdc : device context handle
7724 static inline BOOL LISTVIEW_EraseBkgnd(LISTVIEW_INFO *infoPtr, HDC hdc)
7728 TRACE("(hdc=%p)\n", hdc);
7730 if (!GetClipBox(hdc, &rc)) return FALSE;
7732 return LISTVIEW_FillBkgnd(infoPtr, hdc, &rc);
7738 * Helper function for LISTVIEW_[HV]Scroll *only*.
7739 * Performs vertical/horizontal scrolling by a give amount.
7742 * [I] infoPtr : valid pointer to the listview structure
7743 * [I] dx : amount of horizontal scroll
7744 * [I] dy : amount of vertical scroll
7746 static void scroll_list(LISTVIEW_INFO *infoPtr, INT dx, INT dy)
7748 /* now we can scroll the list */
7749 ScrollWindowEx(infoPtr->hwndSelf, dx, dy, &infoPtr->rcList,
7750 &infoPtr->rcList, 0, 0, SW_ERASE | SW_INVALIDATE);
7751 /* if we have focus, adjust rect */
7752 OffsetRect(&infoPtr->rcFocus, dx, dy);
7753 UpdateWindow(infoPtr->hwndSelf);
7758 * Performs vertical scrolling.
7761 * [I] infoPtr : valid pointer to the listview structure
7762 * [I] nScrollCode : scroll code
7763 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7764 * [I] hScrollWnd : scrollbar control window handle
7770 * SB_LINEUP/SB_LINEDOWN:
7771 * for LVS_ICON, LVS_SMALLICON is 37 by experiment
7772 * for LVS_REPORT is 1 line
7773 * for LVS_LIST cannot occur
7776 static LRESULT LISTVIEW_VScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7777 INT nScrollDiff, HWND hScrollWnd)
7779 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7780 INT nOldScrollPos, nNewScrollPos;
7781 SCROLLINFO scrollInfo;
7784 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7785 debugscrollcode(nScrollCode), nScrollDiff);
7787 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7789 scrollInfo.cbSize = sizeof(SCROLLINFO);
7790 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7792 is_an_icon = ((uView == LVS_ICON) || (uView == LVS_SMALLICON));
7794 if (!GetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo)) return 1;
7796 nOldScrollPos = scrollInfo.nPos;
7797 switch (nScrollCode)
7803 nScrollDiff = (is_an_icon) ? -LISTVIEW_SCROLL_ICON_LINE_SIZE : -1;
7807 nScrollDiff = (is_an_icon) ? LISTVIEW_SCROLL_ICON_LINE_SIZE : 1;
7811 nScrollDiff = -scrollInfo.nPage;
7815 nScrollDiff = scrollInfo.nPage;
7818 case SB_THUMBPOSITION:
7820 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7827 /* quit right away if pos isn't changing */
7828 if (nScrollDiff == 0) return 0;
7830 /* calculate new position, and handle overflows */
7831 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7832 if (nScrollDiff > 0) {
7833 if (nNewScrollPos < nOldScrollPos ||
7834 nNewScrollPos > scrollInfo.nMax)
7835 nNewScrollPos = scrollInfo.nMax;
7837 if (nNewScrollPos > nOldScrollPos ||
7838 nNewScrollPos < scrollInfo.nMin)
7839 nNewScrollPos = scrollInfo.nMin;
7842 /* set the new position, and reread in case it changed */
7843 scrollInfo.fMask = SIF_POS;
7844 scrollInfo.nPos = nNewScrollPos;
7845 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_VERT, &scrollInfo, TRUE);
7847 /* carry on only if it really changed */
7848 if (nNewScrollPos == nOldScrollPos) return 0;
7850 /* now adjust to client coordinates */
7851 nScrollDiff = nOldScrollPos - nNewScrollPos;
7852 if (uView == LVS_REPORT) nScrollDiff *= infoPtr->nItemHeight;
7854 /* and scroll the window */
7855 scroll_list(infoPtr, 0, nScrollDiff);
7862 * Performs horizontal scrolling.
7865 * [I] infoPtr : valid pointer to the listview structure
7866 * [I] nScrollCode : scroll code
7867 * [I] nScrollDiff : units to scroll in SB_INTERNAL mode, 0 otherwise
7868 * [I] hScrollWnd : scrollbar control window handle
7874 * SB_LINELEFT/SB_LINERIGHT:
7875 * for LVS_ICON, LVS_SMALLICON 1 pixel
7876 * for LVS_REPORT is 1 pixel
7877 * for LVS_LIST is 1 column --> which is a 1 because the
7878 * scroll is based on columns not pixels
7881 static LRESULT LISTVIEW_HScroll(LISTVIEW_INFO *infoPtr, INT nScrollCode,
7882 INT nScrollDiff, HWND hScrollWnd)
7884 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7885 INT nOldScrollPos, nNewScrollPos;
7886 SCROLLINFO scrollInfo;
7888 TRACE("(nScrollCode=%d(%s), nScrollDiff=%d)\n", nScrollCode,
7889 debugscrollcode(nScrollCode), nScrollDiff);
7891 if (infoPtr->hwndEdit) SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
7893 scrollInfo.cbSize = sizeof(SCROLLINFO);
7894 scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
7896 if (!GetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo)) return 1;
7898 nOldScrollPos = scrollInfo.nPos;
7900 switch (nScrollCode)
7914 nScrollDiff = -scrollInfo.nPage;
7918 nScrollDiff = scrollInfo.nPage;
7921 case SB_THUMBPOSITION:
7923 nScrollDiff = scrollInfo.nTrackPos - scrollInfo.nPos;
7930 /* quit right away if pos isn't changing */
7931 if (nScrollDiff == 0) return 0;
7933 /* calculate new position, and handle overflows */
7934 nNewScrollPos = scrollInfo.nPos + nScrollDiff;
7935 if (nScrollDiff > 0) {
7936 if (nNewScrollPos < nOldScrollPos ||
7937 nNewScrollPos > scrollInfo.nMax)
7938 nNewScrollPos = scrollInfo.nMax;
7940 if (nNewScrollPos > nOldScrollPos ||
7941 nNewScrollPos < scrollInfo.nMin)
7942 nNewScrollPos = scrollInfo.nMin;
7945 /* set the new position, and reread in case it changed */
7946 scrollInfo.fMask = SIF_POS;
7947 scrollInfo.nPos = nNewScrollPos;
7948 nNewScrollPos = SetScrollInfo(infoPtr->hwndSelf, SB_HORZ, &scrollInfo, TRUE);
7950 /* carry on only if it really changed */
7951 if (nNewScrollPos == nOldScrollPos) return 0;
7953 if(uView == LVS_REPORT)
7954 LISTVIEW_UpdateHeaderSize(infoPtr, nNewScrollPos);
7956 /* now adjust to client coordinates */
7957 nScrollDiff = nOldScrollPos - nNewScrollPos;
7958 if (uView == LVS_LIST) nScrollDiff *= infoPtr->nItemWidth;
7960 /* and scroll the window */
7961 scroll_list(infoPtr, nScrollDiff, 0);
7966 static LRESULT LISTVIEW_MouseWheel(LISTVIEW_INFO *infoPtr, INT wheelDelta)
7968 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
7969 INT gcWheelDelta = 0;
7970 INT pulScrollLines = 3;
7971 SCROLLINFO scrollInfo;
7973 TRACE("(wheelDelta=%d)\n", wheelDelta);
7975 SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
7976 gcWheelDelta -= wheelDelta;
7978 scrollInfo.cbSize = sizeof(SCROLLINFO);
7979 scrollInfo.fMask = SIF_POS;
7986 * listview should be scrolled by a multiple of 37 dependently on its dimension or its visible item number
7987 * should be fixed in the future.
7989 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, (gcWheelDelta < 0) ?
7990 -LISTVIEW_SCROLL_ICON_LINE_SIZE : LISTVIEW_SCROLL_ICON_LINE_SIZE, 0);
7994 if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
7996 int cLineScroll = min(LISTVIEW_GetCountPerColumn(infoPtr), pulScrollLines);
7997 cLineScroll *= (gcWheelDelta / WHEEL_DELTA);
7998 LISTVIEW_VScroll(infoPtr, SB_INTERNAL, cLineScroll, 0);
8003 LISTVIEW_HScroll(infoPtr, (gcWheelDelta < 0) ? SB_LINELEFT : SB_LINERIGHT, 0, 0);
8014 * [I] infoPtr : valid pointer to the listview structure
8015 * [I] nVirtualKey : virtual key
8016 * [I] lKeyData : key data
8021 static LRESULT LISTVIEW_KeyDown(LISTVIEW_INFO *infoPtr, INT nVirtualKey, LONG lKeyData)
8023 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8024 HWND hwndSelf = infoPtr->hwndSelf;
8026 NMLVKEYDOWN nmKeyDown;
8028 TRACE("(nVirtualKey=%d, lKeyData=%ld)\n", nVirtualKey, lKeyData);
8030 /* send LVN_KEYDOWN notification */
8031 nmKeyDown.wVKey = nVirtualKey;
8032 nmKeyDown.flags = 0;
8033 notify_hdr(infoPtr, LVN_KEYDOWN, &nmKeyDown.hdr);
8034 if (!IsWindow(hwndSelf))
8037 switch (nVirtualKey)
8040 nItem = infoPtr->nFocusedItem;
8044 if ((infoPtr->nItemCount > 0) && (infoPtr->nFocusedItem != -1))
8046 if (!notify(infoPtr, NM_RETURN)) return 0;
8047 if (!notify(infoPtr, LVN_ITEMACTIVATE)) return 0;
8052 if (infoPtr->nItemCount > 0)
8057 if (infoPtr->nItemCount > 0)
8058 nItem = infoPtr->nItemCount - 1;
8062 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TOLEFT);
8066 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_ABOVE);
8070 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_TORIGHT);
8074 nItem = ListView_GetNextItem(infoPtr->hwndSelf, infoPtr->nFocusedItem, LVNI_BELOW);
8078 if (uView == LVS_REPORT)
8080 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8081 if (infoPtr->nFocusedItem == topidx)
8082 nItem = topidx - LISTVIEW_GetCountPerColumn(infoPtr) + 1;
8087 nItem = infoPtr->nFocusedItem - LISTVIEW_GetCountPerColumn(infoPtr)
8088 * LISTVIEW_GetCountPerRow(infoPtr);
8089 if(nItem < 0) nItem = 0;
8093 if (uView == LVS_REPORT)
8095 INT topidx = LISTVIEW_GetTopIndex(infoPtr);
8096 INT cnt = LISTVIEW_GetCountPerColumn(infoPtr);
8097 if (infoPtr->nFocusedItem == topidx + cnt - 1)
8098 nItem = infoPtr->nFocusedItem + cnt - 1;
8100 nItem = topidx + cnt - 1;
8103 nItem = infoPtr->nFocusedItem + LISTVIEW_GetCountPerColumn(infoPtr)
8104 * LISTVIEW_GetCountPerRow(infoPtr);
8105 if(nItem >= infoPtr->nItemCount) nItem = infoPtr->nItemCount - 1;
8109 if ((nItem != -1) && (nItem != infoPtr->nFocusedItem || nVirtualKey == VK_SPACE))
8110 LISTVIEW_KeySelection(infoPtr, nItem);
8120 * [I] infoPtr : valid pointer to the listview structure
8125 static LRESULT LISTVIEW_KillFocus(LISTVIEW_INFO *infoPtr)
8129 /* if we did not have the focus, there's nothing to do */
8130 if (!infoPtr->bFocus) return 0;
8132 /* send NM_KILLFOCUS notification */
8133 if (!notify(infoPtr, NM_KILLFOCUS)) return 0;
8135 /* if we have a focus rectagle, get rid of it */
8136 LISTVIEW_ShowFocusRect(infoPtr, FALSE);
8138 /* set window focus flag */
8139 infoPtr->bFocus = FALSE;
8141 /* invalidate the selected items before reseting focus flag */
8142 LISTVIEW_InvalidateSelectedItems(infoPtr);
8149 * Processes double click messages (left mouse button).
8152 * [I] infoPtr : valid pointer to the listview structure
8153 * [I] wKey : key flag
8154 * [I] x,y : mouse coordinate
8159 static LRESULT LISTVIEW_LButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8161 LVHITTESTINFO htInfo;
8163 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8165 /* send NM_RELEASEDCAPTURE notification */
8166 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8171 /* send NM_DBLCLK notification */
8172 LISTVIEW_HitTest(infoPtr, &htInfo, TRUE, FALSE);
8173 if (!notify_click(infoPtr, NM_DBLCLK, &htInfo)) return 0;
8175 /* To send the LVN_ITEMACTIVATE, it must be on an Item */
8176 if(htInfo.iItem != -1) notify_itemactivate(infoPtr,&htInfo);
8183 * Processes mouse down messages (left mouse button).
8186 * infoPtr [I ] valid pointer to the listview structure
8187 * wKey [I ] key flag
8188 * x,y [I ] mouse coordinate
8193 static LRESULT LISTVIEW_LButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8195 LVHITTESTINFO lvHitTestInfo;
8196 static BOOL bGroupSelect = TRUE;
8197 POINT pt = { x, y };
8200 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8202 /* send NM_RELEASEDCAPTURE notification */
8203 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8205 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8207 /* set left button down flag and record the click position */
8208 infoPtr->bLButtonDown = TRUE;
8209 infoPtr->ptClickPos = pt;
8211 lvHitTestInfo.pt.x = x;
8212 lvHitTestInfo.pt.y = y;
8214 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8215 TRACE("at %s, nItem=%d\n", wine_dbgstr_point(&pt), nItem);
8216 infoPtr->nEditLabelItem = -1;
8217 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8219 if ((infoPtr->dwLvExStyle & LVS_EX_CHECKBOXES) && (lvHitTestInfo.flags & LVHT_ONITEMSTATEICON))
8221 DWORD state = STATEIMAGEINDEX(LISTVIEW_GetItemState(infoPtr, nItem, LVIS_STATEIMAGEMASK));
8222 if(state == 1 || state == 2)
8226 lvitem.state = INDEXTOSTATEIMAGEMASK(state);
8227 lvitem.stateMask = LVIS_STATEIMAGEMASK;
8228 LISTVIEW_SetItemState(infoPtr, nItem, &lvitem);
8233 if (infoPtr->dwStyle & LVS_SINGLESEL)
8235 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8236 infoPtr->nEditLabelItem = nItem;
8238 LISTVIEW_SetSelection(infoPtr, nItem);
8242 if ((wKey & MK_CONTROL) && (wKey & MK_SHIFT))
8246 if (!LISTVIEW_AddGroupSelection(infoPtr, nItem)) return 0;
8247 LISTVIEW_SetItemFocus(infoPtr, nItem);
8248 infoPtr->nSelectionMark = nItem;
8254 item.state = LVIS_SELECTED | LVIS_FOCUSED;
8255 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8257 LISTVIEW_SetItemState(infoPtr,nItem,&item);
8258 infoPtr->nSelectionMark = nItem;
8261 else if (wKey & MK_CONTROL)
8265 bGroupSelect = (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED) == 0);
8267 item.state = (bGroupSelect ? LVIS_SELECTED : 0) | LVIS_FOCUSED;
8268 item.stateMask = LVIS_SELECTED | LVIS_FOCUSED;
8269 LISTVIEW_SetItemState(infoPtr, nItem, &item);
8270 infoPtr->nSelectionMark = nItem;
8272 else if (wKey & MK_SHIFT)
8274 LISTVIEW_SetGroupSelection(infoPtr, nItem);
8278 if (LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8279 infoPtr->nEditLabelItem = nItem;
8281 /* set selection (clears other pre-existing selections) */
8282 LISTVIEW_SetSelection(infoPtr, nItem);
8288 /* remove all selections */
8289 LISTVIEW_DeselectAll(infoPtr);
8298 * Processes mouse up messages (left mouse button).
8301 * infoPtr [I ] valid pointer to the listview structure
8302 * wKey [I ] key flag
8303 * x,y [I ] mouse coordinate
8308 static LRESULT LISTVIEW_LButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8310 LVHITTESTINFO lvHitTestInfo;
8312 TRACE("(key=%hu, X=%hu, Y=%hu)\n", wKey, x, y);
8314 if (!infoPtr->bLButtonDown) return 0;
8316 lvHitTestInfo.pt.x = x;
8317 lvHitTestInfo.pt.y = y;
8319 /* send NM_CLICK notification */
8320 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8321 if (!notify_click(infoPtr, NM_CLICK, &lvHitTestInfo)) return 0;
8323 /* set left button flag */
8324 infoPtr->bLButtonDown = FALSE;
8326 /* if we clicked on a selected item, edit the label */
8327 if(lvHitTestInfo.iItem == infoPtr->nEditLabelItem && (lvHitTestInfo.flags & LVHT_ONITEMLABEL))
8328 LISTVIEW_EditLabelT(infoPtr, lvHitTestInfo.iItem, TRUE);
8335 * Destroys the listview control (called after WM_DESTROY).
8338 * [I] infoPtr : valid pointer to the listview structure
8343 static LRESULT LISTVIEW_NCDestroy(LISTVIEW_INFO *infoPtr)
8347 /* delete all items */
8348 LISTVIEW_DeleteAllItems(infoPtr);
8350 /* destroy data structure */
8351 DPA_Destroy(infoPtr->hdpaItems);
8352 DPA_Destroy(infoPtr->hdpaPosX);
8353 DPA_Destroy(infoPtr->hdpaPosY);
8354 DPA_Destroy(infoPtr->hdpaColumns);
8355 ranges_destroy(infoPtr->selectionRanges);
8357 /* destroy image lists */
8358 if (!(infoPtr->dwStyle & LVS_SHAREIMAGELISTS))
8360 if (infoPtr->himlNormal)
8361 ImageList_Destroy(infoPtr->himlNormal);
8362 if (infoPtr->himlSmall)
8363 ImageList_Destroy(infoPtr->himlSmall);
8364 if (infoPtr->himlState)
8365 ImageList_Destroy(infoPtr->himlState);
8368 /* destroy font, bkgnd brush */
8370 if (infoPtr->hDefaultFont) DeleteObject(infoPtr->hDefaultFont);
8371 if (infoPtr->clrBk != CLR_NONE) DeleteObject(infoPtr->hBkBrush);
8373 SetWindowLongPtrW(infoPtr->hwndSelf, 0, 0);
8375 /* free listview info pointer*/
8383 * Handles notifications from header.
8386 * [I] infoPtr : valid pointer to the listview structure
8387 * [I] nCtrlId : control identifier
8388 * [I] lpnmh : notification information
8393 static LRESULT LISTVIEW_HeaderNotification(LISTVIEW_INFO *infoPtr, const NMHEADERW *lpnmh)
8395 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8396 HWND hwndSelf = infoPtr->hwndSelf;
8398 TRACE("(lpnmh=%p)\n", lpnmh);
8400 if (!lpnmh || lpnmh->iItem < 0 || lpnmh->iItem >= DPA_GetPtrCount(infoPtr->hdpaColumns)) return 0;
8402 switch (lpnmh->hdr.code)
8407 COLUMN_INFO *lpColumnInfo;
8411 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8414 /* remove the old line (if any) */
8415 LISTVIEW_DrawTrackLine(infoPtr);
8417 /* compute & draw the new line */
8418 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8419 x = lpColumnInfo->rcHeader.left + lpnmh->pitem->cxy;
8420 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8421 infoPtr->xTrackLine = x + ptOrigin.x;
8422 LISTVIEW_DrawTrackLine(infoPtr);
8428 /* remove the track line (if any) */
8429 LISTVIEW_DrawTrackLine(infoPtr);
8430 infoPtr->xTrackLine = -1;
8433 case HDN_ITEMCHANGINGW:
8434 case HDN_ITEMCHANGINGA:
8435 return notify_forward_header(infoPtr, lpnmh);
8437 case HDN_ITEMCHANGEDW:
8438 case HDN_ITEMCHANGEDA:
8440 COLUMN_INFO *lpColumnInfo;
8443 notify_forward_header(infoPtr, lpnmh);
8444 if (!IsWindow(hwndSelf))
8447 if (!lpnmh->pitem || !(lpnmh->pitem->mask & HDI_WIDTH))
8451 hdi.mask = HDI_WIDTH;
8452 if (!Header_GetItemW(infoPtr->hwndHeader, lpnmh->iItem, &hdi)) return 0;
8456 cxy = lpnmh->pitem->cxy;
8458 /* determine how much we change since the last know position */
8459 lpColumnInfo = LISTVIEW_GetColumnInfo(infoPtr, lpnmh->iItem);
8460 dx = cxy - (lpColumnInfo->rcHeader.right - lpColumnInfo->rcHeader.left);
8463 lpColumnInfo->rcHeader.right += dx;
8464 LISTVIEW_ScrollColumns(infoPtr, lpnmh->iItem + 1, dx);
8465 LISTVIEW_UpdateItemSize(infoPtr);
8466 if (uView == LVS_REPORT && is_redrawing(infoPtr))
8469 RECT rcCol = lpColumnInfo->rcHeader;
8471 LISTVIEW_GetOrigin(infoPtr, &ptOrigin);
8472 OffsetRect(&rcCol, ptOrigin.x, 0);
8474 rcCol.top = infoPtr->rcList.top;
8475 rcCol.bottom = infoPtr->rcList.bottom;
8477 /* resizing left-aligned columns leaves most of the left side untouched */
8478 if ((lpColumnInfo->fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
8480 INT nMaxDirty = infoPtr->nEllipsisWidth + infoPtr->ntmMaxCharWidth + dx;
8481 rcCol.left = max (rcCol.left, rcCol.right - nMaxDirty);
8484 LISTVIEW_InvalidateRect(infoPtr, &rcCol);
8490 case HDN_ITEMCLICKW:
8491 case HDN_ITEMCLICKA:
8493 /* Handle sorting by Header Column */
8496 ZeroMemory(&nmlv, sizeof(NMLISTVIEW));
8498 nmlv.iSubItem = lpnmh->iItem;
8499 notify_listview(infoPtr, LVN_COLUMNCLICK, &nmlv);
8503 case HDN_DIVIDERDBLCLICKW:
8504 case HDN_DIVIDERDBLCLICKA:
8505 LISTVIEW_SetColumnWidth(infoPtr, lpnmh->iItem, LVSCW_AUTOSIZE);
8514 * Paint non-client area of control.
8517 * [I] infoPtr : valid pointer to the listview structureof the sender
8518 * [I] region : update region
8521 * TRUE - frame was painted
8522 * FALSE - call default window proc
8524 static BOOL LISTVIEW_NCPaint(LISTVIEW_INFO *infoPtr, HRGN region)
8526 HTHEME theme = GetWindowTheme (infoPtr->hwndSelf);
8530 int cxEdge = GetSystemMetrics (SM_CXEDGE),
8531 cyEdge = GetSystemMetrics (SM_CYEDGE);
8533 if (!theme) return FALSE;
8535 GetWindowRect(infoPtr->hwndSelf, &r);
8537 cliprgn = CreateRectRgn (r.left + cxEdge, r.top + cyEdge,
8538 r.right - cxEdge, r.bottom - cyEdge);
8539 if (region != (HRGN)1)
8540 CombineRgn (cliprgn, cliprgn, region, RGN_AND);
8541 OffsetRect(&r, -r.left, -r.top);
8543 dc = GetDCEx(infoPtr->hwndSelf, region, DCX_WINDOW|DCX_INTERSECTRGN);
8544 OffsetRect(&r, -r.left, -r.top);
8546 if (IsThemeBackgroundPartiallyTransparent (theme, 0, 0))
8547 DrawThemeParentBackground(infoPtr->hwndSelf, dc, &r);
8548 DrawThemeBackground (theme, dc, 0, 0, &r, 0);
8549 ReleaseDC(infoPtr->hwndSelf, dc);
8551 /* Call default proc to get the scrollbars etc. painted */
8552 DefWindowProcW (infoPtr->hwndSelf, WM_NCPAINT, (WPARAM)cliprgn, 0);
8559 * Determines the type of structure to use.
8562 * [I] infoPtr : valid pointer to the listview structureof the sender
8563 * [I] hwndFrom : listview window handle
8564 * [I] nCommand : command specifying the nature of the WM_NOTIFYFORMAT
8569 static LRESULT LISTVIEW_NotifyFormat(LISTVIEW_INFO *infoPtr, HWND hwndFrom, INT nCommand)
8571 TRACE("(hwndFrom=%p, nCommand=%d)\n", hwndFrom, nCommand);
8573 if (nCommand != NF_REQUERY) return 0;
8575 infoPtr->notifyFormat = SendMessageW(hwndFrom, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
8582 * Paints/Repaints the listview control.
8585 * [I] infoPtr : valid pointer to the listview structure
8586 * [I] hdc : device context handle
8591 static LRESULT LISTVIEW_Paint(LISTVIEW_INFO *infoPtr, HDC hdc)
8593 TRACE("(hdc=%p)\n", hdc);
8595 if (infoPtr->bNoItemMetrics && infoPtr->nItemCount)
8597 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8599 infoPtr->bNoItemMetrics = FALSE;
8600 LISTVIEW_UpdateItemSize(infoPtr);
8601 if (uView == LVS_ICON || uView == LVS_SMALLICON)
8602 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8603 LISTVIEW_UpdateScroll(infoPtr);
8606 LISTVIEW_Refresh(infoPtr, hdc);
8611 hdc = BeginPaint(infoPtr->hwndSelf, &ps);
8613 if (ps.fErase) LISTVIEW_FillBkgnd(infoPtr, hdc, &ps.rcPaint);
8614 LISTVIEW_Refresh(infoPtr, hdc);
8615 EndPaint(infoPtr->hwndSelf, &ps);
8624 * Paints/Repaints the listview control.
8627 * [I] infoPtr : valid pointer to the listview structure
8628 * [I] hdc : device context handle
8629 * [I] options : drawing options
8634 static LRESULT LISTVIEW_PrintClient(LISTVIEW_INFO *infoPtr, HDC hdc, DWORD options)
8636 FIXME("Partial Stub: (hdc=%p options=0x%08lx)\n", hdc, options);
8638 if ((options & PRF_CHECKVISIBLE) && !IsWindowVisible(infoPtr->hwndSelf))
8641 if (options & PRF_ERASEBKGND)
8642 LISTVIEW_EraseBkgnd(infoPtr, hdc);
8644 if (options & PRF_CLIENT)
8645 LISTVIEW_Paint(infoPtr, hdc);
8653 * Processes double click messages (right mouse button).
8656 * [I] infoPtr : valid pointer to the listview structure
8657 * [I] wKey : key flag
8658 * [I] x,y : mouse coordinate
8663 static LRESULT LISTVIEW_RButtonDblClk(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8665 LVHITTESTINFO lvHitTestInfo;
8667 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8669 /* send NM_RELEASEDCAPTURE notification */
8670 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8672 /* send NM_RDBLCLK notification */
8673 lvHitTestInfo.pt.x = x;
8674 lvHitTestInfo.pt.y = y;
8675 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8676 notify_click(infoPtr, NM_RDBLCLK, &lvHitTestInfo);
8683 * Processes mouse down messages (right mouse button).
8686 * [I] infoPtr : valid pointer to the listview structure
8687 * [I] wKey : key flag
8688 * [I] x,y : mouse coordinate
8693 static LRESULT LISTVIEW_RButtonDown(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8695 LVHITTESTINFO lvHitTestInfo;
8698 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8700 /* send NM_RELEASEDCAPTURE notification */
8701 if (!notify(infoPtr, NM_RELEASEDCAPTURE)) return 0;
8703 /* make sure the listview control window has the focus */
8704 if (!infoPtr->bFocus) SetFocus(infoPtr->hwndSelf);
8706 /* set right button down flag */
8707 infoPtr->bRButtonDown = TRUE;
8709 /* determine the index of the selected item */
8710 lvHitTestInfo.pt.x = x;
8711 lvHitTestInfo.pt.y = y;
8712 nItem = LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, TRUE);
8714 if ((nItem >= 0) && (nItem < infoPtr->nItemCount))
8716 LISTVIEW_SetItemFocus(infoPtr, nItem);
8717 if (!((wKey & MK_SHIFT) || (wKey & MK_CONTROL)) &&
8718 !LISTVIEW_GetItemState(infoPtr, nItem, LVIS_SELECTED))
8719 LISTVIEW_SetSelection(infoPtr, nItem);
8723 LISTVIEW_DeselectAll(infoPtr);
8731 * Processes mouse up messages (right mouse button).
8734 * [I] infoPtr : valid pointer to the listview structure
8735 * [I] wKey : key flag
8736 * [I] x,y : mouse coordinate
8741 static LRESULT LISTVIEW_RButtonUp(LISTVIEW_INFO *infoPtr, WORD wKey, INT x, INT y)
8743 LVHITTESTINFO lvHitTestInfo;
8746 TRACE("(key=%hu,X=%hu,Y=%hu)\n", wKey, x, y);
8748 if (!infoPtr->bRButtonDown) return 0;
8750 /* set button flag */
8751 infoPtr->bRButtonDown = FALSE;
8753 /* Send NM_RClICK notification */
8754 lvHitTestInfo.pt.x = x;
8755 lvHitTestInfo.pt.y = y;
8756 LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, TRUE, FALSE);
8757 if (!notify_click(infoPtr, NM_RCLICK, &lvHitTestInfo)) return 0;
8759 /* Change to screen coordinate for WM_CONTEXTMENU */
8760 pt = lvHitTestInfo.pt;
8761 ClientToScreen(infoPtr->hwndSelf, &pt);
8763 /* Send a WM_CONTEXTMENU message in response to the RBUTTONUP */
8764 SendMessageW(infoPtr->hwndSelf, WM_CONTEXTMENU,
8765 (WPARAM)infoPtr->hwndSelf, MAKELPARAM(pt.x, pt.y));
8776 * [I] infoPtr : valid pointer to the listview structure
8777 * [I] hwnd : window handle of window containing the cursor
8778 * [I] nHittest : hit-test code
8779 * [I] wMouseMsg : ideintifier of the mouse message
8782 * TRUE if cursor is set
8785 static BOOL LISTVIEW_SetCursor(LISTVIEW_INFO *infoPtr, HWND hwnd, UINT nHittest, UINT wMouseMsg)
8787 LVHITTESTINFO lvHitTestInfo;
8789 if(!(infoPtr->dwLvExStyle & LVS_EX_TRACKSELECT)) return FALSE;
8791 if(!infoPtr->hHotCursor) return FALSE;
8793 GetCursorPos(&lvHitTestInfo.pt);
8794 if (LISTVIEW_HitTest(infoPtr, &lvHitTestInfo, FALSE, FALSE) < 0) return FALSE;
8796 SetCursor(infoPtr->hHotCursor);
8806 * [I] infoPtr : valid pointer to the listview structure
8807 * [I] hwndLoseFocus : handle of previously focused window
8812 static LRESULT LISTVIEW_SetFocus(LISTVIEW_INFO *infoPtr, HWND hwndLoseFocus)
8814 TRACE("(hwndLoseFocus=%p)\n", hwndLoseFocus);
8816 /* if we have the focus already, there's nothing to do */
8817 if (infoPtr->bFocus) return 0;
8819 /* send NM_SETFOCUS notification */
8820 if (!notify(infoPtr, NM_SETFOCUS)) return 0;
8822 /* set window focus flag */
8823 infoPtr->bFocus = TRUE;
8825 /* put the focus rect back on */
8826 LISTVIEW_ShowFocusRect(infoPtr, TRUE);
8828 /* redraw all visible selected items */
8829 LISTVIEW_InvalidateSelectedItems(infoPtr);
8839 * [I] infoPtr : valid pointer to the listview structure
8840 * [I] fRedraw : font handle
8841 * [I] fRedraw : redraw flag
8846 static LRESULT LISTVIEW_SetFont(LISTVIEW_INFO *infoPtr, HFONT hFont, WORD fRedraw)
8848 HFONT oldFont = infoPtr->hFont;
8850 TRACE("(hfont=%p,redraw=%hu)\n", hFont, fRedraw);
8852 infoPtr->hFont = hFont ? hFont : infoPtr->hDefaultFont;
8853 if (infoPtr->hFont == oldFont) return 0;
8855 LISTVIEW_SaveTextMetrics(infoPtr);
8857 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_REPORT)
8858 SendMessageW(infoPtr->hwndHeader, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(fRedraw, 0));
8860 if (fRedraw) LISTVIEW_InvalidateList(infoPtr);
8867 * Message handling for WM_SETREDRAW.
8868 * For the Listview, it invalidates the entire window (the doc specifies otherwise)
8871 * [I] infoPtr : valid pointer to the listview structure
8872 * [I] bRedraw: state of redraw flag
8875 * DefWinProc return value
8877 static LRESULT LISTVIEW_SetRedraw(LISTVIEW_INFO *infoPtr, BOOL bRedraw)
8879 TRACE("infoPtr->bRedraw=%d, bRedraw=%d\n", infoPtr->bRedraw, bRedraw);
8881 /* we cannot use straight equality here because _any_ non-zero value is TRUE */
8882 if ((infoPtr->bRedraw && bRedraw) || (!infoPtr->bRedraw && !bRedraw)) return 0;
8884 infoPtr->bRedraw = bRedraw;
8886 if(!bRedraw) return 0;
8888 if (is_autoarrange(infoPtr))
8889 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8890 LISTVIEW_UpdateScroll(infoPtr);
8892 /* despite what the WM_SETREDRAW docs says, apps expect us
8893 * to invalidate the listview here... stupid! */
8894 LISTVIEW_InvalidateList(infoPtr);
8901 * Resizes the listview control. This function processes WM_SIZE
8902 * messages. At this time, the width and height are not used.
8905 * [I] infoPtr : valid pointer to the listview structure
8906 * [I] Width : new width
8907 * [I] Height : new height
8912 static LRESULT LISTVIEW_Size(LISTVIEW_INFO *infoPtr, int Width, int Height)
8914 RECT rcOld = infoPtr->rcList;
8916 TRACE("(width=%d, height=%d)\n", Width, Height);
8918 LISTVIEW_UpdateSize(infoPtr);
8919 if (EqualRect(&rcOld, &infoPtr->rcList)) return 0;
8921 /* do not bother with display related stuff if we're not redrawing */
8922 if (!is_redrawing(infoPtr)) return 0;
8924 if (is_autoarrange(infoPtr))
8925 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
8927 LISTVIEW_UpdateScroll(infoPtr);
8929 /* refresh all only for lists whose height changed significantly */
8930 if ((infoPtr->dwStyle & LVS_TYPEMASK) == LVS_LIST &&
8931 (rcOld.bottom - rcOld.top) / infoPtr->nItemHeight !=
8932 (infoPtr->rcList.bottom - infoPtr->rcList.top) / infoPtr->nItemHeight)
8933 LISTVIEW_InvalidateList(infoPtr);
8940 * Sets the size information.
8943 * [I] infoPtr : valid pointer to the listview structure
8948 static void LISTVIEW_UpdateSize(LISTVIEW_INFO *infoPtr)
8950 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
8952 TRACE("uView=%d, rcList(old)=%s\n", uView, wine_dbgstr_rect(&infoPtr->rcList));
8954 GetClientRect(infoPtr->hwndSelf, &infoPtr->rcList);
8956 if (uView == LVS_LIST)
8958 /* Apparently the "LIST" style is supposed to have the same
8959 * number of items in a column even if there is no scroll bar.
8960 * Since if a scroll bar already exists then the bottom is already
8961 * reduced, only reduce if the scroll bar does not currently exist.
8962 * The "2" is there to mimic the native control. I think it may be
8963 * related to either padding or edges. (GLA 7/2002)
8965 if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & WS_HSCROLL))
8966 infoPtr->rcList.bottom -= GetSystemMetrics(SM_CYHSCROLL);
8967 infoPtr->rcList.bottom = max (infoPtr->rcList.bottom - 2, 0);
8969 else if (uView == LVS_REPORT && !(infoPtr->dwStyle & LVS_NOCOLUMNHEADER))
8974 hl.prc = &infoPtr->rcList;
8976 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
8978 SetWindowPos(wp.hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
8980 infoPtr->rcList.top = max(wp.cy, 0);
8983 TRACE(" rcList=%s\n", wine_dbgstr_rect(&infoPtr->rcList));
8988 * Processes WM_STYLECHANGED messages.
8991 * [I] infoPtr : valid pointer to the listview structure
8992 * [I] wStyleType : window style type (normal or extended)
8993 * [I] lpss : window style information
8998 static INT LISTVIEW_StyleChanged(LISTVIEW_INFO *infoPtr, WPARAM wStyleType,
8999 const STYLESTRUCT *lpss)
9001 UINT uNewView = lpss->styleNew & LVS_TYPEMASK;
9002 UINT uOldView = lpss->styleOld & LVS_TYPEMASK;
9004 TRACE("(styletype=%x, styleOld=0x%08lx, styleNew=0x%08lx)\n",
9005 wStyleType, lpss->styleOld, lpss->styleNew);
9007 if (wStyleType != GWL_STYLE) return 0;
9009 /* FIXME: if LVS_NOSORTHEADER changed, update header */
9010 /* what if LVS_OWNERDATA changed? */
9011 /* or LVS_SINGLESEL */
9012 /* or LVS_SORT{AS,DES}CENDING */
9014 infoPtr->dwStyle = lpss->styleNew;
9016 if (((lpss->styleOld & WS_HSCROLL) != 0)&&
9017 ((lpss->styleNew & WS_HSCROLL) == 0))
9018 ShowScrollBar(infoPtr->hwndSelf, SB_HORZ, FALSE);
9020 if (((lpss->styleOld & WS_VSCROLL) != 0)&&
9021 ((lpss->styleNew & WS_VSCROLL) == 0))
9022 ShowScrollBar(infoPtr->hwndSelf, SB_VERT, FALSE);
9024 if (uNewView != uOldView)
9026 SIZE oldIconSize = infoPtr->iconSize;
9029 SendMessageW(infoPtr->hwndEdit, WM_KILLFOCUS, 0, 0);
9030 ShowWindow(infoPtr->hwndHeader, SW_HIDE);
9032 ShowScrollBar(infoPtr->hwndSelf, SB_BOTH, FALSE);
9033 SetRectEmpty(&infoPtr->rcFocus);
9035 himl = (uNewView == LVS_ICON ? infoPtr->himlNormal : infoPtr->himlSmall);
9036 set_icon_size(&infoPtr->iconSize, himl, uNewView != LVS_ICON);
9038 if (uNewView == LVS_ICON)
9040 if ((infoPtr->iconSize.cx != oldIconSize.cx) || (infoPtr->iconSize.cy != oldIconSize.cy))
9042 TRACE("icon old size=(%ld,%ld), new size=(%ld,%ld)\n",
9043 oldIconSize.cx, oldIconSize.cy, infoPtr->iconSize.cx, infoPtr->iconSize.cy);
9044 LISTVIEW_SetIconSpacing(infoPtr, 0, 0);
9047 else if (uNewView == LVS_REPORT)
9052 hl.prc = &infoPtr->rcList;
9054 SendMessageW( infoPtr->hwndHeader, HDM_LAYOUT, 0, (LPARAM)&hl );
9055 SetWindowPos(infoPtr->hwndHeader, infoPtr->hwndSelf, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
9058 LISTVIEW_UpdateItemSize(infoPtr);
9061 if (uNewView == LVS_REPORT)
9062 ShowWindow(infoPtr->hwndHeader, (lpss->styleNew & LVS_NOCOLUMNHEADER) ? SW_HIDE : SW_SHOWNORMAL);
9064 if ( (uNewView == LVS_ICON || uNewView == LVS_SMALLICON) &&
9065 (uNewView != uOldView || ((lpss->styleNew ^ lpss->styleOld) & LVS_ALIGNMASK)) )
9066 LISTVIEW_Arrange(infoPtr, LVA_DEFAULT);
9068 /* update the size of the client area */
9069 LISTVIEW_UpdateSize(infoPtr);
9071 /* add scrollbars if needed */
9072 LISTVIEW_UpdateScroll(infoPtr);
9074 /* invalidate client area + erase background */
9075 LISTVIEW_InvalidateList(infoPtr);
9082 * Window procedure of the listview control.
9085 static LRESULT WINAPI
9086 LISTVIEW_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9088 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(hwnd, 0);
9090 TRACE("(uMsg=%x wParam=%x lParam=%lx)\n", uMsg, wParam, lParam);
9092 if (!infoPtr && (uMsg != WM_CREATE))
9093 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9097 case LVM_APPROXIMATEVIEWRECT:
9098 return LISTVIEW_ApproximateViewRect(infoPtr, (INT)wParam,
9099 LOWORD(lParam), HIWORD(lParam));
9101 return LISTVIEW_Arrange(infoPtr, (INT)wParam);
9103 /* case LVM_CANCELEDITLABEL: */
9105 case LVM_CREATEDRAGIMAGE:
9106 return (LRESULT)LISTVIEW_CreateDragImage(infoPtr, (INT)wParam, (LPPOINT)lParam);
9108 case LVM_DELETEALLITEMS:
9109 return LISTVIEW_DeleteAllItems(infoPtr);
9111 case LVM_DELETECOLUMN:
9112 return LISTVIEW_DeleteColumn(infoPtr, (INT)wParam);
9114 case LVM_DELETEITEM:
9115 return LISTVIEW_DeleteItem(infoPtr, (INT)wParam);
9117 case LVM_EDITLABELW:
9118 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, TRUE);
9120 case LVM_EDITLABELA:
9121 return (LRESULT)LISTVIEW_EditLabelT(infoPtr, (INT)wParam, FALSE);
9123 /* case LVM_ENABLEGROUPVIEW: */
9125 case LVM_ENSUREVISIBLE:
9126 return LISTVIEW_EnsureVisible(infoPtr, (INT)wParam, (BOOL)lParam);
9129 return LISTVIEW_FindItemW(infoPtr, (INT)wParam, (LPLVFINDINFOW)lParam);
9132 return LISTVIEW_FindItemA(infoPtr, (INT)wParam, (LPLVFINDINFOA)lParam);
9134 case LVM_GETBKCOLOR:
9135 return infoPtr->clrBk;
9137 /* case LVM_GETBKIMAGE: */
9139 case LVM_GETCALLBACKMASK:
9140 return infoPtr->uCallbackMask;
9142 case LVM_GETCOLUMNA:
9143 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9145 case LVM_GETCOLUMNW:
9146 return LISTVIEW_GetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9148 case LVM_GETCOLUMNORDERARRAY:
9149 return LISTVIEW_GetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9151 case LVM_GETCOLUMNWIDTH:
9152 return LISTVIEW_GetColumnWidth(infoPtr, (INT)wParam);
9154 case LVM_GETCOUNTPERPAGE:
9155 return LISTVIEW_GetCountPerPage(infoPtr);
9157 case LVM_GETEDITCONTROL:
9158 return (LRESULT)infoPtr->hwndEdit;
9160 case LVM_GETEXTENDEDLISTVIEWSTYLE:
9161 return infoPtr->dwLvExStyle;
9163 /* case LVM_GETGROUPINFO: */
9165 /* case LVM_GETGROUPMETRICS: */
9168 return (LRESULT)infoPtr->hwndHeader;
9170 case LVM_GETHOTCURSOR:
9171 return (LRESULT)infoPtr->hHotCursor;
9173 case LVM_GETHOTITEM:
9174 return infoPtr->nHotItem;
9176 case LVM_GETHOVERTIME:
9177 return infoPtr->dwHoverTime;
9179 case LVM_GETIMAGELIST:
9180 return (LRESULT)LISTVIEW_GetImageList(infoPtr, (INT)wParam);
9182 /* case LVM_GETINSERTMARK: */
9184 /* case LVM_GETINSERTMARKCOLOR: */
9186 /* case LVM_GETINSERTMARKRECT: */
9188 case LVM_GETISEARCHSTRINGA:
9189 case LVM_GETISEARCHSTRINGW:
9190 FIXME("LVM_GETISEARCHSTRING: unimplemented\n");
9194 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, FALSE);
9197 return LISTVIEW_GetItemExtT(infoPtr, (LPLVITEMW)lParam, TRUE);
9199 case LVM_GETITEMCOUNT:
9200 return infoPtr->nItemCount;
9202 case LVM_GETITEMPOSITION:
9203 return LISTVIEW_GetItemPosition(infoPtr, (INT)wParam, (LPPOINT)lParam);
9205 case LVM_GETITEMRECT:
9206 return LISTVIEW_GetItemRect(infoPtr, (INT)wParam, (LPRECT)lParam);
9208 case LVM_GETITEMSPACING:
9209 return LISTVIEW_GetItemSpacing(infoPtr, (BOOL)wParam);
9211 case LVM_GETITEMSTATE:
9212 return LISTVIEW_GetItemState(infoPtr, (INT)wParam, (UINT)lParam);
9214 case LVM_GETITEMTEXTA:
9215 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9217 case LVM_GETITEMTEXTW:
9218 return LISTVIEW_GetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9220 case LVM_GETNEXTITEM:
9221 return LISTVIEW_GetNextItem(infoPtr, (INT)wParam, LOWORD(lParam));
9223 case LVM_GETNUMBEROFWORKAREAS:
9224 FIXME("LVM_GETNUMBEROFWORKAREAS: unimplemented\n");
9228 if (!lParam) return FALSE;
9229 LISTVIEW_GetOrigin(infoPtr, (LPPOINT)lParam);
9232 /* case LVM_GETOUTLINECOLOR: */
9234 /* case LVM_GETSELECTEDCOLUMN: */
9236 case LVM_GETSELECTEDCOUNT:
9237 return LISTVIEW_GetSelectedCount(infoPtr);
9239 case LVM_GETSELECTIONMARK:
9240 return infoPtr->nSelectionMark;
9242 case LVM_GETSTRINGWIDTHA:
9243 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, FALSE);
9245 case LVM_GETSTRINGWIDTHW:
9246 return LISTVIEW_GetStringWidthT(infoPtr, (LPCWSTR)lParam, TRUE);
9248 case LVM_GETSUBITEMRECT:
9249 return LISTVIEW_GetSubItemRect(infoPtr, (UINT)wParam, (LPRECT)lParam);
9251 case LVM_GETTEXTBKCOLOR:
9252 return infoPtr->clrTextBk;
9254 case LVM_GETTEXTCOLOR:
9255 return infoPtr->clrText;
9257 /* case LVM_GETTILEINFO: */
9259 /* case LVM_GETTILEVIEWINFO: */
9261 case LVM_GETTOOLTIPS:
9262 if( !infoPtr->hwndToolTip )
9263 infoPtr->hwndToolTip = COMCTL32_CreateToolTip( hwnd );
9264 return (LRESULT)infoPtr->hwndToolTip;
9266 case LVM_GETTOPINDEX:
9267 return LISTVIEW_GetTopIndex(infoPtr);
9269 /*case LVM_GETUNICODEFORMAT:
9270 FIXME("LVM_GETUNICODEFORMAT: unimplemented\n");
9273 /* case LVM_GETVIEW: */
9275 case LVM_GETVIEWRECT:
9276 return LISTVIEW_GetViewRect(infoPtr, (LPRECT)lParam);
9278 case LVM_GETWORKAREAS:
9279 FIXME("LVM_GETWORKAREAS: unimplemented\n");
9282 /* case LVM_HASGROUP: */
9285 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, FALSE, FALSE);
9287 case LVM_INSERTCOLUMNA:
9288 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9290 case LVM_INSERTCOLUMNW:
9291 return LISTVIEW_InsertColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9293 /* case LVM_INSERTGROUP: */
9295 /* case LVM_INSERTGROUPSORTED: */
9297 case LVM_INSERTITEMA:
9298 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9300 case LVM_INSERTITEMW:
9301 return LISTVIEW_InsertItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9303 /* case LVM_INSERTMARKHITTEST: */
9305 /* case LVM_ISGROUPVIEWENABLED: */
9307 /* case LVM_MAPIDTOINDEX: */
9309 /* case LVM_MAPINDEXTOID: */
9311 /* case LVM_MOVEGROUP: */
9313 /* case LVM_MOVEITEMTOGROUP: */
9315 case LVM_REDRAWITEMS:
9316 return LISTVIEW_RedrawItems(infoPtr, (INT)wParam, (INT)lParam);
9318 /* case LVM_REMOVEALLGROUPS: */
9320 /* case LVM_REMOVEGROUP: */
9323 return LISTVIEW_Scroll(infoPtr, (INT)wParam, (INT)lParam);
9325 case LVM_SETBKCOLOR:
9326 return LISTVIEW_SetBkColor(infoPtr, (COLORREF)lParam);
9328 /* case LVM_SETBKIMAGE: */
9330 case LVM_SETCALLBACKMASK:
9331 infoPtr->uCallbackMask = (UINT)wParam;
9334 case LVM_SETCOLUMNA:
9335 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, FALSE);
9337 case LVM_SETCOLUMNW:
9338 return LISTVIEW_SetColumnT(infoPtr, (INT)wParam, (LPLVCOLUMNW)lParam, TRUE);
9340 case LVM_SETCOLUMNORDERARRAY:
9341 return LISTVIEW_SetColumnOrderArray(infoPtr, (INT)wParam, (LPINT)lParam);
9343 case LVM_SETCOLUMNWIDTH:
9344 return LISTVIEW_SetColumnWidth(infoPtr, (INT)wParam, (short)LOWORD(lParam));
9346 case LVM_SETEXTENDEDLISTVIEWSTYLE:
9347 return LISTVIEW_SetExtendedListViewStyle(infoPtr, (DWORD)wParam, (DWORD)lParam);
9349 /* case LVM_SETGROUPINFO: */
9351 /* case LVM_SETGROUPMETRICS: */
9353 case LVM_SETHOTCURSOR:
9354 return (LRESULT)LISTVIEW_SetHotCursor(infoPtr, (HCURSOR)lParam);
9356 case LVM_SETHOTITEM:
9357 return LISTVIEW_SetHotItem(infoPtr, (INT)wParam);
9359 case LVM_SETHOVERTIME:
9360 return LISTVIEW_SetHoverTime(infoPtr, (DWORD)wParam);
9362 case LVM_SETICONSPACING:
9363 return LISTVIEW_SetIconSpacing(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9365 case LVM_SETIMAGELIST:
9366 return (LRESULT)LISTVIEW_SetImageList(infoPtr, (INT)wParam, (HIMAGELIST)lParam);
9368 /* case LVM_SETINFOTIP: */
9370 /* case LVM_SETINSERTMARK: */
9372 /* case LVM_SETINSERTMARKCOLOR: */
9375 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, FALSE);
9378 return LISTVIEW_SetItemT(infoPtr, (LPLVITEMW)lParam, TRUE);
9380 case LVM_SETITEMCOUNT:
9381 return LISTVIEW_SetItemCount(infoPtr, (INT)wParam, (DWORD)lParam);
9383 case LVM_SETITEMPOSITION:
9386 pt.x = (short)LOWORD(lParam);
9387 pt.y = (short)HIWORD(lParam);
9388 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, pt);
9391 case LVM_SETITEMPOSITION32:
9392 if (lParam == 0) return FALSE;
9393 return LISTVIEW_SetItemPosition(infoPtr, (INT)wParam, *((POINT*)lParam));
9395 case LVM_SETITEMSTATE:
9396 return LISTVIEW_SetItemState(infoPtr, (INT)wParam, (LPLVITEMW)lParam);
9398 case LVM_SETITEMTEXTA:
9399 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, FALSE);
9401 case LVM_SETITEMTEXTW:
9402 return LISTVIEW_SetItemTextT(infoPtr, (INT)wParam, (LPLVITEMW)lParam, TRUE);
9404 /* case LVM_SETOUTLINECOLOR: */
9406 /* case LVM_SETSELECTEDCOLUMN: */
9408 case LVM_SETSELECTIONMARK:
9409 return LISTVIEW_SetSelectionMark(infoPtr, (INT)lParam);
9411 case LVM_SETTEXTBKCOLOR:
9412 return LISTVIEW_SetTextBkColor(infoPtr, (COLORREF)lParam);
9414 case LVM_SETTEXTCOLOR:
9415 return LISTVIEW_SetTextColor(infoPtr, (COLORREF)lParam);
9417 /* case LVM_SETTILEINFO: */
9419 /* case LVM_SETTILEVIEWINFO: */
9421 /* case LVM_SETTILEWIDTH: */
9423 case LVM_SETTOOLTIPS:
9424 return (LRESULT)LISTVIEW_SetToolTips(infoPtr, (HWND)lParam);
9426 /* case LVM_SETUNICODEFORMAT: */
9428 /* case LVM_SETVIEW: */
9430 /* case LVM_SETWORKAREAS: */
9432 /* case LVM_SORTGROUPS: */
9435 return LISTVIEW_SortItems(infoPtr, (PFNLVCOMPARE)lParam, (LPARAM)wParam);
9437 /* LVM_SORTITEMSEX: */
9439 case LVM_SUBITEMHITTEST:
9440 return LISTVIEW_HitTest(infoPtr, (LPLVHITTESTINFO)lParam, TRUE, FALSE);
9443 return LISTVIEW_Update(infoPtr, (INT)wParam);
9446 return LISTVIEW_ProcessLetterKeys( infoPtr, wParam, lParam );
9449 return LISTVIEW_Command(infoPtr, wParam, lParam);
9452 return LISTVIEW_Create(hwnd, (LPCREATESTRUCTW)lParam);
9455 return LISTVIEW_Destroy(infoPtr);
9458 return LISTVIEW_Enable(infoPtr, (BOOL)wParam);
9461 return LISTVIEW_EraseBkgnd(infoPtr, (HDC)wParam);
9464 return DLGC_WANTCHARS | DLGC_WANTARROWS;
9467 return (LRESULT)infoPtr->hFont;
9470 return LISTVIEW_HScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9473 return LISTVIEW_KeyDown(infoPtr, (INT)wParam, (LONG)lParam);
9476 return LISTVIEW_KillFocus(infoPtr);
9478 case WM_LBUTTONDBLCLK:
9479 return LISTVIEW_LButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9481 case WM_LBUTTONDOWN:
9482 return LISTVIEW_LButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9485 return LISTVIEW_LButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9488 return LISTVIEW_MouseMove (infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9491 return LISTVIEW_MouseHover(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9494 return LISTVIEW_NCDestroy(infoPtr);
9497 if (LISTVIEW_NCPaint(infoPtr, (HRGN)wParam))
9502 if (lParam && ((LPNMHDR)lParam)->hwndFrom == infoPtr->hwndHeader)
9503 return LISTVIEW_HeaderNotification(infoPtr, (LPNMHEADERW)lParam);
9506 case WM_NOTIFYFORMAT:
9507 return LISTVIEW_NotifyFormat(infoPtr, (HWND)wParam, (INT)lParam);
9509 case WM_PRINTCLIENT:
9510 return LISTVIEW_PrintClient(infoPtr, (HDC)wParam, (DWORD)lParam);
9513 return LISTVIEW_Paint(infoPtr, (HDC)wParam);
9515 case WM_RBUTTONDBLCLK:
9516 return LISTVIEW_RButtonDblClk(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9518 case WM_RBUTTONDOWN:
9519 return LISTVIEW_RButtonDown(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9522 return LISTVIEW_RButtonUp(infoPtr, (WORD)wParam, (SHORT)LOWORD(lParam), (SHORT)HIWORD(lParam));
9525 if(LISTVIEW_SetCursor(infoPtr, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)))
9530 return LISTVIEW_SetFocus(infoPtr, (HWND)wParam);
9533 return LISTVIEW_SetFont(infoPtr, (HFONT)wParam, (WORD)lParam);
9536 return LISTVIEW_SetRedraw(infoPtr, (BOOL)wParam);
9539 return LISTVIEW_Size(infoPtr, (short)LOWORD(lParam), (short)HIWORD(lParam));
9541 case WM_STYLECHANGED:
9542 return LISTVIEW_StyleChanged(infoPtr, wParam, (LPSTYLESTRUCT)lParam);
9544 case WM_SYSCOLORCHANGE:
9545 COMCTL32_RefreshSysColors();
9548 /* case WM_TIMER: */
9549 case WM_THEMECHANGED:
9550 return LISTVIEW_ThemeChanged(infoPtr);
9553 return LISTVIEW_VScroll(infoPtr, (INT)LOWORD(wParam), 0, (HWND)lParam);
9556 if (wParam & (MK_SHIFT | MK_CONTROL))
9557 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9558 return LISTVIEW_MouseWheel(infoPtr, (short int)HIWORD(wParam));
9560 case WM_WINDOWPOSCHANGED:
9561 if (!(((WINDOWPOS *)lParam)->flags & SWP_NOSIZE))
9563 UINT uView = infoPtr->dwStyle & LVS_TYPEMASK;
9564 SetWindowPos(infoPtr->hwndSelf, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE |
9565 SWP_NOZORDER | SWP_NOMOVE | SWP_NOSIZE);
9567 if ((infoPtr->dwStyle & LVS_OWNERDRAWFIXED) && (uView == LVS_REPORT))
9569 MEASUREITEMSTRUCT mis;
9570 mis.CtlType = ODT_LISTVIEW;
9571 mis.CtlID = GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_ID);
9575 mis.itemHeight= infoPtr->nItemHeight;
9576 SendMessageW(infoPtr->hwndNotify, WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis);
9577 if (infoPtr->nItemHeight != max(mis.itemHeight, 1))
9578 infoPtr->nMeasureItemHeight = infoPtr->nItemHeight = max(mis.itemHeight, 1);
9581 LISTVIEW_UpdateSize(infoPtr);
9582 LISTVIEW_UpdateScroll(infoPtr);
9584 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9586 /* case WM_WININICHANGE: */
9589 if ((uMsg >= WM_USER) && (uMsg < WM_APP))
9590 ERR("unknown msg %04x wp=%08x lp=%08lx\n", uMsg, wParam, lParam);
9593 /* call default window procedure */
9594 return DefWindowProcW(hwnd, uMsg, wParam, lParam);
9601 * Registers the window class.
9609 void LISTVIEW_Register(void)
9613 ZeroMemory(&wndClass, sizeof(WNDCLASSW));
9614 wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
9615 wndClass.lpfnWndProc = LISTVIEW_WindowProc;
9616 wndClass.cbClsExtra = 0;
9617 wndClass.cbWndExtra = sizeof(LISTVIEW_INFO *);
9618 wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW);
9619 wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
9620 wndClass.lpszClassName = WC_LISTVIEWW;
9621 RegisterClassW(&wndClass);
9626 * Unregisters the window class.
9634 void LISTVIEW_Unregister(void)
9636 UnregisterClassW(WC_LISTVIEWW, NULL);
9641 * Handle any WM_COMMAND messages
9644 * [I] infoPtr : valid pointer to the listview structure
9645 * [I] wParam : the first message parameter
9646 * [I] lParam : the second message parameter
9651 static LRESULT LISTVIEW_Command(LISTVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
9653 switch (HIWORD(wParam))
9658 * Adjust the edit window size
9661 HDC hdc = GetDC(infoPtr->hwndEdit);
9662 HFONT hFont, hOldFont = 0;
9667 if (!infoPtr->hwndEdit || !hdc) return 0;
9668 len = GetWindowTextW(infoPtr->hwndEdit, buffer, sizeof(buffer)/sizeof(buffer[0]));
9669 GetWindowRect(infoPtr->hwndEdit, &rect);
9671 /* Select font to get the right dimension of the string */
9672 hFont = (HFONT)SendMessageW(infoPtr->hwndEdit, WM_GETFONT, 0, 0);
9675 hOldFont = SelectObject(hdc, hFont);
9678 if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
9680 TEXTMETRICW textMetric;
9682 /* Add Extra spacing for the next character */
9683 GetTextMetricsW(hdc, &textMetric);
9684 sz.cx += (textMetric.tmMaxCharWidth * 2);
9692 rect.bottom - rect.top,
9693 SWP_DRAWFRAME|SWP_NOMOVE);
9696 SelectObject(hdc, hOldFont);
9698 ReleaseDC(infoPtr->hwndEdit, hdc);
9704 return SendMessageW (infoPtr->hwndNotify, WM_COMMAND, wParam, lParam);
9713 * Subclassed edit control windproc function
9716 * [I] hwnd : the edit window handle
9717 * [I] uMsg : the message that is to be processed
9718 * [I] wParam : first message parameter
9719 * [I] lParam : second message parameter
9720 * [I] isW : TRUE if input is Unicode
9725 static LRESULT EditLblWndProcT(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL isW)
9727 LISTVIEW_INFO *infoPtr = (LISTVIEW_INFO *)GetWindowLongPtrW(GetParent(hwnd), 0);
9728 BOOL cancel = FALSE;
9730 TRACE("(hwnd=%p, uMsg=%x, wParam=%x, lParam=%lx, isW=%d)\n",
9731 hwnd, uMsg, wParam, lParam, isW);
9736 return DLGC_WANTARROWS | DLGC_WANTALLKEYS;
9743 WNDPROC editProc = infoPtr->EditWndProc;
9744 infoPtr->EditWndProc = 0;
9745 SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (DWORD_PTR)editProc);
9746 return CallWindowProcT(editProc, hwnd, uMsg, wParam, lParam, isW);
9750 if (VK_ESCAPE == (INT)wParam)
9755 else if (VK_RETURN == (INT)wParam)
9759 return CallWindowProcT(infoPtr->EditWndProc, hwnd, uMsg, wParam, lParam, isW);
9763 if (infoPtr->hwndEdit)
9765 LPWSTR buffer = NULL;
9767 infoPtr->hwndEdit = 0;
9770 DWORD len = isW ? GetWindowTextLengthW(hwnd) : GetWindowTextLengthA(hwnd);
9774 if ( (buffer = Alloc((len+1) * (isW ? sizeof(WCHAR) : sizeof(CHAR)))) )
9776 if (isW) GetWindowTextW(hwnd, buffer, len+1);
9777 else GetWindowTextA(hwnd, (CHAR*)buffer, len+1);
9781 LISTVIEW_EndEditLabelT(infoPtr, buffer, isW);
9783 if (buffer) Free(buffer);
9787 SendMessageW(hwnd, WM_CLOSE, 0, 0);
9793 * Subclassed edit control Unicode windproc function
9796 * [I] hwnd : the edit window handle
9797 * [I] uMsg : the message that is to be processed
9798 * [I] wParam : first message parameter
9799 * [I] lParam : second message parameter
9803 LRESULT CALLBACK EditLblWndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9805 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, TRUE);
9810 * Subclassed edit control ANSI windproc function
9813 * [I] hwnd : the edit window handle
9814 * [I] uMsg : the message that is to be processed
9815 * [I] wParam : first message parameter
9816 * [I] lParam : second message parameter
9820 LRESULT CALLBACK EditLblWndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
9822 return EditLblWndProcT(hwnd, uMsg, wParam, lParam, FALSE);
9827 * Creates a subclassed edit cotrol
9830 * [I] infoPtr : valid pointer to the listview structure
9831 * [I] text : initial text for the edit
9832 * [I] style : the window style
9833 * [I] isW : TRUE if input is Unicode
9837 static HWND CreateEditLabelT(LISTVIEW_INFO *infoPtr, LPCWSTR text, DWORD style,
9838 INT x, INT y, INT width, INT height, BOOL isW)
9840 WCHAR editName[5] = { 'E', 'd', 'i', 't', '\0' };
9845 TEXTMETRICW textMetric;
9846 HINSTANCE hinst = (HINSTANCE)GetWindowLongPtrW(infoPtr->hwndSelf, GWLP_HINSTANCE);
9848 TRACE("(text=%s, ..., isW=%d)\n", debugtext_t(text, isW), isW);
9850 style |= WS_CHILDWINDOW|WS_CLIPSIBLINGS|ES_LEFT|ES_AUTOHSCROLL|WS_BORDER;
9851 hdc = GetDC(infoPtr->hwndSelf);
9853 /* Select the font to get appropriate metric dimensions */
9854 if(infoPtr->hFont != 0)
9855 hOldFont = SelectObject(hdc, infoPtr->hFont);
9857 /*Get String Length in pixels */
9858 GetTextExtentPoint32W(hdc, text, lstrlenW(text), &sz);
9860 /*Add Extra spacing for the next character */
9861 GetTextMetricsW(hdc, &textMetric);
9862 sz.cx += (textMetric.tmMaxCharWidth * 2);
9864 if(infoPtr->hFont != 0)
9865 SelectObject(hdc, hOldFont);
9867 ReleaseDC(infoPtr->hwndSelf, hdc);
9869 hedit = CreateWindowW(editName, text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9871 hedit = CreateWindowA("Edit", (LPCSTR)text, style, x, y, sz.cx, height, infoPtr->hwndSelf, 0, hinst, 0);
9873 if (!hedit) return 0;
9875 infoPtr->EditWndProc = (WNDPROC)
9876 (isW ? SetWindowLongPtrW(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcW) :
9877 SetWindowLongPtrA(hedit, GWLP_WNDPROC, (DWORD_PTR)EditLblWndProcA) );
9879 SendMessageW(hedit, WM_SETFONT, (WPARAM)infoPtr->hFont, FALSE);